I have been searching for tow days, but nothing that could help me.
I want to disconnect all signals while functions are running. The main trick is the class that emits signal and the class that receives it are both different classes. I have a QPushButton
in class that emits signals, and my custom class Screen
which receives signals, they are both connected.
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();
}
}
I was trying to use disconnect
, and nothing. Was trying to use
QObject::blockSignals()
the same story.
Please any help would be great!!
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();
}
I've taken the more recent code you've posted and made some changes to it.
Let's start with your comment:
i can't reach "GO" button from the screen.cpp unless it is global. And i do not want to use any globals.
You're on the right track in trying to avoid globals. However, I don't think your Screen
class really needs to know about your GO
button. Instead, you could do what I suggested in our discussion, which is that, instead of connect
ing your GO
button directly to your Screen
's generate()
slot, you should instead connect
the button to a separate on_button_clicked
event handler in your Game
class that will:
GO
button,screen
's generate()
method, andgenerate()
returns.This would also imply that generate()
may no longer need to be a slot, but that's up to you.
I've made the following changes:
In game.h
, I added a slot to the Game
class:
private slots:
void on_go_clicked();
In game.cpp
, I added the implementation as follows:
void Game::on_go_clicked()
{
GO->setEnabled(false); // or QObject::disconnect(....)
screen->generate();
GO->setEnabled(true); // or QObject::connect(....)
}
And also replaced your constructor's QObject::connect
call with the one below:
QObject::connect(GO, SIGNAL(clicked(bool)), this, SLOT(on_go_clicked()));
Now, you can keep your Screen
class unaware of the existence of your GO
button, which means less coupling and complexity, but still get to prevent the user from using your button in the meantime, which is what you want.
Basically, you need to use QObject::disconnect
as follows:
QObject::disconnect(emitter, SIGNAL(signalName()), receiver, SLOT(slotName()));
You can do this in the slot after the event gets handled.
Disconnecting the signals/slots between the objects of interest is a better approach than trying to disconnect all signals globally for the whole application for several reasons, including unintended side-effects that may lead to bugs or other unexpected behavior.
In your particular example, you have:
QObject::connect(GO, SIGNAL(clicked(bool)), screen, SLOT(generate()));
So to disconnect, you may only need to write this when you start processing:
QObject::disconnect(GO, SIGNAL(clicked(bool)), screen, SLOT(generate()));
And then re-connect after you're done processing.
Or, as @Matt said in a comment, you could simply disable the button widget in the user interface and not mess around with the signals. If the user cannot click the button, then the signal cannot be emitted by the user.
This is probably a simpler and more reliable solution.
If you still want to connect/disconnect and you're using Qt 5.3+, then you should use QSignalBlocker
, which also takes care of preserving and restoring things back to their previous state. Quoting from their docs:
{ const QSignalBlocker blocker(someQObject); // no signals here }
is thus equivalent to
const bool wasBlocked = someQObject->blockSignals(true); // no signals here someQObject->blockSignals(wasBlocked);
A short example app consisting of a window with a QPushButton
and a QListWidget
in a QMainWindow
follows below.
#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();
}
You can copy-paste the contents of the .ui file to reproduce the simple layout if needed. It's below:
<?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>