Qt: display window content on slow startup - c++

It feels like this question has been asked about a hundred times before (e.g. here) but I haven't found a working solution yet..
I have a Qt5 program (Linux) which takes some time (about 2sec) for initialization. I don't want to spawn a thread (for several reasons) and before initialization is done the program is not usable anyway.
Currently the program starts and it shows a black window, until initialization is done.
I'd like to have the window content be drawn as soon as possible and queue a method which does the rest which gets executed right after the main window has been drawn.
This is what I tried:
class my_window : public QMainWindow {
Q_OBJECT
explicit my_window(QWidget *parent = 0) : QMainWindow(parent) {
initializeUI();
/// UI is ready and should be drawn. initializeRest() should
/// be queued
/// tried to repaint() or update() the MainWindow and to 'force'
/// processing outstanding events - with no effect
update();
repaint();
QApplication::processEvents();
/// don't call - just queue
QMetaObject::invokeMethod(this, "initializeRest", Qt::QueuedConnection);
}
void initializeRest() {
// do stuff which takes a while
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
But the main window stayes black until initializeRest() has been executed.
How can I tell Qt to execute initializeRest() right after the window has been constructed?
I can think of starting a timer (bad, introduces extra latency) or an event handler which reacts on some kind of "WindowDrawn" event (bad, complicated).
What's the Qt-way to do this?
Update:
I've also tried to put the initializeRest() method into the main() function like suggested by Murphy:
my_window::my_window(QWidget *parent = 0) : QMainWindow(parent) {
initializeUI();
}
int main(int a_argsc, char *a_argsv[]) {
QApplication l_application(a_argsc, a_argsv);
my_window mainWindow;
mainWindow.show();
QApplication::processEvents();
mainWindow.initializeRest();
return l_application.exec();
}
With same results: Waiting for a couple of seconds inside initializeRest() makes show up the initially black main window and be drawn right after initializeRest() returned (which seems to be logical to me because the event loop has not been started yet..)

Note: This suggestion doesn't solve the issue; it's left here for completeness.
You can split the startup into smaller steps in main():
Create the QApplication instance.
Instantiate the main window (I'll call the variable mainWindow here). You can safely remove all that repaint-workaround stuff after initializeUI(); from the constructor of your code example.
Call mainWindow.show() to enforce showing the main window, followed by a call to QApplication::processEvents() to enforce the paint events being handled.
Do all the other initialization stuff of your application.
Start the event loop as usual by calling QApplication::exec().
Be aware that with complex applications/main window implementations it can get quite hairy to do everything in the right order; a QSplashScreen would surely be the less tedious solution.

I have the same problem. I think the problem is based not on show() function. Try to run the next code.
If add a button to boxLayout the application starts fast. But if I try setGeometry() the application takes a long time to start.

Related

Animated gif image isn't being animated in my modeless Gtk::Dialog

My goal is to show a brief "Please Wait..." dialog with an animated gif (spinner) in a Gtk::Dialog.
My problem is that when I do not use Gtk:Dialog::run(), the gif won't be animated, and when I do use the Gtk:Dialog::run() method it completely blocks my running code afterwards. And since I don't have any buttons in my dialog it would hang there indefinitely. Is there a way around that? I have had no success in getting the animated gif to work in a non-modal dialog, i.e without using the run() method.
I'm using gtkmm 3.0
Compile with : g++ examplewindow.cc main.cc -o main `pkg-config gtkmm-3.0 --cflags --libs`
main.cc
#include "examplewindow.h"
#include <gtkmm/application.h>
#include <iostream>
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create("org.gtkmm.example");
ExampleWindow window;
//Shows the window and returns when it is closed.
//return app->make_window_and_run<ExampleWindow>(argc, argv);
return app->run(window);
}
examplewindow.h
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H
#include <gtkmm.h>
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow();
virtual ~ExampleWindow();
protected:
//Signal handlers:
void on_button_clicked();
//Child widgets:
Gtk::Box m_VBox;
Gtk::Box m_ButtonBox;
Gtk::Button m_Button;
};
#endif //GTKMM_EXAMPLEWINDOW_H
examplewindow.cc
#include "examplewindow.h"
#include <iostream>
ExampleWindow::ExampleWindow()
: m_VBox(Gtk::Orientation::ORIENTATION_VERTICAL),
m_ButtonBox(Gtk::Orientation::ORIENTATION_VERTICAL),
m_Button("Show Dialog")
{
set_title("Test animated gif");
set_default_size(800, 600);
add(m_VBox);
m_VBox.pack_start(m_ButtonBox);
m_ButtonBox.pack_start(m_Button);
m_Button.set_hexpand(true);
m_Button.set_halign(Gtk::Align::ALIGN_CENTER);
m_Button.set_valign(Gtk::Align::ALIGN_CENTER);
m_Button.grab_focus();
m_Button.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_button_clicked));
show_all_children();
}
ExampleWindow::~ExampleWindow()
{
}
void ExampleWindow::on_button_clicked()
{
Gtk::Dialog m_Dialog;
m_Dialog.set_transient_for(*this);
m_Dialog.set_size_request(200, 200);
m_Dialog.set_decorated(false);
Gtk::Image imageLoading = Gtk::Image();
imageLoading.property_pixbuf_animation() = Gdk::PixbufAnimation::create_from_file("gtkmm_logo.gif");
m_Dialog.get_vbox()->pack_start(imageLoading);
m_Dialog.show_all();
m_Dialog.run();
/******** This, below, never gets executed as run() is blocking the program...********/
// Dummy "long" operation
for (int i = 0; i <= 2010101010; i++)
{
if (i == 2010101010)
std::cout << "Done" << std::endl;
}
m_Dialog.response(Gtk::RESPONSE_ACCEPT);
m_Dialog.hide();
}
Let us look at the original problem. You created a dialog, called show() on it, did some long-running process, then closed the dialog. The process worked, but your program froze during the processing. Why is that?
A graphical interface works by processing messages (events). Some events run off a timer, such as the ones that tell an animation to go to the next frame. Some are generated as needed, such as the ones that tell an image to draw the current frame. These events need to be both triggered and processed to be effective. You triggered the appropriate events with your call to show_all(), but you did not give your program a chance to handle those events.
You used a button click to start your long-running process. That click is an event that was handled by your main event handling loop. That loop then waited for the click to be fully handled before moving on to the next event. However, you have your long-running process in the handler. The main event loop had to wait for that process to finish before it could handle new events, such as the ones to show and animate your image. You never gave your dialog a chance to do its job before you destroyed it.
Calling the dialog's run() method partially fixed the situation by starting a new event loop for the dialog. So even though the main event loop was still blocked by your click handler, new events could be handled. The dialog's event loop received the events required to show an animation, hence your program was again responsive. Unfortunately, run() blocked your long-running process, so we're not really any better off.
The simplest fix is to no longer completely block your main event loop. You could have your long-running process periodically allow events to be processed via Gtk::Main::iteration(). This function invokes an iteration of the main event loop, allowing your program to stay responsive. Pass it a false argument so that it only processes events if there are some to process (rather than waiting for an event to occur).
for (unsigned long i = 0; i <= 2010101010; i++)
{
if (i == 2010101010)
std::cout << "Done" << std::endl;
// Periodically process events
if ( i % 10000 == 0 ) // <---- after some suitable amount of work
if ( !Gtk::Main::iteration(false) ) // <---- allow events to be processed
// Abort the work.
break;
}
The return value is supposed to tell you if you should quit or not, but I didn't get this working in my test (and the return value seemed to have the opposite meaning compared to the documentation). Maybe the dialog itself was keeping the app alive? Eh, that can be the next question, once this part is working.
Other approaches would move your long-running process out of the click handler. If you let the click handler end quickly, the main event loop can do its job without the extra prompting from you. However, this requires a few adjustments so that the Gtk::Dialog outlives the call to on_button_clicked(). That's a bit of refactoring, but it might be worth the time. I'll present two options (without code).
You could have your work operate on multiple timeout signals. Divide your long-running process into smaller chunks, each chunk suitably sized for a callback. (How big is that? Not sure. For now, let's say at most a few milliseconds.) Have the button click event start the first timeout signal with a priority that allows the GUI to update. (As I recall, PRIORITY_DEFAULT_IDLE should work.) For the interval, I would try 0 if that does not overly confuse Gtk+. (I have not tried it, but it seems plausible.) If the 0-interval works, it might be wise to use connect_once() instead of connect(), and have each chunk schedule the next with another timeout. The final chunk would be responsible for closing the dialog.
You could move your long-running process to another thread. Multi-threaded programming has its own set of problems and sometimes a lot of setup, but this is something it is well-suited for. If your long-running process is in a different thread than your main event loop, the operating system becomes responsible for making sure each thread gets some CPU time. Your long-running process can chug away, and the main event loop would simultaneously be able to process events with no special intervention from you.
Final notes:
If your dialog is for one-way communication to the user, it seems more like a monologue than a dialogue. Excuse me, more like an ordinary window than a dialog. Also, I'll make sure you are aware of Gtk::ProgressBar, which "is typically used to display the progress of a long running operation." Just an option; preferring your image is understandable.

QPropertyAnimation not running immediately when QAbstractAnimation::start() is called

I am trying to make a simple multi-page widget with QStackedWidgets in Qt Creator with Qt 4.8.6. The goal is to have a QLabel and QProgressBar fade away after the progress bar reaches full value. I thought the best way to achieve this was using QGraphicsOpacityEffect and QPropertyAnimation in tandem as seen below.
void MainWindow::AnimateStartScreen()
{
//QSound::play("./Audio/THX-DeepNote-48Khz.wav");
sleep(5);
logoEffect = new QGraphicsOpacityEffect();
logoAnimation = new QPropertyAnimation(logoEffect, "opacity");
ui->startLogo->setGraphicsEffect(logoEffect);
logoAnimation->setDuration(2000);
logoAnimation->setStartValue(1);
logoAnimation->setEndValue(0);
logoAnimation->start();
progressBarEffect = new QGraphicsOpacityEffect();
progressBarAnimation = new QPropertyAnimation(progressBarEffect, "opacity");
ui->startProgressBar->setGraphicsEffect(progressBarEffect);
progressBarAnimation->setDuration(2000);
progressBarAnimation->setStartValue(1);
progressBarAnimation->setEndValue(0);
progressBarAnimation->start();
sleep(3);
}
Which is then called by the main method:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.HideTitleBar();
w.CenterWidget();
//QMainWindow::showFullScreen();
w.show();
w.IncrementProgressbar();
w.AnimateStartScreen();
w.ChangePageTo(1);
return a.exec();
}
Here lies the problem. I want this animation to finish before I change the page like so:
void MainWindow::ChangePageTo(int page)
{
ui->stackedWidget->setCurrentIndex(page);
}
Is there a way I can delay page change until my animation is complete? When I comment out the page change, the animation doesn't seem to even start until QApplication::exec() is called in my main method, evident by a 8 second waiting period (the combination of both sleep calls), rather than a 5 second delay before QAbstractAnimation::start() is called.
I believe what is happening is that the sleep(3) line is stopping the thread in which the GUI is running on, hence delaying the start of the animation for three seconds. When the sleep call has completed, I wonder if the animation tries to continue, but because of the page change,there is just no visible effect from the quick transition.
How can I make a 3 second delay without stopping the thread that the animation is running off of? Or are there any other suggestions that will produce the same effect?
I believe what is happening is that the sleep(3) line is stopping the thread in which the GUI is running on
Almost certainly. For the animation to run you need to keep the message queue running which, in the code you've posted, won't occur until the subsequent call to a.exec().
If you really need to block execution of MainWindow::AnimateStartScreen until the animation has completed then perhaps a better idea would be to replace the call to sleep(3) with...
while (progressBarAnimation->state() != QAbstractAnimation::Stopped)
QCoreApplication::processEvents();
(Although you might have to play about with the flags pass to QCoreApplication::processEvents to get the precise behaviour you require.)
Use next pattern for non-blocking waiting (instead of sleep):
QEventLoop loop;
connect( progressBarAnimation, &QAbstractAnimaion::finished, &loop, &QEventLoop::quit );
loop.exec();

