如何在Qt中运行函数时断开所有信号

How to disconnect all signals while function is running in Qt?

本文关键字:断开 信号 函数 运行 Qt      更新时间:2023-10-16

我一直在寻找拖车日,但没有什么可以帮助我。我想在函数运行时断开所有信号。主要技巧是发射信号的类和接收信号的类都是不同的类。我在类中有一个发出信号的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按钮直接connectScreengenerate()槽,不如将按钮connectGame类中的单独on_button_clicked事件处理程序,该处理程序将:

  1. 禁用或断开GO按钮,
  2. 调用screengenerate()方法,以及
  3. 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 非常有用。这方面的一个例子是,如果你有一堆小部件,它们都独立地更新了一些计算 - 你不希望它们中的每一个都重新计算,只是在它们全部设置好时重新计算一次。

我不会在这里使用断开连接/连接,因为您可能会遇到信号连接两次的问题。