Search code examples
pythonqtsubprocesspyqt5qapplication

Sending signal to QApplication


I'm sending a signal.CTRL_BREAK_EVENT to a QApplication subprocess and the subprocess's handler manages to catch the signal (and perform some magic before exiting). However, when the signal is sent, it is not processed until I interact with the QApplication window (causing it to somehow use CPU cycles LOL), only then will it handle the signal.

E.g.

  1. I start QApplication as subprocess
  2. I send a signal.CTRL_BREAK_EVENT (from server that started the subprocess).
  3. Nothing happens.
  4. I click on any button in the QApplication.
  5. It handles the signal.CTRL_BREAK_EVENT and exits.

I would of course prefer step 5 to take place at step 3.

What's wrong? How would I "refresh" the QApplication or virtualy click a button when it's run in a subprocess? I suspect that the QApplication's main event loop somehow is in idle mode... until the application is interacted with. (?)

server.py

app = None

def start_app():
    global app

    app = subprocess.Popen("python app.py")

def exit_app():
    global app

    p = app.poll()
    if p==None:
        print("Subprocess is alive") # debug
    app.send_signal(signal.CTRL_BREAK_EVENT)

app.py

import sys, signal
from runner import mainWindow

from PyQt5.QtWidgets import QApplication

app = None
mw = None

def exit_signal_handler(signal, frame):
    global app, mw

    print("Terminate signal received")
    app.quit()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    signal.signal(signal.SIGBREAK, exit_signal_handler)

    mw = mainWindow.MainWindow() # this is the Qt window starting
    mw.actionExit.triggered.connect(app.quit)

    sys.exit("Runner exit with code: " + str(app.exec()))

Solution

  • Adding a Qtimer() in app.py for poll reasons seems to do the trick. Any signals sent will be processed every second when pollMe()is called by Qtimer

    import sys, signal
    from runner import mainWindow
    from PyQt5 import QtCore           #<---- NEW
    from PyQt5.QtWidgets import QApplication
    
    app = None
    mw = None
    
    def pollMe():                      # <--- NEW
        timerPoll.start(1000)
    
    def exit_signal_handler(signal, frame):
        global app, mw
    
        print("Terminate signal received")
        app.quit()
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        signal.signal(signal.SIGBREAK, exit_signal_handler)
    
        timerPoll = QtCore.QTimer()        #<---- NEW
        timerPoll.timeout.connect(pollMe)
        timerPoll.start(1000)
    
        mw = mainWindow.MainWindow() # this is the Qt window starting
        mw.actionExit.triggered.connect(app.quit)
    
        sys.exit("Runner exit with code: " + str(app.exec()))