I have a PySide application that connects a number of buttons to a function call that spins off a bunch of external processes (this isn't ideal, but it is necessary).
The function call takes less than a quarter of a second to run, but it still presents a chance for the user to outrun my code, causing lag, freezing, or crashing.
This leads me to a few questions...
What exactly does it mean for a callable to be connected to a signal? Suppose button A's click signal is connected a Python function f. If A is clicked, and then clicked again before f exits, does PySide simply queue another call to f, or does it do something fancier?
One possible solution is to make f disconnect itself from the signal and then reconnect before it exits. Before I go off rewriting things to try this, does anyone know if this is at all efficient, or if there is a more elegant solution?
I'm guessing it does exactly what every other GUI toolkit does, which is try to execute that function for every button press. If you don't want that, then there are several approaches you can take:
Disable the button until the function finishes
Use a sentinel value, like self.running, and add a check to see if it is True. If so, return. Otherwise, set it to True and execute. Just remember to set it back to False at the end of the function
Do what you mentioned and disconnect the function from the signal
Those are the main ones. I'm sure there are some more esoteric solutions out there too though.
Related
I have a custom table and I have implemented a search/filtering function which goes through all the elements in the table and then hides/shows the item in the table depending upon whether that item/element matches the one we are searching for.
For example, let's say I have a text control and I type "wxwidgets" in it then my custom function will go through all the elements in the table and hide the elements that do not match this "wxwidgets" entry. This works fine and I am correctly able to hide/show the elements. But the problem is that this search blocks the main thread(gui) since I am doing this in the main thread. The table has around 1000 entries or can be more in the future. My question is how can I avoid this blocking of the main thread. I am thinking of using another worker thread that will do this searching of elements. But then I read that "no secondary threads should call gui functions". But then how can i show/hide the elements of the table from the worker thread. For example, currently in the main thread I use Show(true) or Show(false) to show/hide a particular entry from the table and all this is done while I am in a for/while loop. But if i implement this(the for loop) in a worker thread then according to the quoted advice i should not use the Show() functions from inside that worker thread. What can be done in this situation? Also, is there any other way/advice of doing this searching for elements of the table. I am thinking of starting a new detachable thread every time the user enters some text in the text field. And then delete that old thread if the user appends some more text to the text field and start a new thread which starts searching from the beginning. Is this the right solution for this?
The problem is that inside my for loop i am using the wxWidgets functions. For example, this is what my for loop looks like:
void AnotherClass::onTextChanged(wxCommandEvent &event)
{
for(int i = 5; i<154;++i)
{
SomeClass *element = dynamic_cast<SomeClass*>(FindWindowById(i));
if(element.GetLabel() == textEnteredInTextCtrl)
{
element.Show(true);//element found
//update the necessary layout here using layout call
}
else
{
element.Show(false);//element not found
//update the necessary layout here using Layout() call
}
}
}
This is the main part of the search. Now inside the workerthread should/can i use the functions like FindWindowById() and GetLabel()? Are they considered GUI functions or not so that I can use them from the worker thread? Can i or can i not use FindWindowByID() and GetLabel() and other similar functions(like Layout() and Show()) from inside the worker thread. How should I make this work? I mean I know how to use wxThread and send events using QueueEvent and already have another worker thread in my program that does some other calculation but I am asking about how I should make it work in this particular case.
Another solution suggested by QuentinC would be to use a timer. His suggestion is as follows:
In my case, I don't start refreshing the list immediately after the
user has typed a letter in the search box. Instead, when the user has
typed a letter (wxEVT_TEXT), I start or restart a timer of 500ms. Only
when the timer goes out (the user stops typing for 500ms) then the list
is refreshed. Again, this is a measure to avoid a rapid succession of
useless refreshes.
But in this case of using a timer i have several queries. I am sending the wxEVT_TEXT from CustomTextCtrl's onTextChanged method to this class' onTextChanged method. I guess i could start the timer of 500ms inside the onTextChanged method of the CustomTextCtrl class when the user types a letter. But then where should i check that the timer is still running? In the CustomTextCtrl's onTextChanged method or inside the AnotherClass's onTextChanged method?
So for clarification i have two classes:
the CustomTextCtrl class which have a onTextChanged method which uses event.Skip() to forward this event to its parent.
The parent class AnotherClass which also have a onTextChanged method and this method receives this forwarded event and do the searching and updates the table.
Where and how should i start/restart/stop the timer to update the UI?
NOTE: The process of filtering the elements is working perfectly but the only problem is that the main(GUI) thread is blocked when the user type some text inside the textctrl. After lets say 6 or 7 seconds, the text appears inside the textctrl and the UI is updated. I don't want this unresponsiveness of the main UI for 6-7 seconds.
Also, note that i am not using any wxList/wxGrid. I am just using wxPanels and wxStaticText and using show/hide on them.
Edit: One improvement in the code above is that to only use Layout() call from outside the for loop. If i use the Layout() calls from outside the for loop then the search functionality works almost instantly. But this(method) still has the potential of blocking the main thread in the future if the table has many more elements. So i want to use a thread or a timer method. But i don't know how the secondary thread could use the gui functions or how can/should i use a wxTimer method(if any) to solve this problem.
I have several thoughts on this subjet
A) If you have a speed issue, then you better profile your code, to see where the bottle neck is.
B) Calling GDI functions from a not-main thread is risky. Maybe just asking for window-id and its label is not that dangerous, but I think calling Show() definitely is.
C) This piece of code is mainly GUI related. I don't think a worker thread is useful here. But stacking similar callings may improve its speed. For this, I have three advises:
Use CallAfter() passing the elemnt.Show() method
Use Freeze() before the loop and Thaw after it.
Call Layout()only once, right after the loop. About this I wonder if Show()/Hide() controls is better than Enable()/Disabñe()
D) Because you call FindWindowById() so many times, and also many user changes, it will be better to cache all affected windows in a container (a std::map with id as the key). Then, inside the loop use the container instead of 'FindWindowById()`.
E) As a last resource, if the GUI is still blocked, use wxYield() every xxx (say 100) loop-iterations. Depending of pending messages this solution may get things worse (reentrances, crossed effects, etc).
I have QToolButton:
btn_ = new QToolButton(this);
btn_->setFocusPolicy(Qt::NoFocus);
btn_->setAutoRepeat(false);
connect(btn_, SIGNAL(pressed()), this, SLOT(btnPressed()));
and my slot called twice per on appreciable press on touchpad.
During debug of my program I can see that the first one call from QAbstractButton::mousePressEvent
and the second one QAbstractButton::mouseMoveEvent.
If I press touchpad with one instantaneous and then remove hand from
touchpad, then I got only one call of btnPressed from QAbstractButton::mousePressEvent.
Any idea how to fix this issue? So I have not remove hands from notebook's touchpad
for pressing. I think about timer to measure time from one btnPressed to another,
but have no idea of value of timeout to prevent this. I can of course choose timeout for my notebook, but what if on another notebook it will be too small,
or if choose big one, then users start complains about not responsible interface.
linux/x11/qt4.8/amd64
Try using clicked() instead of pressed(), as it reflects the behaviour the user expect from a single button press/click. In general, if you're not sure you really want the slot called instantly at pressing the button (and handle the special cases that may come with that), you should use clicked().
Let me explain my question with an example. In my app, I need to provide a way to automatically close it from within "itself." (An example of such function is when an installer/updater of my app needs it to close before installing an update. Another example is when my app performs actions on schedule and needs to automatically close once it's done.)
I picture that in the simplest situation posting the WM_CLOSE message to the own window will accomplish this task, but there're the following cases that will not work with just that:
My app may be displaying an arbitrary number of child dialog windows.
My app may be displaying a common control window, such as Open File dialog:
or this one, but there could be others:
And lastly what could one do if the close button is not even available:
At the current point, I simply resort to terminating my process (either with the exit() method from within, or with TerminateProcess from outside.)
But I'm curious, if there's a more graceful way of closing my GUI app (from within)?
There is no single answer, it depends on your app's architecture and what it's doing. I don't quite understand why you would want to force close your application while it is in a common dialog, but if you do, then exit() is safe. In that case the process is actually shutdown from the operating system's perspective as opposed to a forced termination (TerminateProcess). In theory TerminateProcess might leave objects in the OS (DLL global data and such) in an inconsistent state, and exit() will be cleaner in that regard.
Generally though I'd avoid having visible UI that is up for the user just disappear and instead close the windows in reaction to some user choice.
But, exit() will work.
You can use exit() but a safer way to close the window would be:
SomewWindow->ShowWindow(SW_HIDE);
You could then destroy the window object or show the window later.
Hey guys ... Well I'm experiencing this silly problem that whenever I perform a double click event, two mousePressed events are also triggered, meaning that mousePressed event code is also executed twice for no reason .. How can I configure the event such that first the clicks are checked for doubleClick event, and only if this is NOT true, they move on to mousePressed events .. ? Is this possible ?
Before you spend too much time trying to figure this out, consider what Raymond Chen has said about the "Logical consequences of the way Windows converts single-clicks into double-clicks". The techniques he talks about should be easily adaptable to Qt. But also the UI consequences of the "dubious design of having the double-click action be unrelated to the single-click action" - you may be trying to do something that will be confusing to your users (on the other hand - you might trying to prevent something from confusing your users).
Also, the related article, "Why doesn't double-right-click bring up the Properties dialog?" might be of interest.
I'm going to assume you mean for the same widget. The quick and dirty way would be to move the mouse press code into a private method, have the mouse press event set a timer to go off after the expire timer for a possible double click. In the double click code be sure to turn off the timer if it gets called. This will prevent the mouse press event from running twice. In the timer code, have it call the private method.
I'm New to QT. I understand that you can force a display refresh, but I've pulled all my hair out trying to figure out how. Here is what I'm specifically trying to do.
I press a button (onClick signal event), which runs code that changes an image (QLabel) on the display, waits for input, and then proceeds by changing a new image (different QLabel). I've tried everything and the display doesn't refresh until the onclick signal event code is complete. Right now, I'm not waiting for user input, I'm using usleep(~500 ms) for testing purposes.
From what I read, QT is event driven meaning that I'm basically creating a bunch of events, that get put in a que, and executed when the (onClick signal event) returns to the (main loop)/(event handler). I don't want to wait until the function is complete, it's going to make programming extremely painful if I have to accomplish this routine entirely based on events.
How can I force the QLabel pixmap to refresh. I've tried everything. Below is all the code I have tried in my onClick signal event handler. (upButton is the name of the QLabel which is a pixmap)
update();
repaint();
ui->upButton->setUpdatesEnabled(TRUE);
update();
repaint();
QPaintEvent paintevent(ui->upButton->childrenRegion());
QPaintEvent * test = &paintevent;
paintEvent(test);
this->changeEvent(test);
ui->upButton->update();
ui->upButton->repaint();
ui->upButton->repaint(ui->upButton->childrenRegion());
repaint();
QApplication::sendPostedEvents();
this->parentWidget()->update();
usleep(100000);
As you can see, I'm just shooting in the dark at this point. I've tried to look at sample code and do all my homework, but I'm lost. Appreciate any help, advice, and or sample code.
I was using sleep to emulate a brief amount of time the computer was waiting for something to happen.
As I stated in my question, I didn't want to use events because it's a whole lot of unnecessary work to accomplish something extremely simply.
Also, the 'event' that needs to take place for the program to continue, is a USB event. Since I'm using an HID class device, there is no way to set an event to happen without a wait loop. USB HID classes don't permit setting interrupts, the OS claims the device.
I managed to get the above to work. I walked through the debugger and noticed the display would refresh before the sleep function. Running the program independently, I got random results with the display refreshing 1% of the time. I got rid of the sleep function, and added some other code in it's place to emulate a delay, and it was fine.
Just for everyone's knowledge, this is possible, it's not forbidden, and it's easy to do with the following:
qApp->processEvents();
qApp is a global external variable in the QApplication header.
Because this USB event is making my flow tricky, I stumbled upon the QWaitCondition Class. I was going to start a process waiting for the USB event. I would wait until the process releases the wait condition for my routine to continue.
But if anyone thinks this is a bad idea, please, speak out. I really do appreciate your feedback PiedPiper and Hostile Fork.
Thank you.
I noticed sometimes when you have multiple layered widgets, or widgets inside of widgets it helps to call their repaint() events.
For example
this->repaint();
this->parentWidget()->repaint();
this->parentWidget()->parentWidget()->repaint();
This is far easier then pushing out any processing to another Thread, or creating additional event handlers.
You shouldn't be waiting for input in your event handler. You need to rethink the logic of your program to use events the way they were intended. All the update() and repaint() calls in your code are unnecessary if you return to the event loop.
If i understood correctly, you have a slot and in this slot, you update the image shown in a QLabel. But you want this change to be displayed before the slot finishes.
If that is the case, issue an update() event, and call qApp->processEvents(). This method processes events that are waiting in the event queue and then returns, therefore this may be what you are after.
PS: an update() may not be necessary at all, i am not sure.