[英]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();
}
我采用了您发布的最新代码并对其进行了一些更改。
让我们从您的评论开始:
除非它是全局的,否则我无法从screen.cpp到达“转到”按钮。 而且我不想使用任何全局变量。
您在避免全局变量方面走了正确的路。 但是,我认为您的Screen
类确实不需要了解您的GO
按钮。 相反,您可以按照我在讨论中所建议的那样进行操作 ,即,与其connect
GO
按钮直接连接到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
的窗口。
#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()));
}
#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
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
如果需要,您可以复制粘贴.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非常有用。 例如,如果您有一堆独立地更新某些计算的小部件-您不希望每个小部件都重新设置,只在它们全部设置一次时重新计算。
在这里我不使用断开连接/连接,因为您可能会遇到信号两次连接的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.