我有一个小例子,使用由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
答案 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";
}
};