Qt : how to automate looped slider movement in the background? - c++

What's the most straightforward approach to have a slider move in a loop at a given speed when a button is pressed? I'm guessing it involves forking a thread that sends the appropriate signals periodically to the slider. Is there an canonical approach example for doing this?

I would suggest to use QPropertyAnimation to do the job. just set the start value, the end value and the curve you want the value to change
QPropertyAnimation *animation = new QPropertyAnimation(slider,"sliderPosition");
//set the duration (how long the animation should run - will change value faster when shorter)
animation->setDuration(1000);
//set the start value - in this case check if value in range of the slider
animation->setStartValue(slider->minimum());
//same as start value
animation->setEndValue(slider->maximum());
//easingCurve defines if it goes straight or bouncing n stuff
animation->setEasingCurve(QEasingCurve::OutCubic);
// as coyote mentioned, you can loop the animation as well (credit him as well ;))
// -1 defines to run forever
animation->setLoopCount(loopCount)
animation->start();

This would just be a matter of updating the slider's position on a timer. So, create a timer and on each update, call QSlider::setValue.
When the value is at maximum, set it back to the minimum and continue.
QSlider* pSlider = new QSlider;
QButton * pButton = new QButton("Go");
QTimer* pTimer = nullptr; // C++ 11 nullptr
// On button click, start a timer
connect(pButton, &QButton::clicked(), [=](){
// exit if already running
if(pTimer)
return;
pTimer = new QTimer;
connect(pTimer, &QTimer::timeout, [=](){
if(pSlider->value()+1 > pSlider->maximum())
pSlider->setValue(pSlider->minimum());
else
pSlider->setValue(++pSlider->value());
});
pTimer->start(1000); // update every second
});

You can use QTimer. Minimal example:
QSlider *sl = new QSlider;
QTimer *ttt = new QTimer;
sl->setValue(0);
connect(ttt,&QTimer::timeout,[=]() {
sl->setValue(sl->value() + 5);
});
sl->show();
ttt->start(500);
I used here C++11 (CONFIG += c++11 to .pro file) and new syntax of signals and slots, but of course you can use old syntax if you want.

There's no canonical approach to my knowledge.
The timers are given in the other answers, but you can also use the animation framework, and adjust the speed by adjusting the duration of the animation.
You can set the loopcount to how many times you want the animation to run, for example 1000000 to make it run a long time.

Related

QPropertyAnimation modifying interval

I'm using a QPropertyAnimation object to move some controls in the following way:
QPropertyAnimation* animation = new QPropertyAnimation(message_, "pos");
animation->setDuration(2000);
animation->setStartValue(current_pos);
animation->setEndValue(new_pos);
animation->setEasingCurve(QEasingCurve::Linear);
animation->start(QAbstractAnimation::DeleteWhenStopped);
asl::checkedConnect(animation, SIGNAL(finished()), this, SLOT(slotScrollingFinished()));
The object works AFAIK by executing every few milliseconds and modifying the value of the pos property until the duration expires and the end value is reached.
I was wondering if there is a way to modify the internal QPropertyAnimation object's interval timer to change the amount of times it will access the pos property within this 2 second duration.
I can't see anything in the docs.
You can do this:
#include <qt/src/corelib/animation/qabstractanimation_p.h>
QUnifiedTimer::instance()->setTimingInterval(new_interval);

Make a QLabel blink

I'm using Qt to make a gui application for my beagleboard.
I'm trying to make a QLabel blink with a custom image.
QTimer::connect(timer, SIGNAL(timeout()), this, SLOT(blink()));
timer->start(1000);
I'm thinking to use QTimer to call the blink() function every second, but I do not have a clue what the code should be in blink() function. I hope someone can help, since i have struggle with this problem in a while now.
Create blinking animation in a GIF file.
Create whatever animation you like with tools like GIF Animator.
Show it like below:
auto movie = new QMovie("myblinking.gif");
QLabel blinklabel = new QLabel();
blinklabel->setMovie(movie);
movie->start();
The easiest way is to hide and show it again.
Class::blink()
{
if(label->isHidden())
label->show();
else
label->hide();
}
This approach is good because you don't need to set your image again and again, just show/hide it(set empty pixmap or set image every second is not efficient approach).
If you use layout, then it can really break your layout, so you can use QStackedWidget with imageLabel and empty label and change it every second. I think that it will be still better than set empty pixmap or set image every second. Choose the best for you.
http://qt-project.org/doc/qt-4.8/qstackedwidget.html
Using a bool member isQLabelVisible in your class
Class::blink() {
if(isQLabelVisible) {
doHideQLabel();
isQLabelVisible = false;
} else {
doShowQLabel();
isQLabelVisible = true;
}
}
void Class::doHideQLabel() {
[...]
}
void Class::doShowQLabel() {
[...]
}
You have a starting point for a good solution. Now, to implements do{Hide,Show}QLabel(), read answers from this question and decide which one is the best for your needs.

Using QTimer for displaying timer

