PDA

View Full Version : Refresh application GUI after running external application



evanol
6th July 2015, 10:54
Hello,

I'm looking for some advice on how to refresh/redraw my main application after running an external application.

Currently I'm doing the following:



QProcess systemCall;
systemCall.start( externalAppPath, externalAppArgs );
systemCall.waitForFinished( -1 ); //wait indefinitely for process to finish
systemCall.close();

QWidget *qwParent = this;
while( (qwParent) )
{
qDebug() << "repainting parent :" << qwParent;
qwParent->repaint();
qwParent = qwParent->parentWidget();
qDebug() << "next parent :" << qwParent;
}


As one of the options in my main application I have to launch our external application and wait for it to finish.
On exiting the external application and returning to my main app I need to repaint all parent widgets using the above while loop.

My question is - is there a better way of achieving this?

anda_skoa
6th July 2015, 11:28
Well, it is usually very bad to block event processing of a UI application.
Do you have a specific requirement for that?

Cheers,
_

yeye_olive
6th July 2015, 11:44
Why do you think you need to explicitly redraw all widgets? Qt handles this as long as you let control return to the event loop. Your current code, which blocks the UI thread until the external process has finished executing, is inelegant because it prevents the user from interacting with your application, and the widgets are not updated. However, if you return to the event loop after the external process has finished executing, the widgets should be updated automatically.

Now, if you want the UI to remain responsive while the external process is running, you need an event loop to be running. Instead of calling QProcess::waitForFinished(), you could connect QProcess::finished() to a slot, then start the process and return to the event loop. Your slot will be executed after the external process has finished executing.

evanol
6th July 2015, 12:27
Well, it is usually very bad to block event processing of a UI application.
Do you have a specific requirement for that?

Cheers,
_


Why do you think you need to explicitly redraw all widgets? Qt handles this as long as you let control return to the event loop. Your current code, which blocks the UI thread until the external process has finished executing, is inelegant because it prevents the user from interacting with your application, and the widgets are not updated. However, if you return to the event loop after the external process has finished executing, the widgets should be updated automatically.

Now, if you want the UI to remain responsive while the external process is running, you need an event loop to be running. Instead of calling QProcess::waitForFinished(), you could connect QProcess::finished() to a slot, then start the process and return to the event loop. Your slot will be executed after the external process has finished executing.

