插槽只用一个线程打断了?

时间:2015-11-06 13:15:34

标签: qt signals-slots

我有一个小例子,使用由mousemovement和mousewheel调用的Slots。

现在我遇到的问题是,当我同时缩放和移动时,首先调用onZoom-slot,然后在它完成之前调用onMouseMoved-slot。这导致第一个插槽锁定互斥锁(在我的另一个线程使用的原始程序中),第二个插槽等待它。

如何防止插槽相互中断(为什么他们首先在同一个线程中执行它?)。

我读过有关使用Qt :: QueuedConnection的内容,但这会导致访问冲突异常。

的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

输出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 

3 个答案:

答案 0 :(得分:0)

您可以使用QObject::blockSignals来阻止特定对象的信号, 但我认为你对代码的运作方式有错误的认识。

有一个GUI线程,如果在同一个GUI线程中有信号+插槽, 在你的情况下,然后他们按顺序调用。如果PPI::onZoom将在另一个线程上下文中调用,那么你在这里遇到问题,因为在非GUI线程中使用像ui.gVPPI->scale这样的东西是不允许的,并且可能导致断言崩溃,或者只是崩溃,或者随机工作,UB原样。

当您看到slot 1被调用,然后在slot 1 slot 2结束之前被调用的情况很可能是因为您在slot 1内调用了另一个调用另一个函数的函数函数等和一些函数深入调用signal 2,调用slot 2调用,只需在调试器中设置断点并找出正在发生的事情。

答案 1 :(得分:0)

在不与其他线程协作的单个GUI线程中使用任何形式的锁定是没有意义的。 GUI线程中的所有内容都已为您序列化。你正在解决一个想象中的问题。只要您的代码正在执行,就不会在同一个线程中执行任何其他操作。

  

onZoom-slot被调用,在它完成之前它正在调用onMouseMoved-slot

这是错误的。在您显示的代码中没有发生任何类型的事情。也许ui.gVPPI->scale()正在重新进入事件循环。您真的需要展示一个自包含的示例:您的问题出现在您未显示的代码中。

  

如何防止插槽互相中断

已经阻止了你。你什么都不做。

  

在另一个线程使用的原始程序中

一般来说,生活在GUI线程中的对象不能直接从其他线程调用方法。

大多数时候,在问题上抛出第二个线程会让你最终遇到两个问题。正确设计的非阻塞代码可以正常工作,即使在单个线程中运行时也可能有点慢。这是你的出发点 - 然后你将对象移动到其他线程只是为了扩展工作。但理想情况下,异步处理应该通过QtConcurrent::run异步运行工作来完成。

答案 2 :(得分:0)

我设法设置我的调试器,问题似乎是QGraphicsView :: scale()直接在内部调用QgraphicsScene :: mouseMoveEvent()。所以我需要引入一个告诉mouseMoveEvent的变量,它是从QGraphicsView :: scale()还是从物理鼠标移动调用的。

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";
        }
    };