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();
}
Related
My first naive at updating my progress bar was to include the following lines in my loop which is doing the processing, making something like this:
while(data.hasMoreItems())
{
doSomeProcessing(data.nextItem())
//Added these lines but they don't do anything
ui->progressBar->setValue(numberProcessed++);
ui->progressBar->repaint();
}
I thought adding the repaint() would make the execution pause while it updated the GUI, but apparently it's not that simple. After looking at the questions:
QProgressBar Error
Progress bar is not showing progress
it looks like I'm going to have to put the data processing in a different thread and then connect a signal from the data processing thread to the GUI thread to update the progressbar. I'm rather inexperienced with GUIs and threads and I was wondering if anyone could just point me in the right direction, ie what Qt classes should I be looking at using for this. I'd guess I need a QThread object but I've been looking through the QProgressBar documentation but it doesn't bring up the topic of threading.
As #rjh and #Georg have pointed out, there are essentially two different options:
Force processing of events using QApplication::processEvents(), OR
Create a thread that emits signals that can be used to update the progress bar
If you're doing any non-trivial processing, I'd recommend moving the processing to a thread.
The most important thing to know about threads is that except for the main GUI thread (which you don't start nor create), you can never update the GUI directly from within a thread.
The last parameter of QObject::connect() is a Qt::ConnectionType enum that by default takes into consideration whether threads are involved.
Thus, you should be able to create a simple subclass of QThread that does the processing:
class DataProcessingThread : public QThread
{
public:
void run();
signals:
void percentageComplete(int);
};
void MyThread::run()
{
while(data.hasMoreItems())
{
doSomeProcessing(data.nextItem())
emit percentageCompleted(computePercentageCompleted());
}
}
And then somewhere in your GUI code:
DataProcessingThread dataProcessor(/*data*/);
connect(dataProcessor, SIGNAL(percentageCompleted(int)), progressBar, SLOT(setValue(int));
dataProcessor.start();
You need to call QApplication::processEvents() periodically inside your processing loop to let it handle UI events.
As Georg says, Qt is a single-threaded co-operative multitasking environment. You get full control of your process until you relinquish it voluntarily with processEvents() - until you do that, Qt can't update the UI elements, handle async HTTP requests, handle input, or pretty much anything else. It's up to you to make sure that stuff gets a timeslice while you're in a long processing loop.
You can create a sub-class of QThread that emits a signal progressChanged, which you connect to the QProgressBar.
connect() makes the connections auto connections per default. That means that the signal-slot-mechanism already takes care of the threading issues for you, so you don't need to worry about that.
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.
I am developing Qt application (Qt version 4.7.3) on SBC6000x board.
I have a MessageBox class derived from QDialog. I have made this class singleton.
Whenever a messagebox is to be show I am using .exec method to show it.
There are few places where I need to show messageboxes one after another.
So, to show new messagebox, I have to close previous one and show new one.
e.g. When Messagebox is open and at same time I receive an error from background I have to close the messagebox which is currently shown and show the one with error.
To closes previous dialog I have exposed CloseDlg method from messagebox class and trying to close it.
Inside this CloseDlg I am emitting finished signal.
void CMsgBox::CloseDlg()
{
if (NULL != CMsgBox::m_msgBox)
{
if(CMsgBox::m_msgBox->isVisible())
{
emit CMsgBox::m_msgBox->finished(0);
//QApplication::processEvents();
}
}
}
and calling it as
CMsgBox::CloseDlg();
My show method is :-
int CMsgBox::showMsgBox(Icon icon, const QString &textMsg, const QString &okBtnText)
{
if (CMsgBox::m_msgBox == NULL)
{
CMsgBox::m_msgBox = new CMsgBox();
}
CMsgBox::m_msgBox->setText(textMsg);
CMsgBox::m_msgBox->setIcon(icon);
CMsgBox::m_msgBox->setOkBtnText(okBtnText);
CMsgBox::m_msgBox->exec();
return CMsgBox::m_msgBox->m_btnPressed; //return, unblock the call
}
Again when I call showMsgBox,it is showing me following warning.
QDialog::exec: Recursive call detected
Problem is, it doesn’t return from previous exec call (unless we return, as commented above //).
I tried same with close(), accept(), reject() methods instead of finished() event but nothing worked.
What is the way to return from previous exe call and achieve above scenario? Any help is welcome.
What you have here looks like a race condition. A modal QDialog runs its own event loop, so your application behaves like a multithreaded application and you need to take care of concurrency and race conditions.
When you receive a second in your main event loop, you call CMsgBox::CloseDlg() and CMsgBox::showMsgBox() in quick succession. However, CloseDlg() tells the dialog's event loop to return, but CloseDlg() actually returns before the dialog's event loop is done cleaning up, and showMsgBox() attempts to call exec() on a dialog which hasn't finished exiting yet.
What you need to do is, when you call CMsgBox::CloseDlg(), connect to the finished(int) signal, and only when you receive the finished(int) can you safely exec() the dialog again.
NOTE: When connecting to the finished(int) signal, make sure to use a Qt::QueuedConnection instead of a Qt::DirectConnection which is the default.
So, you need modeless dialog box. As explained in their documentation :
Modeless dialogs are displayed using show(), which returns control to the caller immediately.
Therefore, instead of showing the box with exec(), show it with show().
Alternative to show() method suggested in another answer is, use QDialog::open(). It will return, but will still give you modal dialog, so the rest of the GUI will be disabled until you close it.
I am doing one web related project. Recently I hit on this situation. This is just a stub
Class My_Class
{
public:
My_Class();
void start();
public slots():
void after_Load_Function();
}
My_Class::My_Class()
{
//Some initializations
connect(WebPage,SIGNAL(finished()),this,SLOTS(after_Load_Function()));
}
void My_Class::start()
{
WebPage->load();
}
void My_Class::after_Load_Function()
{
//Do something with the finished WebPage
}
int main(int argc,char * argv[])
{
//Some Qt things
My_Class a;
a.start();
}
"WebPage" emits the signal "finished" when it loaded fully.
Now the problem is before the "webPage" got loaded the "start" is returning. Thereby the control reaches the "main". So, now the control should return from "start" only after "after_Load_Function" finishes it's job. Thereby I want the below sequence,
main creates the My_Class object A.
main calls "start" from A.
start calls load from "WebPage" and it waits untill the "WebPage" emits "finished",
and that emit in turn calls the "after_Load_Function", and "after_Load_Function"
finishes it's job.
now, the "start" returns
main returns
But, I don't know how to make this kind of wait condition. How can I go about it?
You can do this by running a local event loop, letting the components process network income and load the page. When they emit the signal, you execute a slot on the event loop to quit it.
void My_Class::start()
{
QEventLoop qel;
QObject::connect(WebPage, SIGNAL(finished()), &qel, SLOT(quit()));
WebPage->load();
qel.exec();
}
I've been using this before and it works fine. I don't advice to use this too often though, because it will process events, including those that the caller of start might not be expecting to be processed during the call to start, so you need to document this to its callers. You can prevent the processing of some events by passing certain flags to QEventLoop::exec, like preventing to process user interface events.
You should never wait in UI code. You need to break your "main" function into pieces so the later part can be executed separately.
Use condition variables thats what they are used for. You can make threads wait on a condition variable and proceed when notified.
The WebPage->load() method is asynchronous, meaning that it runs immediately, not when the loading is complete. The operation runs in the background while you go to do other things.
This is considered a good thing, as it enables your app to be more responsive and get more done. For example, if your app has a GUI, you could update the GUI with some sort of animation that indicates that the web page is being retrieved.
If you prefer a model in which the application blocks until the page is fully loaded, then consider making this change:
void My_Class::start()
{
WebPage->load();
while (!WebPage->isLoaded())
Sleep(1);
after_Load_Function();
}
Notes:
the Sleep function will work on Windows. If you are on a Unix OS you can use usleep.
since this function will effectively block until the web page is loaded, there is no reason to use the signal from the web page object, you can just simply call your handler after the wait completes, as this will make your handler run in the same thread.
doing this is really bad practice. You may get away with it if your program is command line and has no GUI and/or event loop, but you should consider a better design for your app where the loading of web pages does not block the whole app.
I have a custom class inheriting from QDialog. I'm creating this dialog with function foo, and foo would like to continue doing its thing only when a certain button in the dialog is pressed. I was thinking of using signals and slots, but then how could I get foo to respond to a signal from another thread?
EDIT: basically I want to know how to reimplement the functionality of QInputDialog::getText() using my own dialog.
Your foo() function could call wait() on a [QWaitCondition][1] object, then your button could call wakeOne() on the same object to cause the wait() to return.
That said, there is really no necessity for using multithreading to reimplement QInputDialog::getText(). You should be able to reimplement that functionality inside a single thread without any problems, and doing it that way will be much simpler and more reliable.
(Note: assuming you want your version of getText() to block and not return until after a button is pressed, you'll need to call QDialog::exec(). I don't recommend that style of programming though, as it's error-prone... for example, what happens if the user closes your QInputDialog's parent window while the QInputDialog is still open? That deletes the QInputDialog object whose getText() method the program is still blocked inside, likely causing a crash because the QInputDialog's "this" pointer is now a dangling pointer. It's much cleaner and safer to make everything event-based instead (i.e. signals and slots only), and not attempt to block or recurse Qt's event loop in your own code)
http://doc.qt.io/qt-4.8/qdialog.html#modal-dialogs
Modal dialogs will block the user from interacting with other windows, which it sounds like you will need. Also, I think you want to call exec() instead of show(). Show() returns execution to the caller immediately, where as exec() blocks.