Long operations and keeping the GUI uptodate
-
hi - been trying to find a good solution but get stuck with old suggestions.
I am working on a fairly simply Qt 5.1 Desktop Application for myself and want to do the following:
/* pseudo code */
- load an image from file
- setup a few things (such as rescale image, convert to grayscale, set some start values, disable some menus, etc.)
- click on some Start! button or select some Start! action from the menu
- show a modal progress dialog with a cancel button (as following process may take a few hours, or even days, but that's OK, it's actually designed that way)
- foreach pixel do a few things (e.g. talk to microprocessor about pixel via a SerialPort, wait for microprocessor to say 'ready')
- update progress bar in progress dialog
- next pixel until either finished==true or cancel==pressed
- do some postprocessing, then back to square 1.
If I add a simple ui.form with such a progressbar and cancel button, and ui.exec() this, and then run my pixel-processing routine in my mainWindow (currently just a dummy doing nothing), and update the progressbar from there, nothing happens and the application sort of freezes.
I can't really go multithreading as I need to go pixel-by-pixel in proper order, and I need to keep my processing routine inside my mainWindow, as too many other things are linked to it, moving the routine into the progressWindow class would make it too complicated, I think.
Any simple but workable ideas? Input most appreciated.
O.
-
Hi and welcome to devnet,
I think you got something wrong: moving your logic to its own thread doesn't mean you'll parallelize the different pieces of the operation. This kind of things must be designed and use several threads.
Have your logic in one object and execute the long operation in another thread, you'll keep your GUI responsive and your code clean.
I would recommend the Mandelbrot example as a good source of inspiration.
Hope it helps
-
Hi SGaist,
many thanks for the rapid and informing reply. Will have a look at the Mandelbrot example you suggested.
I tried this so far, using QApplication::processEvents(), but it doesnt work with the progressDialog.exec() call, and using the show() or showNormal() calls requires a special handler for the .cancelled() signal ...
mainwindow.h:
@
class MainWindow : public QMainWindow
{
Q_OBJECT
QProgressDialog *progressDialog;
...
@mainwindow.cpp:
@
...
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
progressDialog = new QProgressDialog;
ui->setupUi(this);
...
}void MainWindow::on_action_Run_triggered()
{
/* do the run processing here. Pretend we're scanning an image /
progressDialog->setRange(0,100);
progressDialog->setValue(50);
progressDialog->showNormal();
// are we actually ever getting here?
// Nope, not with ->exec() but
// Yes! with ->show() and with ->showNormal() ...
int nmax = 6; int mmax = 8; // should last around 50 seconds for demo
for (int m=1; m<mmax; m++) {
for (int n=1; n<nmax; n++) {
int p = int(100(n+mnmax)/(nmaxmmax));
progressDialog->setValue(p);
sleep(1); // pretend slow process (but not too slow for debugging)
QApplication::processEvents();
}
}
progressDialog->close();
}
@O.
-
You have an example on how to use QProgressDialog in the documentation. You won't avoid the additional cancel variable, but it's not a bad design at all.
-
Yes, I found the example as well, thanks for the input.
I think the main issue I have is how to structure my problem: let me explain ...
I'm trying to write an Qt based interface to run some code on a microprocessor, offloading some hardware control to it. Think "polargraph":http://www.polargraph.co.uk/ (TM) and similar things. It's supposed to go sort-of like this:@
prepare_some_basic_settings() // such as serial port settings
load_image_from_file()
foreach pixel do {
some_simple_maths();
send_command_2_uP;
repeat // (this may take a minute or two!! -> use a thread to prevent gui hangup ?!?)
if (serialport.available()) then read_serial_port();
until (incoming_message == "ready");
update_progress_bar();
do_next_pixel() ;
until (last_pixel == true) OR (cancel_pressed()==true) }
@
The main problem I have is probably how to structure the code, so as not to do too many things too many times - i.e. where to include the serialport stuff (in the mainWindow, in the serialportconfigdlg, or in the run() routine of the worker thread ? Or in all of the above?
Same for some of the typedef/struct Settings = { ... } for the settings code. I seem to always end up defining and redefining things multiple times, not very efficient.Currently I have something that contains:
- a mainWindow (QWidget, .h, .cpp, .ui)
- a serialPortConfigDlg (QDialog, .h, .cpp, .ui)
- a settingsConfigDlg (QDialog, .h, .cpp, .ui)
- no thread yet
Where would I add the QThread *thread ? And would this need to have some instance of a QSerialPort, or can I make it point to the one already declared in the serialPortConfigDlg ?
Any idea what would be the better way of doing this? Sure, I could just buy a Polargraph, but that in itself is not much fun to do and the learning effect is then fairly limited ...
Any help appreciated. O.
-
Before going further you should rather do a bit of design e.g. isolate each functionality you want:
- Pixel calculation
- Serial port communication
- Configuration (it seems this one is already covered)
Create one or more classes that are responsible for each element. It will then be easier to glue them together.
An example: you have some sort of protocol to communicate with your board ? Then encapsulate it so you can easily test it.
You need to perform action sequentially with some steps depending on input ? Like waiting for answer of the micro-controller ? The maybe a QStateMachine would be a good idea