如何在Qt中运行函数时断开所有信号
How to disconnect all signals while function is running in Qt?
我一直在寻找拖车日,但没有什么可以帮助我。我想在函数运行时断开所有信号。主要技巧是发射信号的类和接收信号的类都是不同的类。我在类中有一个发出信号的QPushButton
,我的自定义类Screen
接收信号,它们都是连接的。
The class that manages events (class sender)
Game::Game(QWidget *parent)
: QGraphicsView(parent)
{
//.................................//some declaration
//add the main screen
Screen * screen = new Screen();
screen->SetImageOnTheScreen();
scene->addItem(screen);
//make the lunch work
QPushButton * GO = new QPushButton;
GO->setText("GO!!!");
GO->setGeometry(134,-98,134,99);
scene->addWidget(GO);
QObject::connect(GO, SIGNAL(clicked(bool)), screen, SLOT(generate()));
//want to disconnect this connection while generate() doing its job
//.................................//some declaration
}
Class receiver
void Screen::SetImageOnTheScreen()
{
//.................................//some declaration
}
void Screen::setWinningIcon()
{
//.................................//some declaration
}
void Screen::generate()
{
line = 0;
std::random_shuffle(mas, mas + 27);
SetImageOnTheScreen();
if(mas[0] == mas[1] || mas[0] == mas[2] || mas[1] == mas[2])
{
line = 1;
delay();
setWinningIcon();
delay();
SetImageOnTheScreen();
}
if(mas[3] == mas[4] || mas[3] == mas[5] || mas[4] == mas[5])
{
line = 2;
delay();
setWinningIcon();
delay();
SetImageOnTheScreen();
}
if(mas[6] == mas[7] || mas[6] == mas[8] || mas[7] == mas[8])
{
line = 3;
delay();
setWinningIcon();
delay();
SetImageOnTheScreen();
}
}
我试图使用disconnect
,但什么都没有。正在尝试使用 QObject::blockSignals()
同样的故事。
请任何帮助会很棒!!
更新
game.h
#ifndef GAME_H
#define GAME_H
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QPushButton>
#include "screen.h"
class Game : public QGraphicsView
{
private:
QGraphicsScene * scene;
QPushButton * GO;
Screen * screen;
protected:
virtual void wheelEvent (QWheelEvent * event);
public:
Game(QWidget *parent = 0);
~Game();
};
#endif // GAME_H
game.cpp
#include "game.h"
#include <QGraphicsGridLayout>
#include <QDebug>
#include <QPushButton>
#include "screen.h"
#include <QStyle>
Game::Game(QWidget *parent)
: QGraphicsView(parent)
{
scene = new QGraphicsScene(this);
scene->setSceneRect(0,0,400,200);
screen = new Screen;
screen->SetImageOnTheScreen();
scene->addItem(screen);
GO = new QPushButton;
GO->setGeometry(0,0,100,50);
GO->setText("GO");
QObject::connect(GO, SIGNAL(clicked(bool)), screen, SLOT(generate()));
//when generate is processing i want to disconnect the button "GO" and the "screen"
scene->addWidget(GO);
setScene(scene);
show();
}
void Game::wheelEvent(QWheelEvent * event)
{
if (event->type() == QEvent::Wheel)
return;
return;
}
Game::~Game()
{
}
screen.h
#ifndef SCREEN_H
#define SCREEN_H
#include <QGraphicsPixmapItem>
#include <QGraphicsTextItem>
class Screen : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
private:
int mas[27];
int line;
QGraphicsTextItem * text;
public:
Screen(QGraphicsPixmapItem * parent = 0);
void delay(int msecs);
void setWinningIcon();
void SetImageOnTheScreen();
public slots:
void generate();
};
#endif // SCREEN_H
screen.cpp
#include "screen.h"
#include <QDebug>
#include <QTime>
#include <QCoreApplication>
#include "game.h"
Screen::Screen(QGraphicsPixmapItem * parent)
: QObject(), QGraphicsPixmapItem(parent)
{
for(int i = 0, j =1; i < 27; i++, j++)
{
if(j == 10)
j = 1;
mas[i] = j;
}
line = 0;
text = new QGraphicsTextItem(this);
}
void Screen::delay(int msecs)
{
QTime dieTime= QTime::currentTime().addMSecs(msecs);
while (QTime::currentTime() < dieTime)
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
void Screen::setWinningIcon()
{
switch(line)
{
case 1:
text->setPlainText("TEST_#2 I'm NOT allowed to press /GO/ nfor 3 seconds but i can");
text->setDefaultTextColor(Qt::red);
break;
case 2:
text->setPlainText("TEST_#3 I'm NOT allowed to press /GO/ nfor 3 seconds but i can");
text->setDefaultTextColor(Qt::red);
break;
case 3:
text->setPlainText("TEST_#4 I'm NOT allowed to press /GO/ nfor 3 seconds but i can");
text->setDefaultTextColor(Qt::red);
break;
}
}
void Screen::SetImageOnTheScreen()
{
text->setPlainText("TEST_#1 I'm ALLOWED to press /GO/");
text->setPos(0,50);
text->setDefaultTextColor(Qt::green);
}
void Screen::generate()
{
//In here i want to prevent to press "GO" while "generate()" is processing
//Screen::grabMouse(); //here it is my solution
std::random_shuffle(mas, mas + 27);
SetImageOnTheScreen();
if(mas[0] == mas[1] || mas[0] == mas[2] || mas[1] == mas[2])
{
line = 1;
delay(500);
setWinningIcon();
delay(3000);
SetImageOnTheScreen();
}
if(mas[3] == mas[4] || mas[3] == mas[5] || mas[4] == mas[5])
{
line = 2;
delay(500);
setWinningIcon();
delay(3000);
SetImageOnTheScreen();
}
if(mas[6] == mas[7] || mas[6] == mas[8] || mas[7] == mas[8])
{
line = 3;
delay(500);
setWinningIcon();
delay(3000);
SetImageOnTheScreen();
}
//Screen::ungrabMouse(); //here it is my solution
}
main.cpp
#include "game.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Game w;
w.show();
return a.exec();
}
更新
我采用了您发布的最新代码并对其进行了一些更改。
总结
让我们从您的评论开始:
我无法从屏幕上到达"GO"按钮.cpp除非它是全局的。和 我不想使用任何全局变量。
在尝试避免全局变量方面,您走在正确的轨道上。但是,我认为您的Screen
班真的不需要了解您的GO
按钮。相反,您可以按照我在讨论中的建议进行操作,即与其将GO
按钮直接connect
到Screen
的generate()
槽,不如将按钮connect
到Game
类中的单独on_button_clicked
事件处理程序,该处理程序将:
- 禁用或断开
GO
按钮, - 调用
screen
的generate()
方法,以及 generate()
返回后重新启用或重新连接按钮。
这也意味着generate()
可能不再需要成为插槽,但这取决于您。
您的代码 - 使用我建议的更新
我进行了以下更改:
在game.h
中,我在Game
类中添加了一个插槽:
private slots:
void on_go_clicked();
在game.cpp
中,我添加了如下实现:
void Game::on_go_clicked()
{
GO->setEnabled(false); // or QObject::disconnect(....)
screen->generate();
GO->setEnabled(true); // or QObject::connect(....)
}
并且还用下面的调用替换了构造函数的QObject::connect
调用:
QObject::connect(GO, SIGNAL(clicked(bool)), this, SLOT(on_go_clicked()));
现在,你可以让你的Screen
类不知道你的GO
按钮的存在,这意味着更少的耦合和复杂性,但仍然可以防止用户同时使用你的按钮,这是你想要的。
原始响应
基本上,您需要按如下方式使用QObject::disconnect
:
QObject::disconnect(emitter, SIGNAL(signalName()), receiver, SLOT(slotName()));
您可以在处理事件后的插槽中执行此操作。
断开感兴趣对象之间的信号/插槽是比出于多种原因(包括可能导致错误或其他意外行为的意外副作用(尝试断开整个应用程序的所有信号更好的方法。
在您的特定示例中,您有:
QObject::connect(GO, SIGNAL(clicked(bool)), screen, SLOT(generate()));
因此,要断开连接,您可能只需要在开始处理时编写以下内容:
QObject::disconnect(GO, SIGNAL(clicked(bool)), screen, SLOT(generate()));
然后在完成处理后重新连接。
或者,正如@Matt评论中所说,您可以简单地禁用用户界面中的按钮小部件,而不是弄乱信号。如果用户无法单击按钮,则用户无法发出信号。
这可能是一个更简单、更可靠的解决方案。
信号阻断更新
如果您仍然想要连接/断开连接并且使用的是Qt 5.3+,那么您应该使用QSignalBlocker
,它还负责将内容保留和恢复到以前的状态。引用他们的文档:
{ const QSignalBlocker blocker(someQObject); // no signals here }
因此等价于
const bool wasBlocked = someQObject->blockSignals(true); // no signals here someQObject->blockSignals(wasBlocked);
工作示例代码
下面是一个简短的示例应用,其中包含一个带有QPushButton
的窗口和一个QMainWindow
中的QListWidget
。
主窗口.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_button_clicked()
{
ui->listWidget->addItem(QString("Clicked"));
// this is the important line :o
QObject::disconnect(ui->button, SIGNAL(clicked(bool)), this, SLOT(on_button_clicked()));
}
主窗口.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
// this naming convention allows Qt to
// create connections automatically
void on_button_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
主.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
主窗口.UI
如果需要,可以复制粘贴 .ui 文件的内容以重现简单布局。如下:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="button">
<property name="text">
<string>Click Me</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>27</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
我使用的是blockSignals,它阻止信令对象发送信号:但是它不会阻止接收对象接收信号,这可能是您的情况。
当您想要为 Ui 小部件设置值但又不希望这些小部件发送 valueChanged 信号时,使用 blockSignals 非常有用。这方面的一个例子是,如果你有一堆小部件,它们都独立地更新了一些计算 - 你不希望它们中的每一个都重新计算,只是在它们全部设置好时重新计算一次。
我不会在这里使用断开连接/连接,因为您可能会遇到信号连接两次的问题。
- Qt VTK交互风格的信号到小部件
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 当套接字连接断开时检测C/C++Unix
- 如何将点击的信号和插槽添加到qt中的自定义按钮中
- 如何在没有信号的情况下从C++执行QML插槽
- 线程之间的布尔停止信号
- 如何在信号处理程序和普通函数中对全局变量进行互斥读写操作
- 当用户在qtablewidget中输入单元格时,如何获得信号?C++
- 有可能在信号处理程序中设置promise吗
- 代码在我的计算机上运行良好,但是在将其提交给coursera时遇到未知的信号11问题
- 升压信号2将插槽传递到成员功能以断开连接
- 断开信号失败
- 增强信号:在类接口中公开信号本身或连接/断开方法
- 升压信号2 - 通过插槽断开连接时出错
- 如何在Qt中运行函数时断开所有信号
- 我仍然需要从Qt5.5中的信号断开lambda吗?
- 正确的方式断开信号从两个qobject不被销毁/删除
- 安全地从boost::信号断开
- 断开并稍后重新连接Qt信号
- Boost信号自动断开lambda槽