I am programming a GUI with wxWidgets. I got a button to open a serial port and receive data. In there is a while(1) loop to constantly receive the data.
Now I want to do a "Disconnect" button to stop receiving.
Is there an event handler or a callback function to interrupt the while and jump out of the loop from the first button, when I press another button?
A wxButton sends a message when it is clicked. Create a handler for this message where you do your "interrruption".
Now, a loop can be only exited from its inner guts. I mean, inside the loop you should check a flag, and depending on the value of this flag continue or exit.
My advice is that you create a worker thread. When the button "connect" is clicked then you create the thread that receives data.
This thread checks an extern flag and finishes depending on the value of the flag.
The "disconnect" button click-handler just sets that extern flag to a value that makes the thread exits when it checks that flag.
Take a look at the thread sample provided with wxWidgets sources.
If you have a while(1) loop in the callback function for "button pressed", you will hang the UI. Callback functions must return quickly.
The quickest solution is to put the while(1) loop into a worker thread. That way your GUI won't hang.
The next problem is how to stop the loop. That's quite easy. Change the loop into:
while (keep_going.load()) {
....
where keep_going is of type std::atomic_bool. Then your "stop" button just calls keep_going.store(false).
Related
If I have a progressbar in the ui thread (window), which shall run endless until a method finishes it's work, will the ui thread block and so the progress bar if I'm waiting for a second QThread to finish? If the ui thread blocks waiting, then i would not to wait for the second thread. I thought to implement a callback method which will be called when the second thread finished, but then: How can i connect to the callback method?
What do I want to do?
I have a window, this window has a progressbar which is first not visible. When the user presses a certain button to request data, a method will be called which returns a RequestPointer which contains a method which returns the request status.
When the user presses the button, i want to make the progress bar visible, running infinitely till the request is finished and i can print the data to the window.
To the Worker i want to pass this pointer and the worker checks in a while (flag) loop, if the status is still running and sleep if so. When the worker finishes, i want to stop the progressbar and make it unvisible again. Do i have to pass the progress bar to the thread, or can i wait for the thread without blocking the ui?
I'm not quite a Qt pro. Really new to it. I tried to get some info from the https://doc.qt.io/Qt-5/qthread.html website, but it's a bit hard for me to understand the code example.
Method in my worker class:
void Worker::watchRequest(RequestPtr r_ptr)
{
bool exit = true;
while (!exit)
{
ErrorCode errorCode = r_ptr->Test();
switch (errorCode)
{
case Request_RUNNING:
QThread::msleep(10);
break;
case Request_ABORTED:
exit = true;
break;
case Request_SUCCESS:
exit = true;
break;
}
}
QThread has a finished signal. Connect this one to some appropriate slot, which will trigger any action necessary on thread completion.
I suppose best candidate to know how far the progress went is the worker thread itself. You might create your own signal that sends the current progress to some slot that will do the update of the progress bar appropriately.
Alternatively, you might use a QTimer to read the current progress from time to time (this resembles closer to what you have now, but does not block the UI).
If you don't want to block the user interface, all you have to do is to call QApplication::processEvents(); in your while-loop.
I have some code which uses std::future instead of QThread and my code looks like this:
while (!progressIndicator->UserBreak()
&& (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready))
{
QApplication::processEvents();
}
This works well.
To update the UI Thread's progress bar while the operation is running, use a QTimer object to increment the progress bar's value (Max value will be :One less than Progress bar's value when the operation completes). Also connect the QThread to a slot by Signal/Slot method to signal the UI thread when the operation has ended. When the QThread finishes the operation, send a signal to a Slot in the UI Thread, which will set the final value of the progress bar and also stop the QTimer.
I have a function that could run in an infinite loop, so I need a way of stopping the function at the press of a button. The problem is that while the function is running, the Qt widgets become unresponsive so I can't just set a stop bool when the button is pressed.
I am unsure of how to do this:
Connect the button signal to a QEventLoop and use that to stop the function (don't know how that would work) or have the function in a separate thread (never done that before)?
Basically exactly what the title says. I would like to update the text that a button contains every 1 second when the user presses that particular button. I have noted that when the program doesn't have focus it works alright and the text refreshes correctly but when I am hovering over the program or when I am trying to click on it's menu Windows inform me that the program is unresponsive and asks me if I want it terminated. When the loop finishes the program returns to its normal state. Also any action I might have done (like moving it around or closing it) while it was Sleep()-ing is executed after the loop. Here is a bit of code:
case ID_BUTTON_START:
// Code executed when pressing Start Button.
char startButtonText[30]; // Storing next loop text
for (int i=5; i>0; i--)
{
sprintf(startButtonText, "Starting in ... %d", i);
SendMessage(hwndButtonStart, WM_SETTEXT, 0, (LPARAM)(startButtonText));
Sleep(1000);
}
Is this normal? If not what's causing this?
The WndProc does not process messages asynchronously within an application which means all messages are expected to be handled quickly and a return value delivered immediately. You must not Sleep in the UI thread since it will block other UI events from being processed. Any heavy work or synchronous requests/jobs which are likely to take a long time should be performed in worker threads. There are at least three viable options:
Create a new (worker thread) for the task.
If the task is likely to be done often, use a thread pool instead.
Set and subscribe to timer events.
I think the call to Sleep() might be keeping you from returning from the WndProc, so your application is not processing the incomming events for 5 secs. I suggest you try to subscribe to 5 timer events in 1s, 2s,..., 5s. Like when the timer message is recieved the button text must change. I don't know a way how to do that off the top of my head.
I've been trying to figure out how to make the program stop in a for-loop and wait for a button to be clicked and the for-loop continues.
Isn't there any easy way to stop the loop like QSystem.stop() and in the button clicked function QSystem.star() and the loop continues.
In C++ you could use system("pause") or the program stopped when you used a cin<<. But how do i do this in QT?
Since the userinterface needs its code to run, if you halt event loop (which also means that any function that gets called from within the event loop is blocking, waiting for some event), you also halt the user interface, which means clicks into the window won't be processed. That's why in event based programming, which is what all UI kits do, it is essential to return to the main event handler loop as quickly as possible.
What you can do is create a second thread and wait on a condition variable. The GUI thread can signal that condition variable in the button click event slot.
In C++ you could use system("pause") or the program stopped
Exactly: The program is stopped. Which means it won't fetch events from the operating system. However receiving data from stdin is not an event. It's blocking on streamed I/O. Graphical user interfaces are event based though.
Note that conceptually it's not really impossible to think of a user interface to provide streaming I/O channels. However that doesn't work in single threaded programs. You need parallel execution (coroutines, threads, or such) for a concept like this to work.
You don't need threads, nested event loop will do the job.
int waitUntilSignalIsEmitted(QObject *sender, const char *signal) {
QEventLoop loop;
connect(sender, signal,
&loop, SLOT(quit()));
return loop.exec();
}
// usage:
while(yourLoopCondition) {
// some stuff
...
// pause here
waitUntilSignalIsEmitted(yourContinuationButton, SIGNAL(clicked()));
// loop continuation
...
}
Basically exactly what the title says. I would like to update the text that a button contains every 1 second when the user presses that particular button. I have noted that when the program doesn't have focus it works alright and the text refreshes correctly but when I am hovering over the program or when I am trying to click on it's menu Windows inform me that the program is unresponsive and asks me if I want it terminated. When the loop finishes the program returns to its normal state. Also any action I might have done (like moving it around or closing it) while it was Sleep()-ing is executed after the loop. Here is a bit of code:
case ID_BUTTON_START:
// Code executed when pressing Start Button.
char startButtonText[30]; // Storing next loop text
for (int i=5; i>0; i--)
{
sprintf(startButtonText, "Starting in ... %d", i);
SendMessage(hwndButtonStart, WM_SETTEXT, 0, (LPARAM)(startButtonText));
Sleep(1000);
}
Is this normal? If not what's causing this?
The WndProc does not process messages asynchronously within an application which means all messages are expected to be handled quickly and a return value delivered immediately. You must not Sleep in the UI thread since it will block other UI events from being processed. Any heavy work or synchronous requests/jobs which are likely to take a long time should be performed in worker threads. There are at least three viable options:
Create a new (worker thread) for the task.
If the task is likely to be done often, use a thread pool instead.
Set and subscribe to timer events.
I think the call to Sleep() might be keeping you from returning from the WndProc, so your application is not processing the incomming events for 5 secs. I suggest you try to subscribe to 5 timer events in 1s, 2s,..., 5s. Like when the timer message is recieved the button text must change. I don't know a way how to do that off the top of my head.