为什么事件队列未处理我的QT信号
Why my Qt signal is not processed by the event queue?
情况的快速描述
我试图让最小的GUI开始一个无尽的过程,该过程通过CAN BUS通过自定义协议进行通信。
根据我在这里阅读的内容,我构造了我的代码:
我一方面有一个类,用2个简单的按钮"开始"answers"停止",即MainWindow。
,另一方面,按照上述链接(即工人(中所述管理我的自定义协议的课程。
在这些中间,我有一个将整体链接在一起的控制器。该控制器之所以在这里,是因为处理了其他一些任务,但这不是本文的目的。
关于信号和插槽
我已将按钮信号(释放(((连接到控制器的信号。因此,GUI不知道到底是什么开始。
这些控制器的信号连接到工人的插槽。这些插槽在那里开始并停止该过程。
关于线程
工人实例生活在自己的qthread中。可能涉及其他任务,所以我认为最好在自己的线程中处理每个任务。
开始,工人的过程是通过信号/插槽来处理的,这些信号/插槽使状态机在各州都在过渡方面演变。由于信号/插槽机制,如果我正确,则线程循环可以从其队列中处理事件。
问题
我的启动信号正确发送给工人,启动该过程,从而使状态机。该机器是循环的,直到用户请求停止信号为止。但是,当用户单击"停止"按钮时,未调用关联的插槽。同时,机器继续无休止地运行,看不到停止请求(我已经放了一些调试消息来查看真正执行的内容(。
代码片段
这是代码片段。mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "controller.h"
class QPushButton;
class QWidget;
class QVBoxLayout;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(Controller& controller, QWidget *parent = 0);
~MainWindow();
private:
Controller& controller;
QPushButton* startButton;
QPushButton* stopButton;
QWidget* centralWidget;
QVBoxLayout* layout;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
MainWindow::MainWindow(Controller &controller, QWidget *parent) :
QMainWindow(parent), controller(controller)
{
centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
layout = new QVBoxLayout();
startButton = new QPushButton("START", this);
stopButton = new QPushButton("STOP", this);
layout->addWidget(startButton);
layout->addWidget(stopButton);
centralWidget->setLayout(layout);
connect(startButton, SIGNAL(released()), &controller, SIGNAL(startSignal()));
connect(stopButton, SIGNAL(released()), &controller, SIGNAL(stopSignal()));
}
MainWindow::~MainWindow()
{
delete stopButton;
delete startButton;
delete layout;
delete centralWidget;
}
Controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include <QThread>
class MainWindow;
class Worker;
class Controller : public QObject
{
Q_OBJECT
public:
Controller();
virtual ~Controller();
signals:
void startSignal() const;
void stopSignal() const;
private:
MainWindow* mainWindow;
QThread workerThread;
Worker* worker;
};
#endif // CONTROLLER_H
controler.cpp(继承public qobject(
#include "controller.h"
#include "mainwindow.h"
#include "worker.h"
Controller::Controller()
{
mainWindow = new MainWindow(*this);
mainWindow->show();
worker = new Worker();
worker->moveToThread(&workerThread);
connect(this, SIGNAL(startSignal()), worker, SLOT(startProcess()));
connect(this, SIGNAL(stopSignal()), worker, SLOT(stopProcess()));
workerThread.start();
}
Controller::~Controller()
{
workerThread.quit();
workerThread.wait();
delete worker;
delete mainWindow;
}
工人处理具有枚举的State
和Transition
的状态机。Worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class Worker : public QObject
{
Q_OBJECT
public:
enum State { IDLE, STATE_1, STATE_2 };
enum Transition { OK, ERROR };
enum Mode { MODE_1, MODE_2 };
explicit Worker();
void read();
public slots:
void startProcess();
void stopProcess();
void processEvent(const Transition& transition);
signals:
void sendSignal(const Transition& transition) const;
private:
State currentState;
Mode selectedMode;
bool stopRequested;
};
#endif // WORKER_H
worker.cpp(继承public qobject(
#include "worker.h"
#include <QDebug>
#include <QThread>
Worker::Worker() : QObject()
{
stopRequested = false;
currentState = IDLE;
connect(this, SIGNAL(sendSignal(Transition)), this, SLOT(processEvent(Transition)));
}
void Worker::read()
{
qDebug() << "Reading...";
QThread::msleep(500);
emit sendSignal(OK);
}
void Worker::startProcess()
{
qDebug() << "Start requested";
selectedMode = MODE_1;
stopRequested = false;
emit sendSignal(OK);
}
void Worker::stopProcess()
{
qDebug() << "Stop requested";
stopRequested = true;
}
void Worker::processEvent(const Worker::Transition &transition)
{
qDebug() << "Process event";
switch(currentState) {
case IDLE:
switch(selectedMode) {
case MODE_1:
currentState = STATE_1;
read();
break;
case MODE_2:
currentState = STATE_2;
break;
}
break;
case STATE_1:
if (!stopRequested) {
if (transition == OK) {
read();
} else {
currentState = IDLE;
// No emission. The state machine stops on error
}
}
break;
case STATE_2:
// Not implemented yet
break;
}
}
.pro文件
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = sample_project
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += main.cpp
mainwindow.cpp
controller.cpp
worker.cpp
HEADERS += mainwindow.h
controller.h
worker.h
免责声明代码无法正确戒烟。最好在IDE中启动它,以便您可以轻松杀死它。
这些代码片段已使用QT5.8.0 Mingw 32bits构建。为了重现问题,只需点击"开始",调试消息就会出现在控制台中。然后点击"停止",消息不断到达,不要按照应有的停止。
我通过直接从Controller
调用stopProcess()
而不是使用信号来找到解决方法。这样做可以正确设置stopRequested
并停止该过程。
不过,我想知道为什么事件队列永远不会从Controller
处理信号?即使使用信号/插槽处理状态机器,事件队列在到达时可以处理事件。
(我尝试将中介插槽放在Controller
中,该插槽将信号发送到Worker
,以查看GUI是否正确发送了信号,并且确实执行了该插槽。但是stopProcess()
插槽仍未完成。(
有什么想法?
正如Oktalist指出的那样,问题是您永远不会返回工作人员线程中的QT事件循环。默认情况下,QT使用Qt::AutoConnection
,如果接收器生活在同一线程中,则是Qt::DirectConnection
。因此,QT以无限的方式递归地调用processEvent
。
解决方案1:从这两个线程中写入/读取stopRequested
。
正如您建议的那样,直接从Controller
调用stopProcess
可能会解决您的问题,但不是线程安全。您可以将stopRequested
定义为volatile
,但这仅在Windows上起作用,并且可能在其他情况下起作用。
更好的方法是将其定义为std::atomic
,如果C 11是您的选择。
解决方案2:避免递归功能调用
您可以指定您想要哪种连接类型的QObject::connect
的第五参数。选择Qt::QueuedConnection
将打破您的递归动作。这样,QT将能够处理您的stopRequested
信号。
此方法的优点是所有线程安全问题均由QT透明地处理,但这会使您的状态机器稍慢。
- Qt 信号/插槽问题
- Qt信号和插槽如果从QRunnable或其他线程调用,则不起作用
- C++Qt信号和插槽
- 使用std::move将std::unique_ptr作为qt信号参数传递
- 使用std::bind将Qt信号连接到插槽
- 如果代码中没有连接任何插槽,是否有理由发出Qt信号?
- 文档在哪里说明如果参数不是从末端删除参数,则无法从QT信号插槽连接中删除参数?
- 按钮组上的 Qt 信号未连接
- 通过排队的QT信号插槽连接发送QSHAREDPOINTERS的QVECTOR
- QT信号和插槽无法提供错误消息
- 如何监控Qt信号事件队列深度
- QT信号在虚拟机中不连接
- 为什么事件队列未处理我的QT信号
- 如何在打印机完成打印时从打印机获取Qt信号?
- 这是在Qt信号和插槽中使用参数调用函数的好方法吗?
- 删除 QObject 后 Qt 信号仍在触发
- Qt c++信号槽 - 清除信号所必需的
- 一个类中的QT信号/插槽,但从不同的线程发出
- QT:C 信号到QML连接
- 来自静态方法的QT信号