Qt - GUI freezing

I wrote in C++ a solver for the 8-puzzle game, and now I'm trying to use Qt to give it a GUI.
Basically I have an underlying object of type "Board" which represents the board of the puzzle, and I have organized the GUI as a grid of QPushButton. Then I have a method updateUI which associates to every button the correct text, based on the Board. Something like
for(int i=0; i<Board::MATRIX_DIM * Board::MATRIX_DIM; i++)
{
m_buttons[i]->setText(m_values[i]);
}
In another method (solveGUI) I have
void MainWindow::solveGUI()
{
m_game->solve();
int solutionDepth = m_game->getSolutionDepth();
Move *solutionMoves = m_game->getSolutionMoves();
for(int i=0; i<solutionDepth; i++)
{
Move m = solutionMoves[i];
m_board.performMove(m); /* perform the move on the Board object */
updateUI(); /* should update the GUI so that it represents the Board */
Sleep(1000);
}
}
where the first line (m_game->solve) takes some time. Then I obtain a list of the moves performed, in solutionMoves, and what I would like to do is showing this moves on the board, with some delay between a move and the next one. This method is called by my main, which looks like this:
QApplication app(argc, argv);
MainWindow w;
w.show();
w.solveGUI();
return app.exec();
The result is that the GUI hangs and, after some time, it displays only the solution, completely skipping the moves.
What am I missing? Thank you!
P.S. I don't think I need a different Thread for the solver because I want the solver to run before the solution is displayed. Is it right?
It's app.exec() that actually runs the main loop which handles all events, including displaying GUI. If you want to call solve() before that, it's OK, but if you want to actually display and update GUI before exec(), it's wrong. I'm not sure if it's totally impossible, but it's definitely not the right way to do it.
There are two ways around it. The more canonical way is to redesign a program using a QTimer. Then everything will be smooth and responsive. But that can be tedious sometimes. In your case it should be quite easy, though. Just save the results somewhere, and call a slot using a QTimer object every 1000 seconds - it will have the same effect as your Sleep(), but will keep everything responsive.
The other solution is to call your solveGUI() method after exec() starts its job. It can be done, for example, using QTimer::singleShot():
QTimer::singleShot(0, &w, SLOT(showGUI()));
return app.exec();
Then, before each Sleep(), you should call QApplication::processEvents(), which basically allows you to temporary yield control, processing all pending events, including GUI updates. This approach is somewhat easier, but it's inferior since the GUI still freezes at each Sleep(). For example, if the user wants to exit the application, or if the window is needed to be repainted, it will cause uncomfortable GUI lags.
You're stalling the main thread (which also does the event processing) and rendering it uncapable of responding to keyboard/mouse/window messages.
You should use an asynchronous timer operation instead of the sleep function: use a QTimer to delay showing the next solution and avoid messages being left unanswered for too long.
There is a nice article of methods to keep the GUI responsive during processing loops. if it's not a complicated case I think, just insert QCoreApplication::processEvents(); inside the long processing loops.
try the following:
void MainWindow::Wait(int interval ) {
QTime timer = new QTime;
timer.restart();
while(timer.elapsed() < interval) {
QApplication::processEvents();
}
}
...
for(...) {
//wait 1 second (1000 milliseconds) between each loop run at first
Wait(1000);
...
}
...
not tested yet - but should work (maybe there is some cpu load)!

