4

I have 3 classes

One is the Console class:

class Console(QWidget):
    def __init__(self):
        super().__init__()
        self.editor = QPlainTextEdit(self)
        self.editor.setReadOnly(True)
        self.font = QFont()
        self.font.setFamily(editor["editorFont"])
        self.font.setPointSize(12)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.editor, 1)
        self.setLayout(self.layout)
        self.output = None
        self.error = None
        self.editor.setFont(self.font)

    def run(self, command):
        """Executes a system command."""

        out, err = subprocess.Popen(command, shell=True,    stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
        self.output = out
        self.error = err
        self.editor.setPlainText((self.output + self.error).decode())
        return self.output + self.error

The other one is a Tabs class which assigns Console() to the variable self.console.

And then I have the Main class which has a function called Terminal which can be called by a keyboard shortcut Shift+F10

That will take the current filename of the file opened (this is handled with the Tabs class) and run it using subprocess.

Now we get to the problem: when running some programs that aren't instant, the whole GUI freezes and I can't figure out how to make the GUI responsive when the Console class has executed the run function.

The whole code can be found here: https://github.com/Fuchsiaff/PyPad

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Now that I look over it, it only needs to return self.error because that is used to help users find a problem to their error. –  Aug 13 '18 at 19:11
  • When I tried messing around with signals, I couldn't get it to work –  Aug 13 '18 at 19:14
  • First i assigin Console() to self.Console on the line 556 then on line 995 I use it. –  Aug 13 '18 at 19:19
  • Yeah that would be amazing! –  Aug 13 '18 at 19:21

2 Answers2

3

You do not use subprocess.Popen() because it is blocking, and just one of the disadvantages of blocking tasks is that they do not allow the GUI to perform other jobs, for this Qt provides the QProcess class that does not block the event-loop:

import sys

from PyQt5 import QtCore, QtGui, QtWidgets

class Console(QtWidgets.QWidget):
    errorSignal = QtCore.pyqtSignal(str) 
    outputSignal = QtCore.pyqtSignal(str)
    def __init__(self):
        super().__init__()
        self.editor = QtWidgets.QPlainTextEdit(self)
        self.editor.setReadOnly(True)
        self.font = QtGui.QFont()
        # self.font.setFamily(editor["editorFont"])
        self.font.setPointSize(12)
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.editor, 1)
        self.setLayout(self.layout)
        self.output = None
        self.error = None
        self.editor.setFont(self.font)
        self.process = QtCore.QProcess()
        self.process.readyReadStandardError.connect(self.onReadyReadStandardError)
        self.process.readyReadStandardOutput.connect(self.onReadyReadStandardOutput)

    def onReadyReadStandardError(self):
        error = self.process.readAllStandardError().data().decode()
        self.editor.appendPlainText(error)
        self.errorSignal.emit(error)

    def onReadyReadStandardOutput(self):
        result = self.process.readAllStandardOutput().data().decode()
        self.editor.appendPlainText(result)
        self.outputSignal.emit(result)


    def run(self, command):
        """Executes a system command."""
        # clear previous text
        self.editor.clear()
        self.process.start(command)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = Console()
    w.show()
    w.errorSignal.connect(lambda error: print(error))
    w.outputSignal.connect(lambda output: print(output))
    w.run("ping 8.8.8.8 -c 100")
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • 4
    subprocess.Popen() is not blocking. The problem is the communicate call. While QProcess should probably be prefered, subprocess.Popen works just fine with Qt. The problem is solely that communicate() blocks until it the process is finished. – Jannick Aug 15 '18 at 14:32
  • Whooo, you overrode two functions just to use a third party library? – surge10 Aug 15 '18 at 14:33
  • What, QT is part of the standard library now ? For your Info, Never use a third-party framework if there is an existing standard library that can do the job. – surge10 Aug 15 '18 at 14:40
  • 2
    @eyllanesc Don't feed the troll :). Well in your first sentence you write that subprocess.Popen() is blocking, which is not true. So my comment is just meant as a minor correction to an otherwise perfectly nice answer. subprocess.Popen() is not blocking and can be used but has the disadvantage that no signal is emitted when the calculation is finished. So you have to regularly check whether it is still running and then use process.communicate(). Therefore QProcess is to be prefered as indicated by you. – Jannick Aug 15 '18 at 14:49
-2

Use python's built in threading module.

Then do:

import threading

Use it as:

def run(self, command):
    """Executes a system command."""

    tt = threading.Threading( target = self._sub_thread )
    tt.start()

def _sub_thread(self):
    out, err = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE).communicate()
    self.output = out
    self.error = err
    self.editor.setPlainText((self.output + self.error).decode())

This is pragmatics

surge10
  • 622
  • 6
  • 18
  • 2
    What does it mean? – eyllanesc Aug 15 '18 at 10:24
  • use the the built in threading module instead ie. ``import threading``` – surge10 Aug 15 '18 at 11:29
  • it seems to me that you are from the days of anthropic. sure I know pyqt, built almost everything UI I've built with it, still building with it. Load my files on [Github](http://github.com/amoh-godwin) and enjoy the code. How else is the threading module used. – surge10 Aug 15 '18 at 12:34
  • The guy says he has three classes I show him an elaboration of how the threading module is used. using his own class above. This is just an elaboration. The code works fine with the other classes. Besides good luck with your love of already built softwares camouflage as libraries – surge10 Aug 15 '18 at 14:06
  • do you know python? threading works with ```target = function``` syntax – surge10 Aug 15 '18 at 14:38
  • Sure console = Console() was bad, but ```target = object``` works – surge10 Aug 15 '18 at 15:07