Pranav Natekar
Pranav Natekar

Reputation: 35

"GUI becomes unresponsive after clicking the button"

I've designed a GUI for my application and after a button click, I've connected a custom function of mine wherein I call a bash command to run 2 Python scripts simultaneously. Both the scripts are Qt applications. I have a button named 'Start' to run the bash command. But as soon as I have clicked the button, both the applications start but my GUI freezes. Also, I have a 'Stop' button to stop these applications, but the GUI freezes and I'm unable to do anything with the GUI. What am I doing wrong?

I've tried various options for running 2 scripts simultaneously, but the command python3 script1.py & python3 script2.py & worked fine for me. Are there any other ways around? The os.system() or subprocess.call() didn't work out for me. Are there any efficient ways to do so?

This is my main code for my UI.

class MainApp(QtWidgets.QMainWindow, mainui.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainApp, self).__init__(parent)
        self.setupUi(self)
        self.exitapp()
        self.startApp()


    def multipleopen(self):
        self.output = subprocess.check_output(['bash', '-c', 
                      "python3 guiGO.py & python3 liveSpectogram.py &"])
    def startApp(self):
        self.start.clicked.connect(self.multipleopen)

    def exitapp(self):
      self.exit.clicked.connect(QtCore.QCoreApplication.instance().quit)

I'm expecting to start both applications on clicking the 'Start' button and stop it by clicking the 'Stop' button. But after clicking the 'Start' button the GUI freezes and I'm unable to do anything but close the running applications and close the GUI window

GUI becomes unresponsive

I've to force quit the GUI

I have to stop the script manually using the stop button

Upvotes: 1

Views: 905

Answers (1)

eyllanesc
eyllanesc

Reputation: 243897

The function subprocess.check_output() is blocking, so it will prevent the event-loop of the GUI from doing its job by freezing your application, instead you should use QProcess:

class MainApp(QtWidgets.QMainWindow, mainui.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainApp, self).__init__(parent)
        self.setupUi(self)

        self._process = QtCore.QProcess(self)
        self._process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
        self._process.setProcessChannelMode(QtCore.QProcess.ForwardedChannels)
        self._process.setProgram('bash')
        self._process.setArguments(['-c', 'python3 guiGO.py & python3 liveSpectogram.py &'])

        self.exitapp()
        self.startApp()

    # read prints
    def on_readyReadStandardOutput(self):
        self.output = self._process.readAllStandardOutput()
        print(self.output)

    def startApp(self):
        self.start.clicked.connect(self._process.start)

    def exitapp(self):
        self.exit.clicked.connect(self.close)

Update:

If you want to kill the python scripts you must do it through bash, with the pkill command, since this was the one that launched them. And in what part of the code should you call pkill ?, a possible part is the closeEvent method.

from PyQt5 import QtCore, QtWidgets

class MainApp(QtWidgets.QMainWindow): #, mainui.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainApp, self).__init__(parent)
        self.setupUi(self)

        self._scripts = ("guiGO.py", "liveSpectogram.py")
        self._processes = []
        for script in self._scripts:
            process = QtCore.QProcess(self)
            process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
            process.setProcessChannelMode(QtCore.QProcess.ForwardedChannels)
            process.setProgram('bash')
            process.setArguments(['-c', 'python3 {}'.format(script)])
            self._processes.append(process)

        self.exitapp()
        self.startApp()

    # read prints
    def on_readyReadStandardOutput(self):
        self.output = self.sender().readAllStandardOutput()
        print(self.output)

    def startApp(self):
        for process in self._processes:
            self.start.clicked.connect(process.start)

    def exitapp(self):
        self.exit.clicked.connect(self.close)

    def closeEvent(self, event):
        for script in self._scripts:
            QtCore.QProcess.startDetached("bash", ["-c", "pkill -f {}".format(script)])
        super(MainApp, self).closeEvent(event)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MainApp()
    w.show()
    sys.exit(app.exec_())

Upvotes: 2

Related Questions