Issue
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
Solution
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_())
Answered By - eyllanesc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.