views:

859

answers:

4

wx (and wxPython) has two events I miss in PyQt:

  • EVT_IDLE that's being sent to a frame. It can be used to update the various widgets according to the application's state
  • EVT_UPDATE_UI that's being sent to a widget when it has to be repainted and updated, so I can compute its state in the handler

Now, PyQt doesn't seem to have these, and the PyQt book suggests writing an updateUi method and calling it manually. I even ended up calling it from a timer once per 0.1 seconds, in order to avoid many manual calls from methods that may update the GUI. Am I missing something? Is there a better way to achieve this?


An example: I have a simple app with a Start button that initiates some processing. The start button should be enabled only when a file has been opened using the menu. In addition, there's a permanent widget on the status bar that displays information.

My application has states:

  1. Before the file is opened (in this state the status bar show something special and the start button is disabled)
  2. File was opened and processing wasn't started: the start button is enabled, status bar shows something else
  3. The processing is running: the start button now says "Stop", and the status bar reports progress

In Wx, I'd have the update UI event of the button handle its state: the text on it, and whether it's enabled, depending on the application state. The same for the status bar (or I'd use EVT_IDLE for that).

In Qt, I have to update the button in several methods that may affect the state, or just create a update_ui method and call it periodically in a timer. What is the more "QT"-ish way?

+1  A: 

As far as I understand EVT_IDLE is sent when application message queue is empty. There is no such event in Qt, but if you need to execute something in Qt when there are no pending events, you should use QTimer with 0 timeout.

Paul
Yes, but EVT_IDLE is sent just once. How can I make such a timer, that's sent just once when there are no other events? I don't think I can (a single shot doesnt help here, because I want this _every time_ there's no other events)
Eli Bendersky
+4  A: 

The use of EVT_UPDATE_UI in wxWidgets seems to highlight one of the fundamental differences in the way wxWidgets and Qt expect developers to handle events in their code.

With Qt, you connect signals and slots between widgets in the user interface, either handling "business logic" in each slot or delegating it to a dedicated method. You typically don't worry about making separate changes to each widget in your GUI because any repaint requests will be placed in the event queue and delivered when control returns to the event loop. Some paint events may even be merged together for the sake of efficiency.

So, in a normal Qt application where signals and slots are used to handle state changes, there's basically no need to have an idle mechanism that monitors the state of the application and update widgets because those updates should occur automatically.

You would have to say a bit more about what you are doing to explain why you need an equivalent to this event in Qt.

David Boddie
thanks so far. I've placed an example and would love to hear an opinion
Eli Bendersky
One way to implement the application is to have a slot for each state in which you update the widgets in the way you describe. So, you might have a slot in which you update the status bar and disable the start button, for example. Each slot would start some task that would lead to the next slot.
David Boddie
You might find Qt's FTP and Find Files examples useful.
David Boddie
+2  A: 

I would send Qt signals to indicate state changes (e.g. fileOpened, processingStarted, processingDone). Slots in objects managing the start button and status bar widget (or subclasses) can be connected to those signals, rather than "polling" for current state in an idle event.

If you want the signal to be deferred later on in the event loop rather than immediately (e.g. because it's going to take a bit of time to do something), you can use a "queued" signal-slot connection rather than the normal kind.

http://doc.trolltech.com/4.5/signalsandslots.html#signals

The connection type is an optional parameter to the connect() function: http://doc.trolltech.com/4.5/qobject.html#connect , http://doc.trolltech.com/4.5/qt.html#ConnectionType-enum

Reed Hedges
+1  A: 

In general, the more Qt-ish way is to update the button/toolbar as necessary in whatever functions require the update, or to consolidate some of the functionality and directly call that function when the program needs it (such as an updateUi function).

You should be aware that in Qt, changing an attribute of a Ui element doesn't cause an immediate redraw, but queues a redraw in the event system, and multiple redraw calls are compressed into one where possible.

As for the multiple changes relating to state, have a look at this blog post about a hopefully-upcoming addition to Qt to more easily handle states. It looks like this would take care of a lot of your complaints, because in your multiple functions, you could just transition the state variable, and the other parts of the UI should update to match. It's not positive this will make it into the next Qt release (although I would bet on it, or something similar), and I have no idea how closely PyQt tracks the Qt releases. Or alternately, you could use the concept and create your own class to track the state as needed.

Caleb Huitt - cjhuitt