I created a class called aTimer which inherits from QTimer. I want to be able to store the time elapsed into a variable called TimeElapsed of type int. I then want to have the timer automatically started when the main window opens and for it to display the time elapsed therein.
I think I'm using the wrong timer, and I am quite confused as to which tools to be using within Qt because there are different ways of handling time. Suffice it to say that I want a stop-watch kind of module that allows me to start and stop time manually without a cap (or an interval with the case of Timer). How shall I proceed? So far, attempts to use QTimer are fruitless.
You do not really need a derived class for this task. I'd probably use a QTimer and a QElapsedTimer.
Create them in your main window constructor and set the QTimers interval according to how often the time should be updated. Also connect its timeout() signal to a function updating the displayed value. In this function you can get the elapsed time from the QElapsedTimer and update the display.
// *.h
QTimer* timer;
QElapsedTimer *eltimer;
// *.cpp
constructor(){
this->timer = new QTimer(this);
this->timer->setInterval(1000);
connect(this->timer, SIGNAL(timeout()), this, SLOT(update_ui()));
this->timer->start();
this->eltimer = new QElapsedTimer(this);
this->eltimer->start();
}
SLOT update_ui(){
qint64 msecs_elapsed = this->eltimer->elapsed();
// Insert value into ui object
}
Of course you can create some buttons to start() and stop() the QTimer

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)!

How do I progressively load a widget in QT?

I have a custom widget which displays many items in rows:
void update(){ //this is a SLOT which is connected to a button click
QVBoxLayout *layout = this->layout();
if (layout == NULL){
layout = new QVBoxLayout;
this->setLayout(layout);
} else {
QLayout_clear(layout); //this is a function that I wrote that deletes all of the items from a layout
}
ArrayList *results = generateData(); //this generates the data that I load from
for (int i = 0; i < results->count; i++){
layout->addWidget(new subWidget(results->array[i]));
}
}
The problem is that there are about 900 items and a profile reveals that simply adding the child object to the layout takes 50% of the time (constructing takes the other 50%). Overall it takes about 3 seconds to load all of the items.
When I click on the button to load more data, the entire UI freezes for the 3 seconds and then all of the items appear together when everything is done. Is there a way to progressively load more items as they are being created?
The first trick is, as Pavel Zdenek said, to process only some of the results. You want to process as many together so that the overhead (of what we're going to do in the next step) is low, but you don't want to do anything that would make the system seem unresponsive. Based on extensive research, Jakob Nielsen says that "0.1 seconds is about the limit for having the user feel that the system is reacting instantaneously", so as a rough estimate you should cut your work into roughly 0.05 second chunks (leaving another 0.05 seconds for the system to actually react to the user's interactions).
The second trick is to use a QTimer with a timeout of 0. As the QTimer documentation says:
As a special case, a QTimer with a timeout of 0 will time out as soon
as all the events in the window system's event queue have been
processed. This can be used to do heavy work while providing a snappy
user interface.
So that means that a timer with a timeout of 0 will be executed next, unless there is something else in the event queue (for instance, a mouse click). Here's the code:
void update() {
i = 0; // warning, this is causes a bug, see below
updateChunk();
}
void updateChunk() {
const int CHUNK_THRESHOLD = /* the number of things you can do before the user notices that you're doing something */;
for (; i < results->count() && i < CHUNK_THRESHOLD; i++) {
// add widget
}
// If there's more work to do, put it in the event queue.
if (i < results->count()) {
// This isn't true recursion, because this method will return before
// it is called again.
QTimer::singleShot(0, this, SLOT(updateChunk()));
}
}
Finally, test this a little bit because there's a gotcha: now the user can interact with the system in the "middle" of your loop. For instance, the user can click the update button while you're still processing results (which in the above example means that you would reset the index to 0 and reprocess the first elements of the array). So a more robust solution would be to use a list instead of an array and pop each element off the front of the list as you process it. Then whatever adds results would just append to the list.
#Adri is generally right, the twist is that the "another thread" must be the UI thread again. The point is to allow UI thread's event loop to keep spinning. The fast and dirty way is to put QCoreApplication::processEvents() in your for() cycle. Dirty because, as the doc says, it should be called "ocassionally". It might have some overhead even if there are no UI events, and you are messing Qt's performance optimization as to when and how often spin the loop. Slightly less dirty would be to call it only ocassionally, after chunks of result.
Cleaner and proper way is to create a private slot, which pops one result element (or chunk, to speed up), adds to the layout and increments index. Then it will recall itself until end of results. The gotcha is to define connect() with forced connection type Qt::QueuedConnection, so it will get deferred after already queued UI events (if any).
And because you run in only one thread, you don't need any locking over results.
Adding example per OP's request:
While #TomPanning solution is correct, it kind of hides the real solution behind QTimer which you don't need - you don't need any timing, you just need a specific non-timer behavior upon specific parameter value. This solution does the same thing, minus the QTimer layer. On the other hand, #TomPanning has a very good point about the plain ArrayList not being very good data storage, when interaction can happen in between.
something.h
signals: void subWidgetAdded();
private slots: void addNextWidget();
ArrayList* m_results;
int m_indexPriv;
something.cpp
connect(this,SIGNAL(subWidgetAdded()),
this,SLOT(addNextWidget(),
Qt::QueuedConnection);
void addWidget() {
// additional chunking logic here as you need
layout->addWidget(new subWidget(results->array[m_indexPriv++]));
if( m_indexPriv < results->count() ) {
emit subWidgetAdded(); // NOT a recursion :-)
}
}
void update() {
// ...
m_results = generateData();
m_indexPriv = 0;
addNextWidget(); // slots are normal instance methods, call for the first time
}