See attached image when I launch my external application without blocking the main app. I get the following:
11255
alternative link to image (https://www.dropbox.com/s/ffm2lc2192pavdq/IMG_20150706_111143.jpg?dl=0)
With the above code I am trying to prevent this occurring.

Also, when using the external application, the user should not be interacting with the main application so I felt that waiting for the external application to finish was the best option.

I am open to any/all suggestions on how to achieve this in a better way.
Thanks

anda_skoa
6th July 2015, 12:54
As yeye_olive wrote, you don't need any special widget updating, just return from the slot.

I would still not artificially freeze the application.

The option that is closest to your current code is to use a QEventLoop instance and connect the process' finished() signal to the lop's quit() slot.
Then you run exec() with the flag to exclude user input.

Additionally, as a visual clue for the user, I would disable the UI before running the nested loop and re-enable it after exec() returns.

Another option is to use a modal dialog.

Cheers,
_

yeye_olive
6th July 2015, 12:54
I cannot download the attachment in your last post. I get an error 'Invalid Attachment specified' when I try to do so.

Globally preventing user interaction is frowned upon, because you do not even let the user close the application. If you want to prevent interaction with specific widgets, call QWidget::setEnabled(false).

Nevertheless, if you insist on preventing user interaction, you could still run a local QEventLoop with the QEventLoop::ExcludeUserInputEvents flag.

evanol
6th July 2015, 13:15
As yeye_olive wrote, you don't need any special widget updating, just return from the slot.

I would still not artificially freeze the application.

The option that is closest to your current code is to use a QEventLoop instance and connect the process' finished() signal to the lop's quit() slot.
Then you run exec() with the flag to exclude user input.

Additionally, as a visual clue for the user, I would disable the UI before running the nested loop and re-enable it after exec() returns.

Another option is to use a modal dialog.

Cheers,
_


I cannot download the attachment in your last post. I get an error 'Invalid Attachment specified' when I try to do so.

Globally preventing user interaction is frowned upon, because you do not even let the user close the application. If you want to prevent interaction with specific widgets, call QWidget::setEnabled(false).

Nevertheless, if you insist on preventing user interaction, you could still run a local QEventLoop with the QEventLoop::ExcludeUserInputEvents flag.

@ yeye_olive - i've added an alternate link to the image

Thank you both for your feedback.
The main application is running on an embedded linux device and as such the user can never close the main application window.
The device turns-on & launches the main application.
The application (& device) is then shutdown when a user activity timeout occurs or a hardware button is pressed.

I will look into both of your suggestions - any code samples / examples / links that may be helpful would be appreciated

evanol
6th July 2015, 17:01
@ anda_skoa - i've taken the idea of using QDialog to accomplish this, which negates the need to manually update parent widgets.

Any comments/feedback welcome...



#include "externalAppdialog.h"
#include "ui_externalAppdialog.h"

externalAppDialog::externalAppDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::externalAppDialog)
{
ui->setupUi(this);
this->setWindowFlags( Qt::FramelessWindowHint );

connect( this, SIGNAL( finished( int ) ), this, SLOT( deleteLater() ) );
this->showFullScreen();

activityTicker = new QTimer( this );
connect( activityTicker, SIGNAL( timeout() ), this, SLOT( keepMainAppAlive() ) );
activityTicker->setInterval( (30 * 1000) ); //every 30s;

systemCall = new QProcess( this );
connect( systemCall, SIGNAL( finished(int) ), this, SLOT( accept() ) );
connect( systemCall, SIGNAL( finished(int) ), activityTicker, SLOT( stop() ) );

QTimer::singleShot( 0, this, SLOT( launchexternalAppApp() ) );
}

externalAppDialog::~externalAppDialog()
{
delete ui;
}

void externalAppDialog::launchexternalAppApp( void )
{
systemCall->start( QString( "%1/%2" )
.arg( "externalAppPath" )
.arg( "externalApp" ),
QStringList()
<< "-qws" );
activityTicker->start();
}

void externalAppDialog::keepMainAppAlive( void )
{
qApp->postEvent( this, new QEvent( QEvent::MouseMove ) );
}

anda_skoa
6th July 2015, 17:57
Why post a fake mouse event to the dialog?

You can also replace the connect in line 11 with setting the Qt::WA_DeleteOnClose attribute.

Cheers,
_

evanol
7th July 2015, 09:29
Why post a fake mouse event to the dialog?
The main application has a user inactivity timer whereby the device (& application) is shut-down after a period of time. While running the external application, this inactivity timer needs to be serviced(restarted) to prevent this occurring - is there an alternate way of achieving this?


You can also replace the connect in line 11 with setting the Qt::WA_DeleteOnClose attribute.
Thanks, i've adjusted my code to use the Qt::WA_DeleteOnClose attribute.
Is there an advantage to using the attribute vs the connect I used previously?

anda_skoa
7th July 2015, 10:12
The main application has a user inactivity timer whereby the device (& application) is shut-down after a period of time.

Ah



While running the external application, this inactivity timer needs to be serviced(restarted) to prevent this occurring - is there an alternate way of achieving this?

You could just emit a signal from the dialog and connect it to whatever slot triggers the reset of your inactivity watchdog.



Thanks, i've adjusted my code to use the Qt::WA_DeleteOnClose attribute.
Is there an advantage to using the attribute vs the connect I used previously?

They are both pretty equivalent. The attribute has the advantage that it always triggers, no matter if the finished() signal is being emitted or not.
E.g. if the dialog is exited via a code path that does not include the respective emit or when the widget is not a dialog (i.e. works for all kinds of windows)

Cheers,
_