Unsolved QPushButton staying pressed too long
-
Hello,
I have a QPushButton which when pressed, performs quite a bit of calculations and operations. The problem I am having now is that when the QPushButton is pressed, it stays in "pressed" mode for too long. There is about a 1 to 2 second time when the QPushButton is in its "pressed" style.
Prior to this problem happening, I had a QDialog that would prompt the user "Are you sure you wish to click this button?". When I had this QDialog, the QPushButton would not stay "pressed" at all, it would simply flash for a split second to "pressed" as expected.
Is there anyway to get my QPushButton to revert back to "not pressed" faster? Thanks!
PROBLEM: Long operation performed outside of GUI thread
EDIT: WORKAROUND SOLUTION
@Wieland I did it!It was a workaround, no doubt about it, but it seems to work! Here is what I had to do.
- I connect GUI object to the class object which was performing the long operation. I create my own signal and slot. I call something like this in my GUI class when I instantiate the longOperationObject.
connect(longOperationObject, SIGNAL(myLongOpSignal()), this, SLOT(myGUISlot());
- BEFORE performing the long operation in the longOperationObject, I emit myLongOpSignal() which calls myGUISlot(). Then, right BEFORE the long operation, I call QCoreApplication::processEvents(). Then I perform the long operation.
emit myLongOpSignal(); QCoreApplication::processEvents(); longOpFunction(...);
- Now, the magic happens in myGUISlot. In order to make the push button look pressed and released as normal, I set the stylesheet of the button to be pressed. Then I call processEvents again. I wait any amount of time I want using std::clock_t, then I set the button stylesheet back to pressed! In this example the button stays pressed for .5 seconds.
void GUI::myGUISlot(){ button->setStyleSheet("pressed format..."); QCoreApplication::processEvents(); std::clock_t start; double duration; start = std::clock(); duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC; while(duration <= .5){ duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC; } button->setStyleSheet("normal un-pressed format..."); }
Quite the work around...but hey, it works for now. Thanks @Wieland for your help.
EDIT: WORKAROUND SOLUTION -
Hi! The "problem" here is that you're executing a long running operation on the thread that also handles the GUI (e.g. repainting of the button). The nice and clean solution in such a case is to move those long running operations to another thread. The quick and dirty solution is to call
QCoreApplication::processEvents()
from within your long running operation to decrease the time between GUI updates manually. -
hahahah wow!!! I actually thought of this before (I use processEvents in a previous section) but I never thought to apply it to this scenario. Will try right now!
@Wieland just tried it in a few places, problem I am having now is that the operations that occur after the press of the button cause the current tab of my window to change. It eventually returns to the tab it was on when the button was pressed, but by using processEvents it rapidly switches between the tabs while it is doing the operations.
Is there a place I can use processEvents to where it will only affect the "pressed" state of the QPushButton? Or, is there an easy way to switch the operations to another thread? Thanks for your time.
-
@MaxDevI In this case you need to use a separate thread, but that's really simple. You can either use concurrency features that came with C++11 or do it the Qt way, as described here: How To Really, Truly Use QThreads; The Full Explanation.
-
@Wieland I don't really find the QThread to be simple. Instead tried to do
QFuture<void> future = QtConcurrent::run(aFunction, arg1, arg2, ...);
but it gave me many errors saying "QtConcurrent::run expects X arguments - Y provided". I tried for a while to fix this but could never overcome the error,=
-
@MaxDevI Can you please show us the actual declaration of your "aFunction" and the actual line with "QFuture.." ?
-
@Wieland
I just got the Qtconcurrent to run, but it gives me Microsoft Visual C++ Runtime Library Debug Error saying
"ASSERT failure in QCoreApplication::sendEvent: Cannot send events to objects owned by a different thread..."
-
Seems like your aFunction is interacting with objects that live in the GUI thread.
-
@Wieland yes it is, can I do anything to fix this other than rewriting?
-
Might be easy to fix without rewriting it. What exactly do you do that causes the problem?
-
@Wieland There is so much going on in the function it is pretty hard to explain. It goes through many loops looking through widgets and checking their values, and updating other widgets on other tabs that correspond.
Let's say I have tab A, B, C with information on each tab. If I press the button on tab A, it will scan tab A for relative information (predetermined) to send over the tab B and C. Then it goes to tab B and C and enters the information from A, then returns to tab A.
This is why I get problems when using processEvents() because of the tab switching.
-
@MaxDevI Ok, that's a problem that actually cannot be fixed simply because one must never directly interact with GUI elements (widgets) from a thread other then the main thread (aka GUI thread) in Qt. That's simply not supported. But you still can send signals from other threads to the widgets on the GUI thread. They will be processed asynchronously by default. So, yeah, looks like you'll have to refactor your code a bit :-/
-
@Wieland Can I send a signal from the other thread to the GUI thread which will set the button to unpressed immediately after it is pressed? Seems like it would work somehow?
-
@Wieland Maybe I could set the pushbutton to unpressed before making the computations which cause the delay???
-
@Wieland I did it!
It was a workaround, no doubt about it, but it seems to work! Here is what I had to do.
- I connect GUI object to the class object which was performing the long operation. I create my own signal and slot. I call something like this in my GUI class when I instantiate the longOperationObject.
connect(longOperationObject, SIGNAL(myLongOpSignal()), this, SLOT(myGUISlot());
- BEFORE performing the long operation in the longOperationObject, I emit myLongOpSignal() which calls myGUISlot(). Then, right BEFORE the long operation, I call QCoreApplication::processEvents(). Then I perform the long operation.
emit myLongOpSignal(); QCoreApplication::processEvents(); longOpFunction(...);
- Now, the magic happens in myGUISlot. In order to make the push button look pressed and released as normal, I set the stylesheet of the button to be pressed. Then I call processEvents again. I wait any amount of time I want using std::clock_t, then I set the button stylesheet back to pressed! In this example the button stays pressed for .5 seconds.
void GUI::myGUISlot(){ button->setStyleSheet("pressed format..."); QCoreApplication::processEvents(); std::clock_t start; double duration; start = std::clock(); duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC; while(duration <= .5){ duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC; } button->setStyleSheet("normal un-pressed format..."); }
Quite the work around...but hey, it works for now. Thanks @Wieland for your help.
-
@MaxDevI The main thing is that it works ;-)