I have a QDialog on my main thread and I have some logic that happens on a separate thread. When the logic begins, a signal is emitted connected to show() on the dialog. When the logic ends, a signal is emitted that is connected to hide() on the dialog. When the logic actually does work, the dialog is show/hide properly. If the logic does "nothing" and the signals are just emitted sequentially, the dialog doesn't always show/hide properly.
My connections are made similar to this:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent = 0) :
Ui(new Ui::MainWindowUi),
Transferer(new DataTransferer()),
TransferProgress(this),
TransferThread()
{
Ui->setupUi();
connect(&Transferer, SIGNAL(Begin()), &TransferProgress, SLOT(show()));
connect(&Transferer, SIGNAL(End()), &TransferProgress, SLOT(hide()));
Transferer.moveToThread(&TransferThread);
TransferThread.start();
Transferer.DoStuff(true);
}
virtual ~MainWindow()
{
TransferThread.quit();
TransferThread.wait(1000);
delete Ui;
Ui = NULL;
}
private:
Ui::MainWindowUi* Ui;
DataTransferer Transferer;
TransferProgressDialog TransferProgress;
QThread TransferThread;
}
The logic looks similar to this:
class DataTransferer : public QObject
{
Q_OBJECT
public:
DataTransferer(QObject *parent) : QObject(parent) {}
virtual ~DataTransferer() {}
void DoStuff(bool dontDoStuff)
{
emit Start();
if (!dontDoStuff)
{
QThread::sleep(1);
}
emit End();
}
}
When the DataTransferer does stuff, everything works fine. When the dialog is shown and hidden in rapid succession, I get the ghost dialog approximately every other time I call DoStuff().
I used QThread::currentThreadId() and verified that the dialog and logic are running on separate threads.
Why would my dialog not hide properly in this case? Should I just force my logic to always run for at least a few hundred milliseconds (that solution is bad)? Is there a way I can have my dialog make sure it's fully loaded before trying to hide itself? Should I handle these signals/slots differently?
EDIT: I've currently resigned to just putting a QThread::sleep(1) after I emit the signal to show() the dialog. I don't like this solution, but nothing else has seemed to work. The sleep(1) allows the dialog to come all the way up before hiding it. I was also able to get this to work with QThread::msleep(10), but that still resulted in the ghost dialog about 1 in 6 tries.
I tried using a member QMutex in the dialog logic whenever I called either show() or hide(), but this didn't work.
I changed all cross-thread connections to use Qt::BlockingQueuedConnection and Qt::QueuedConnection and neither attempt was successful.
I tried moving the slot connections from the dialog to the object that sets up the connections and then calling the slots directly, but that didn't prove successful either.
I have the same issue, show dialog and when get some signals to close it, when time less than 20ms(which means quickly hide the dialog), it will left a ghost dialog.
So, i just use
QTimer::singleShot(50, this, [this](){
hide(); //hide dialog
});
in close handler function. It seems work well.
My guess is that the problem occurs because "show" and "hide" calls interlace. To verify this, use a semaphore - lock the object until show have finished, and wait on it in hide. Also look at the top-voted answer here for another possible (perhaps better) solution: connecting signal/slot across different threads between QObjects
Use Qt::BlockingQueuedConnection to connect signals to slots. Be sure that event loop of main thread is not blocked. Also, if your worker thread uses a lot of cpu time - you may call QThread::yeldCurrentThread() call.
Related
I am looking for a way to delay or abandon signal under some circumstances.
My GUI looks like that - I have a MainWindow frame, which contains QStackedWidget with several QWidgets. Each QWidget 'provides' mainIcon to the parent widget (MainWindow), which is then connected to a slot realizing switching between pages functionality.
I want to connect this mainIcon to a signal, show a QMessageBox and if user confirms - everything will happen normally (a signal will be passed and analyzed by parent Widget) - after a delay(user has to confirm).
However, if he declines, a signal should not be passed, and nothing should happen.
What I have came up with so far is:
connect(mainIcon, &GuiIconAction::clicked, this, [=](){
//block signals of mainIcon - should be somehow ensured that this is the first realized slot
if(confirmRequired)
{
if(QMessageBox::warning(this, "Manager",
"Are you sure you want to switch window?",
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
//get blocked signals and call them
}
});
Important notes are:
GuiIconAction is a QToolButton,
Widget don't know that the parent exists (i.e. it just provides interface, but could also be placed standalone)
I don't want to realize this functionality in parent widget (MainWindow), as it is only a frame - I want it to be realized on the level of particular Widget in QStackedWidget
I would suggest to implement the functionality with a slot.
In the slot you can create the message box. If the user confirms, emit a signal. Otherwise not.
I have a MainWindow class in Qt, in which several checkable QGroupBox widgets are placed.
What I want to do is, to trigger a general onClick(bool checked) slot whenever one (any) of the QGroupBox objects are clicked, identify the sender and trigger some code.
I need to capture the objects' "clicked" signal in order to prevent a disabling action it performs on its children when the control is clicked.
This is the signal I'm trying to handle:
class Q_WIDGETS_EXPORT QGroupBox : public QWidget
{
...
Q_SIGNALS:
void clicked(bool checked = false);
...
};
I tried adding a custom slot like this and tried connecting it with the signal above but since QGroupBox at its own is not an object or pointer, the operation fails.
void MainWindow::onClick(bool clicked)
{
qDebug()<<"Custom slot triggered";
}
Long story short, I need to handle a control type's default behavior within my MainWindow class.
Thanks for any ideas in advance.
I need to capture the objects' "clicked" signal in order to prevent a disabling action it performs on its children when the control is clicked.
Perhaps, but I'm smelling an XY Problem here. You can certainly prevent emission of signals by invoking blockSignals() on the widget. But that's a hack - it will also prevent Qt's internals from acting on the object's destroyed() signal, and you might subtly break other users of the object's signals.
Instead, one could make the UI stateful and have the controller implement the stateful aspect of the button's interaction with the rest of the application. Currently, the button's clicked() signal is connected directly to other users. Instead, connect the button to the controller, and have the controller disable the children only when it's appropriate to do so. One could use the QStateMachine to make this stateful behavior explicit in terms of states.
#Kuba Ober, maybe you're right, I could not state my problem and the facts around it seperately and clearly. Thank you for pointing that out.
After checking out the QGroupBox class' source, I discovered that a "toggle" signal is emitted after every other operation in a "clicked" event.
http://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/widgets/qgroupbox.cpp
void QGroupBox::setChecked(bool b)
{
Q_D(QGroupBox);
if (d->checkable && b != d->checked) {
update();
d->checked = b;
d->_q_setChildrenEnabled(b);
#ifndef QT_NO_ACCESSIBILITY
QAccessible::State st;
st.checked = true;
QAccessibleStateChangeEvent e(this, st);
QAccessible::updateAccessibility(&e);
#endif
emit toggled(b);
}
}
By combining this information with that of #rbaleksandar 's suggestion, I managed to obtain a solution.
I iterated through the controls of my MainWindow class and connected every QGroupBox's "toggle" signal with a common "MainWindow::onToggle" slot in which I could handle the post-event operations I desire.
As for my previous approach, I could've created a class which inherits QGroupBox and override any methods I wish to. But that would be overkill compared to my current solution. The fix I've came up with did the trick perfectly.
Thanks everyone for their insight and help.
I have a complex widget on the screen which is visible. By pushing a button a dialog appears but parts of the original widget is still visible. When closing the dialog I would like to refresh some data of my particular widget ... but this widget does not know anything about that dialog. Is there any event I can check for this purpose ? Or any other way to get to know when the dialogs closed ?
First I used
virtual void QWidget::showEvent( QShowEvent* event );
but it is not called when the parts of the widget is already visible.
EDIT
I'm sorry but I wasn't precise enough. When I wrote complex, I meant that I have a main window, which has a child widget which also has child widget and so on (for about file level). That lowest level child widget initiates the opening of the dialog. The other widget which needs the closing event of this dialog is also an embedded widget but somewhere else in the application. When I wrote sending the signal through many classes I meant this. In this case if want to notify my final widget I have to connect the closing signal from my source widget to several intermediate widgets which does not even interested in that signal. I also didn't want to (and can't) connect these signal/slot in the main window because of the mentioned structure. In this is there any advice ?
send the signal through many objects just for this purpose
There's no such thing as sending a signal "through" objects unless you insist on manually threading it through a bunch of them. So this doesn't make much sense unless you explain why you want to "thread" the signal through many objects ("threading" as in a string through a needle, not as in a processing thread).
A signal-slot connection has two endpoints. All you need is:
A signal at the dialog.
A slot at the supercomplex widget.
A connection set up outside of both of them. To make life simple, both the widget and the dialog could be instantiated in main(), and a connection set up before anything even appears on the screen.
The dialog signal you're looking for is simply QDialog::finished, if you have a properly designed dialog. Otherwise, use an EventSignaler of some sort to convert a QEvent::close to a signal.
The slot in the supercomplex widget should be e.g.:
Q_SLOT void refreshSomething();
Its signature/name shouldn't refer to the fact that some dialog was closed: that's coupling the API too much to the external detail of some dialog being there. The slot should get the dialog to update/refresh what you need. The reasons for invoking it are irrelevant to the widget.
Inside the widget, the slot's implementation should most likely simply call update() on one or more subwidgets, perhaps with a relevant region - if you wish to, you can have the region as an optional slot parameter.
See this question about how to forward signals or slots inside of a complex composite class that contains many objects. It's supposed to be very simple; if it's not you'll have to edit the question to indicate why it's complex for you.
At the simplest, with the most decoupled code - where the dialog knows nothing about the widget, and vice versa - as it should be, you can have:
class Dialog : public QDialog { ... };
class Widget : public QMainWindow {
Q_OBJECT
Q_SIGNAL void showDialog();
Q_SLOT void refreshSomething();
...
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
...
Widget widget;
Dialog dialog;
QObject::connect(&widget, &Widget::showDialog, &dialog, &QWidget::show);
QObject::connect(&dialog, &QDialog::finished, &widget, &Widget::refreshSomething);
widget.show();
return app.exec();
}
I want to update my database just before my Qt application closes.
I want something like connect(this, SIGNAL(quit()), this, SLOT(updateDatabase()))
One way could be to introduce a quit button, but is it possible to achieve this functionality if user presses Alt+F4?
Use signal aboutToQuit() instead.
This signal is emitted when the application is about to quit the main
event loop, e.g. when the event loop level drops to zero. This may
happen either after a call to quit() from inside the application or
when the users shuts down the entire desktop session.
The signal is particularly useful if your application has to do some
last-second cleanup. Note that no user interaction is possible in this
state.
For example :
connect(this, SIGNAL(aboutToQuit()), this, SLOT(updateDatabase()));
There is another way to do it, not aboutToQuit() signal, but to re-implement the closeEvent(QCloseEvent *event). You can call you slot before the statement event->accept();
like this:
void MainWindow::closeEvent(QCloseEvent *event)
{
call_your_slot_here();
// accept close event
event->accept();
}
I'm writing a simple game in C++ using the (awesome) Qt4 libraries. For a while now, I'm having trouble with the following:
The widget provides (among others) 2 methods, that change the way the widget responds to user input like mouse-movement an clicks etc. Let's call these functions fun1() and fun2() to avoid context. All these functions do, is toggle some internal variables in the widget which are checked by all the event-functions (e.g. mouseMoveEvent()). When fun1() is called, the user is expected to perform a move by interacting with the widget and clicking somewhere. The widget checks whether his move was valid and if so, applies his move to the current gamestate.
Now, the main class containing this widget at some point calls fun1() and fun2() subsequently. I have to implement something (elegant) to ensure that the main loop sits around at the end of fun1() until the move has been completed.
void Widget::fun1()
{
// Set some members in order to tell the Widget how to respond to events
// ...
wait(); // wait for the move to be completed before returning from fun1()
}
How should wait() be implemented whilst maintaining the responsiveness of the GUI, i.e. whilst responding to user interaction?
In the past, I resolved this issue by letting the widget's parent wait for the widget to emit a signal that notifies it has finished. Only when this signal was recieved did I call fun2(). Though this might work fine, it bothers me that it makes the act of waiting the responsibility of the class using the widget instead of the widget itself.
Any suggestions would be very helpful!
First of all, let me apologize for answering my own question. It turns out that the solutions that I had already tried did work, but that I stupidly called fun1() and fun2() from the constructor of the main window. Therefore, app.exec() was never called because of the waiting-function and events were not handled.
The working implementation of wait() for those interested is:
void Widget::wait()
{
static QEventLoop loop;
connect(this, SIGNAL(done()), &loop, SLOT(quit()));
loop.exec();
}