5

I would like to execute a method which can only be called once my QApplication is displayed, i.e. when it has entered its main event loop exec_(). I'm new to Qt4 (using PyQt4): i was hoping to have a on_start()-like callback, but didn't find one. Do i need to create a thread or a timer? Or is there some callback included in the API already?

neydroydrec
  • 6,973
  • 9
  • 57
  • 89

3 Answers3

6

You can use a single-shot timer for this, as in the following simple script:

import sys
from PyQt4 import QtGui, QtCore

app = QtGui.QApplication(sys.argv)

def on_start():
    print(' in event loop!')
    print(' telling app to exit ...')
    app.exit(123)

QtCore.QTimer.singleShot(0, on_start)
print('About to enter event loop')
rc = app.exec_()
print('All done - returned %d' % rc)

when you run this, you should see

About to enter event loop
 in event loop!
 telling app to exit ...
All done - returned 123
Vinay Sajip
  • 95,872
  • 14
  • 179
  • 191
  • ha thanks, i thought of using QTimer too. I chose for [QThread](http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qthread.html) eventually, which i thought was more simple to use. But your example makes it more clear than the documentation :) – neydroydrec Jun 02 '11 at 19:54
0

I haven't used Qt before, so I might be wrong on this, but I imagine you could bind your on_start() method to the ApplicationActivate event, and set a flag from inside your on_start() method so that the code would only be run that very first time and not any other times the ApplicationActivate event is triggered during execution of your program.

On the other hand if the on_start() method would be called by multiple threads, you might want to use thread-specific flags. Haven't done much multithreading myself, so I'm not sure of what the specifics for that would be, or how simple/complicated it would be.

JAB
  • 20,783
  • 6
  • 71
  • 80
  • Hey thanks for that. I've seen the [`Qt.ApplicationActivate`](http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qevent.html#Type-enum) but i remain confused because the [`QApplication`](http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qapplication.html) does not have that signal implemented. If i have to bind this signal to `QApplication`, it would have to take place after the event loop has started too, which brings me back to my initial problem. But perhaps i am just not understanding how signals work in Qt :) – neydroydrec Jun 02 '11 at 19:51
  • "but i remain confused because the QApplication does not have that signal implemented." That's what subclassing is for. – JAB Jun 02 '11 at 20:10
  • @JAB: Yep i thought of that in the time between my comment and yours. But then can the event of `QApplication` be passed to `QWidget` even as it the latter is not the child of the former? I've seen event-handling between children of or with the topmost widget only. – neydroydrec Jun 02 '11 at 20:17
  • I think it should be possible, but as I said I don't have any experience actually using Qt. http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qcoreapplication.html#notify – JAB Jun 02 '11 at 20:26
  • @JAB: ha! perhaps i can connect the `Widget()` instance to the `QApplication` outside the Widget() class definition, i.e. in the `main()` function. I'll try and let you know how it worked. Thanks. – neydroydrec Jun 02 '11 at 20:27
  • @JAB: despite information asserting the contrary, or if indeed the event is emitted after the main loop is entered, the event posted with the [`postEvent()`](http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qcoreapplication.html#postEvent) method is released at a point when the window is yet not displayed. So i stick to QThread. – neydroydrec Jun 03 '11 at 19:37
  • @Benjamin: Even if you use `QtEvent.WindowActivate` rather than `QtEvent.ApplicationActivate`? – JAB Jun 03 '11 at 20:47
0

For now i have chosen to use QThread this way:

class MyThread(QtCore.QThread):
    def run(self):
    ''' reinplemented from parent '''
        # make thread sleep to make sure
        # QApplication is running before doing something
        self.sleep(2)
        do_something()

class MyWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.attr = 'foo'
        self.thread = MyThread(self)
        self.thread.start()

def main():
    app = QtGui.QApplication(sys.argv)
    w = MyWidget()
    w.show()
    sys.exit(app.exec_())

It works for my purpose because my thread in my (real) code actually looks for the application window in X display and will try doing so until it finds it. But otherwise that's not an elegant way to solve the problem. It would be nicer if there was a signal emitted by QApplication when entering the event-loop. JAB proposed the Qt.ApplicationActivate but it doesn't seem to be emitted by QApplication, and even if it was, because the MyWidget() is not instantiated as a child of QApplication, i wouldn't know how to pass the signal from app to w

I'll wait for a better answer, if any, before accepting my answer as the chosen solution.

neydroydrec
  • 6,973
  • 9
  • 57
  • 89