Qt/C++: Checkable button and infinite loop

I'm coding a Qt Gui and I'm trying to implement a new feature, I now the precise result I want to arrive at but not how to code it.
I'm trying to add a checkable button that when checked would run a function that would only stop when the button is unchecked, but every second a PaintArea I have on the window would be updated (letting me see how the multiple executions of my function are changing my data). It seem that I'll need to use some QThread objects, but just the part dealing with the button is already counter intuitive to me, I've been trying to play with the autoRepeatDelay and autoRepeatInterval without getting my hand on what they do and how they could be useful to me.
I guess that what I'm trying to code is not really original, would have an idea of the steps to implement it, or an example of a code?
Edit:
According to the first answers (thank you for them by the way) my question may not be clear. Putting on the side the thread thing, I'd like to implement an infinite loop that only starts when a pressbutton goes to pressed position (it's a checkable button) and stops only when leaving it. The first version I tried to do (with a while(button->isChecked() loop) would completely freeze as the application would be running the loop, the gui would freeze and the button couldn't be turned off (hence the idea of running it in a separate thread). Voila! I hope it's a clearer formulation. Thank you in advance.
Here's a simple skeleton of something that might work. Without knowing your exact requirements, it may or may not be right for your problem. Hopefully it will give you a few hints that do actually help.
void Ui::buttonPressedSlot(bool checked){
if (checked){
Processor *processor = new Processor;
connect(this, SIGNAL(abortCalculations()), processor, SLOT(abort()), Qt::QueuedConnection);
connect(processor, SIGNAL(updateNeeded()), this, SLOT(updateGui()), Qt::QueuedConnection);
QThreadPool::globalInstance()->start(processor);
} else {
emit abortCalculations(); // this is a signal in your UI class
}
}
You can then use the following for your calculations.
class Processor : public QObject, public QRunnable{ // QObject must always be first in multiple inheritance
Q_OBJECT
public:
~Processor();
void run();
public slots:
void abort();
void doCalculations();
signals:
void updateNeeded(); // connect this to the GUI to tell it to refresh
private:
QScopedPointer<QEventLoop> loop;
};
Processor::~Processor(){
abort();
}
void Processor::run() {
loop.reset(new QEventLoop);
QTimer timer;
connect(&timer, SIGNAL(timeout()), this, SLOT(doCalculations()));
timer.setInterval(1000);
timer.start();
loop->exec();
}
void Processor::abort(){
if (!loop.isNull()){
loop->quit();
}
}
void Processor::doCalculations(){
// do whatever needs to be done
emit updateNeeded();
}
I don't know if I really understand what you want to do, but I will try to answer.
First, you want a Button that send a start & stop info to control a thread. You can use a checkbox to begin. This check box send a signal when its state changes. Connect this signal to a slot that perform start thread and stop according to the boolean sent.
Second, in you thread you need to launch the events loop. After, set a timer that call you repaint after every timeout.
Hope it helped.
PS: take care of execution context with you thread and Qt's objects.

How to implement a game-loop with C++ and QtQuick

I'm developing a game with QtQuick 2 (Qt5.2) QML and C++. I want most of the game-logic in C++ (I don't want to do it with JS), and I'm trying to use QStateMachines for a lot of the logic.
Now my question is, how do I implement a proper game-loop in that scenario?
The game has for example objects that are moving from and to certain waypoints on the screen, so I think I can't do everything state/event-based. At some point I need to say "my object should move 10 pixels in direction x every second". So for example when my object is in its state "moving", it should move by a certain amount every second and then of course check for some conditions if it has to switch the state (e.g. to "attacking").
Now all the demos in the Qt-examples and on the web seem to be either fully event-based (e.g. four-in-a-row-wins-like) or written in JavaScript. So I am a bit lost here.
One idea I could find was to create a QTimer with a timer of e.g. 30ms and connect that QTimer's timeout() signal to an advance() slot of every moving object, and start that timer before 'return app.exec();'. Like this:
QTimer timer;
QObject::connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance()));
timer.start(1000 / 33);
return app.exec();
and then each object (e.g. the Mouse) has a
void Mouse::advance(int step)
However, this requires a QGraphicsScene and I'm not sure how well that goes with a QtQuick/QML project on Android/iOS.
Is that a good solution? Or is my view of the problem somehow wrong and I don't need a game loop to accomplish my goal?
The solution shouldn't use any desktop-only stuff from Qt, i.e. it should work on Android, iOS and desktops.
That's the way to go: QTimer. Here you find some detailed example on it:
A typical loop for game in Qt:
int main(int argc, char* argv[]) {
// init stuff
while(game.isRunning()) {
a.processEvents(); //(a is a QApplication created during the init, should use a better name i guess)
QTime currentTime= QTime::currentTime();
int timeSinceLastUpdate = lastUpdate.msecsTo(currentTime);
while(timeSinceLastUpdate>updateTimeStep){
game.update();
timeSinceLastUpdate-=updateTimeStep;
lastUpdate=lastUpdateaddMSecs(updateTimeStep);
}
renderer.setInterpolateFraction(static_cast<float>(timeSinceLastUpdate)/static_cast<float>updateTimeStep);
renderer.renderGameObjects();
renderer.renderGUI();
renderer.swap();
}
a.exit();
return 0;
}
Source: Game loop in Qt
Making a Simple Game Loop with QTimer
Qt as a game engine
That's should be enough info for you to get started.
Usual game loop of simple game can look like this (not sure if I understand you correctly though).
Each class that represents game object have 2 public methods: update(); and render();
On each call of QTimer object you iterate over all game objects and call their update method. After it you repeat the same for render method();
In update methods each object decides what to do on game map (move/shot/stand/...) and changes its coordinates/properties. In render methods each object just draws itself on display.