2015-11-06 21 views
2

我有一個小例子,使用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 
+2

這是不可能的,因爲Qt中的所有GUI都在一個線程中工作。你的互斥體在那裏什麼都不做。你可以檢查,現在執行什麼線程。例如(在每個槽中):'Q_ASSERT(QThread :: currentThread()== this-> thread());' –

+0

這就是爲什麼我很困惑。所有插槽都在同一個線程中運行,但在同時移動和滾動時仍然能夠引發彼此的衝突。我也用'QThread :: currentThreadId()'檢查了線程ID,它們在同一個線程中。在我的例子中,互斥體做了一些事情:它導致程序凍結。如果這些插槽不會中斷對方,那就不會發生。我添加qDebug()的輸出 – honiahaka10

+0

我認爲'ui.gVPPI-> scale'會以某種方式觸發鼠標移動。你可以在'scale'後添加一個你設置爲真正的bevor和false的bool成員,並且只有在測試爲false時才執行移動 – Bowdzone

回答

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

您可以使用QObject::blockSignals阻止信號的特定對象, 但我想你對代碼的工作原理有錯誤的想法。

有一個GUI線程,如果信號+插槽在同一個GUI線程中,就像在你的情況下一樣,然後它們按順序調用。如果PPI::onZoom將在另一個線程上下文中調用,那麼你在這裏有問題,因爲在非GUI線程中使用像ui.gVPPI->scale這樣的東西是不被允許的,並且可能導致斷言,或者只是崩潰或隨機工作,UB。

當你看到slot 1被稱爲情況,那麼slot 1slot 2完成調用之前,很可能是因爲你調用內部slot 1調用另一個函數等,並在內心深處呼喚signal 2一些功能,其發射的slot 2調用一些功能,只需在調試器中設置斷點並找出發生了什麼。

0

在沒有與其他線程協作的單個GUI線程中使用任何形式的鎖定沒有任何意義。 GUI線程中的所有內容都已經爲您序列化。你正在打一個想象中的問題。只要你的代碼正在執行,其他任何東西都不會在同一個線程後面執行。

onZoom槽被調用,之前它完成它被調用onMouseMoved插槽

這是錯誤的。在你展示的代碼中沒有任何事情發生。也許ui.gVPPI->scale()正在重新進入事件循環。您確實需要展示一個獨立的示例:您的問題出現在您未顯示的代碼中。

如何防止插槽打斷對方

它已經阻止你。你需要什麼都不做。

在我被另一個線程使用的原始程序

一般來說,生活在GUI線程對象不能從其它線程直接調用的方法。

大多數情況下,在問題中拋出第二個線程會導致兩個問題。如果可能有點慢,即使在單線程中運行,正確設計的非阻塞代碼也能正常工作。那就是你的出發點 - 然後你將對象移動到其他線程來簡單地傳播工作。理想情況下,異步處理應該通過QtConcurrent::run異步運行工作來完成。