Search code examples
qtsignals-slots

Slots interrupting each other with just one Thread?


I have a small example using Slots called by mousemovement and mousewheel.

Now i have the problem that when i zoom and move at the same time, first the onZoom-slot is called and before it is finished it is calling the onMouseMoved-slot. That causes the first slot to lock the mutex (in my original program used by another thread) and the second one to wait for it.

How can I prevent the slots to interrupt each other (and why are they doing it in first place since they are in same thread?).

I read something about using Qt::QueuedConnection but that causes an access violation exception.

main.cpp

#include "ppi.h"
#include <QtGui/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    PPI w;
    w.show();
    return a.exec();
}

ppi.h

#ifndef PPI_H
#define PPI_H

#include <QtGui/QMainWindow>
#include <QGraphicsView>
#include <QDebug>
#include <QWheelEvent>
#include <QgraphicsEllipseItem>
#include <QMouseEvent>
#include <QMutex>
#include <QThread>
#include <QGraphicsSceneMouseEvent>

//#include "ui_ppi.h"

class PPIView : public QGraphicsView
{
        Q_OBJECT

    public:
        PPIView(QWidget * parent = 0)
            : QGraphicsView(parent)
        {};
        ~PPIView(){};

    private slots:
        void wheelEvent(QWheelEvent *event)
        {emit zoom(event);};

    signals:
        void zoom(QWheelEvent *event);

};

class PPIScene : public QGraphicsScene
{
        Q_OBJECT

    public:
        PPIScene(QObject *parent)
            : QGraphicsScene(parent)
        {};

        ~PPIScene(){};

    private:
        void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
        {emit mouseMoved(event);};

    signals:
        void mouseMoved(QGraphicsSceneMouseEvent *event);
};

class PPI : public QMainWindow
{
        Q_OBJECT

    public:
        PPI(QWidget *parent = 0, Qt::WFlags flags = 0)
        : QMainWindow(parent, flags)
        {
            //ui.setupUi(this);
            //ppiScene is inherited from QGraphicsScene, overriding mouseMoveEvent so it emits mouseMoved();
            ppiScene = new PPIScene(this);
            gVPPI = new PPIView(this);
            gVPPI->setMinimumSize(1024,1024);
            gVPPI->show();

            test = new QGraphicsEllipseItem(-10, -10, 20, 20);
            ppiScene->addItem(test);

            gVPPI->adjustSize();

            connect(ppiScene, SIGNAL(mouseMoved(QGraphicsSceneMouseEvent*)), this, SLOT(onMouseMoved(QGraphicsSceneMouseEvent*)));
            connect(gVPPI, SIGNAL(zoom(QWheelEvent*)), this, SLOT(onZoom(QWheelEvent*)));

            //ui.gVPPI is inherited from QGraphicsView, overriding wheelEvent, so it emits zoom()
            gVPPI->setScene(ppiScene);
            gVPPI->setMouseTracking(true);
        };

        ~PPI(){};

        QMutex mutex;

    private:
        //Ui::ppiClass ui;
        PPIScene* ppiScene;
        PPIView *gVPPI;

        QGraphicsEllipseItem *test;

    protected slots:
        void onZoom(QWheelEvent *event)
        {
            qDebug() << "Zoom lock" << QThread::currentThreadId();
            mutex.lock();
            qDebug() << "Zoom locked";

            if(event->delta() > 0)
                gVPPI->scale(1.01, 1.01);
            else
                gVPPI->scale(1/1.01, 1/1.01);

            qDebug() << "Zoom unlock";
            mutex.unlock();
            qDebug() << "Zoom unlocked";
        };

        void onMouseMoved(QGraphicsSceneMouseEvent *event)
        {
            qDebug() << "Move lock" << QThread::currentThreadId();
            mutex.lock();
            qDebug() << "move locked";

            test->setPos(test->pos()+event->scenePos()-event->lastScenePos());

            qDebug() << "Move unlock";
            mutex.unlock();
            qDebug() << "Move unlocked";
    };
};

#endif // PPI_H

Output qDebug():

Move lock 0x1514 
move locked 
Move unlock 
Move unlocked 
Move lock 0x1514 
move locked 
Move unlock 
Move unlocked 
Zoom lock 0x1514 
Zoom locked 
Move lock 0x1514 

Solution

  • I managed to set up my debugger and the problem seems QGraphicsView::scale() is calling QgraphicsScene::mouseMoveEvent() deep inside directly. So I need to introduce a variable which tells the mouseMoveEvent, whether it was called from QGraphicsView::scale() or from physical mouse movement.

    protected slots:
        void onZoom(QWheelEvent *event)
        {
            qDebug() << "Zoom lock" << QThread::currentThreadId();
            mutex.lock();
            qDebug() << "Zoom locked";
    
            scale = true;
                if(event->delta() > 0)
                    gVPPI->scale(1.01, 1.01);
                else
                    gVPPI->scale(1/1.01, 1/1.01);
            scale = false;
    
            qDebug() << "Zoom unlock";
            mutex.unlock();
            qDebug() << "Zoom unlocked";
        };
    
        void onMouseMoved(QGraphicsSceneMouseEvent *event)
        {
            if(scale == false)
            {
                qDebug() << "Move lock" << QThread::currentThreadId();
                mutex.lock();
                qDebug() << "move locked";
            }
    
            test->setPos(test->pos()+event->scenePos()-event->lastScenePos());
    
            if(scale == false)
            {
                qDebug() << "Move unlock";
                mutex.unlock();
                qDebug() << "Move unlocked";
            }
        };