假设有以下QT代码(QT 5.3.1):
void SenderClass::runSignal()
{
emit mySignal();
}
void ReceiverClass::ReceiverClass()
{
...
connect (senderClassRef, SIGNAL(mySignal()), this, SLOT(mySlot()) );
}
void ReceiverClass::mySlot()
{
//Long operation executions
Sleep(1000);
qDebug() << "1";
Sleep(1000);
qDebug() << "2";
Sleep(1000);
qDebug() << "3";
}
连续调用runSignal()会发生控制台显示如下内容:
1 2 1 3 2 3
这两个类存在于同一个线程中。 我是否必须在插槽中使用QMutexLocker?或者是否有另一种方法来获得有序输出,如:
1 2 3 1 2 3
如果还有一个正在执行,则阻止调用 mySlot()函数。
更新
此处遵循实际的代码段。 发件人:
//Sender.h
#include <QtWidgets/QWidget>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTextBrowser>
class Receiver;
class Sender : public QWidget
{
Q_OBJECT
public:
Sender(QWidget *parent = 0);
QPushButton *pushButton;
QTextBrowser *textBrowser;
Receiver* receiver;
signals:
void buttonClickedSignal();
public slots:
void ButtonClickedSlot();
};
//Sender.cpp
#include "Sender.h"
#include "Receiver.h"
#include <QObject>
Sender::Sender(QWidget *parent)
: QWidget(parent)
{
pushButton = new QPushButton(this);
pushButton->setObjectName(QStringLiteral("pushButton"));
pushButton->setGeometry(QRect(150, 30, 75, 23));
pushButton->setText("Button");
textBrowser = new QTextBrowser(this);
textBrowser->setObjectName(QStringLiteral("textBrowser"));
textBrowser->setGeometry(QRect(50, 90, 256, 192));
receiver = new Receiver(this);
QObject::connect(pushButton, SIGNAL(clicked()), this, SLOT(ButtonClickedSlot()));
}
void Sender::ButtonClickedSlot()
{
emit buttonClickedSignal();
}
接收者:
//Receiver.h
#include "Sender.h"
class Receiver : QObject
{
Q_OBJECT
public:
Receiver(Sender *sender);
Sender *m_sender;
public slots:
void ReceiverSlot();
};
//Receiver.cpp
#include "Receiver.h"
#include <QObject>
#include <QThread>
#include <QApplication>
Receiver::Receiver(Sender *sender)
{
m_sender = sender;
QObject::connect(m_sender, SIGNAL(buttonClickedSignal()), this, SLOT(ReceiverSlot()), Qt::QueuedConnection);
}
void Receiver::ReceiverSlot()
{
m_sender->textBrowser->append("1");
QThread::msleep(100);
qApp->processEvents();
m_sender->textBrowser->append("2");
QThread::msleep(100);
qApp->processEvents();
m_sender->textBrowser->append("3");
QThread::msleep(100);
qApp->processEvents();
m_sender->textBrowser->append("4");
QThread::msleep(100);
qApp->processEvents();
}
快速单击该按钮会导致QTextBrowser中没有连续的数字,即使设置了QueuedConnection也是如此。
更新2
我想要实现的是对ReceiverSlot的排队访问。用户可以任意自由地点击按钮(当他想要并且以任何速度时)。 ReceiverSlot不会错过任何事件。我需要某种事件排队,以便始终执行ReceiverSlot中的(长)操作,可能会延迟,但执行。
答案 0 :(得分:1)
摆脱QApplication::processEvents
并将冻结GUI的长操作移动到新线程。例如,您可以使用移动到新线程的工作对象。有good example of this in the docs。
您还应该阅读this以了解有关Qt中的线程和事件的更多信息。与您在forcing event dispatching部分中解释的情况完全相同:
在“通过其他路径”重新输入事件循环时要非常小心:它可能导致不必要的递归!让我们回到Button示例。如果我们在doWork()槽内调用QCoreApplication :: processEvents(),并且用户再次单击该按钮,则会再次调用doWork()槽:
通常,您希望避免QApplication::processEvents
并创建本地事件循环。如果你的代码中有这些,那么你应该重新设计它,这样你就不必使用它们了。
来自文档的工作者对象检查:
class Worker : public QObject
{
Q_OBJECT
QThread workerThread;
public slots:
void doWork(const QString ¶meter) {
// ...
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
答案 1 :(得分:0)
将我的评论转化为正确答案:
如果,当您在插槽中拨打qApp->processEvents()
时,鼠标按下事件等待传递到按钮,它将被传递,clicked()
信号将再次发出,因此您的广告位将再次被调用。
QMutex
无济于事,因为不涉及线程。添加一个很可能会导致程序死锁。
使用Qt::QueuedConnection
没有帮助,因为这会将一个事件发布到队列中,当处理该队列时,将导致您的插槽被调用。因此,它最多会延迟到下次您致电qApp->processEvents()
。
在您的示例的上下文中,我的建议是摆脱排队连接,而是在插槽工作时禁用该按钮。这样可以防止再次发出clicked()
信号,并向用户显示他们此时不应该尝试点击按钮。
如果由于某种原因您不想这样做,您可以在接收器类中添加一个标志以防止递归调用。像这样的东西可以解决问题,虽然它不是很漂亮:
void Receiver::ReceiverSlot()
{
if( m_inSlot )
return;
m_inSlot = true;
// do work
m_inSlot = false;
}