Given the following code
button = ...
process = QProcess()
button.clicked.connect(start_process)
def start_process():
# Disable the button
button.setEnabled(False)
# This seems to have no effect (...)
# This also has no effect, thus commented out.
# QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
# Execute the program in a blocking way
process.execute('/usr/bin/libreoffice')
# (...) as right now, while libreoffice is running and I click the button
# no animation is taking place, but the click !! is getting registered !!
# If I terminate libreoffice, these clicks are executed and this function is
# called again for every click done.
# When the process and all its children have terminated, enable the button again
button.setEnabled(True)
I have explained what my problem is in the code. I think it has something to do with the main thread (gui thread) getting blocked by the process.execute(...)
call while having the setEnabled(False)
call still in a queue or something.
What I want to achieve:
- User clicks Button -> Button gets disabled and LibreOffice is launched
- While LibreOffice is running, my GUI is blocked and clicks on the button are not registered
- User exits LibreOffice, my GUI is unblocked and the Button is enabled again for another start of LibreOffice
What might eventually work:
Instead of using process.execute(...)
, I use process.start(...)
and hook the start()
signal to my own function to disable the button and the finished()
signal to enable the button again?
What in the end worked:
class C(QObject):
"""
Host (Communicator) for all my signals
"""
enableButton = QtCore.Signal()
disableButton = QtCore.Signal()
class MainWindow(QtGui.QMainWindow):
def __init__(self, program=None, c=None, parent=None):
super(MainWindow, self).__init__(parent)
self.ui = Ui_mainWindow()
self.ui.setupUi(self)
self.program = program
self.c = c # Reference to the C instance
self.setupSignals()
def setupSignals(self):
self.ui.btnStart.clicked.connect(self.program.start) # Trigger button
# Register for events from UI Thread
self.c.enableButton.connect(self.button_enable)
self.c.disableButton.connect(self.button_disable)
def button_enable(self):
self.ui.btnStart.setEnabled(True)
def button_disable(self):
self.ui.btnStart.setEnabled(False)
class Program(object):
def __init__(self, c=None):
self.c = c
self.proc = QProcess() # The process to run in the start method.
# needed here because its runs out of scope otherwise.
def start(self):
def _onStart():
self.c.disableButton.emit()
print("on Start")
def _onFinish():
self.c.enableButton.emit()
print("on Finish")
def _onError():
print("on Error")
self.proc.started.connect(_onStart)
self.proc.finished.connect(_onFinish)
self.proc.error.connect(_onError)
self.proc.start('/usr/bin/libreoffice') # Waits until process ends
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
c = C()
program = Program(c=c)
main_window = MainWindow(program=program, c=c)
main_window.show()
sys.exit(app.exec_())
I hope I haven't stripped it too much. If you have questions, go ahead.
You already answered your question yourself :), so I just summarize it:
QProcess.execute
(it is static, btw, so no need to create aQProcess
object in this case) starts the child process and waits for the child process to finish - while the child process is running, the calling process is blocked and your UI is frozenTo achieve what you require, use
QProcess.start()
to launch the child process asynchronously, and connect the corresponding signalsstarted()
andfinished()
. See the notes in the documentation for some details to consider.