How To Update Ui With Output From Qprocess Loop Without The Ui Freezing?
I am wanting to have a list of commands being processed through a QProcess and have its output be appended to a textfield I have. I've found a these two pages that seems to do each
Solution 1:
It is not necessary to use threads in this case since QProcess is executed using the event loop. The procedure is to launch a task, wait for the finishes signal, get the result, send the result, and execute the next task until all the tasks are finished. The key to the solution is to use the signals and distribute the tasks with an iterator.
Considering the above, the solution is:
from PySide import QtCore, QtGui
classTask:
def__init__(self, program, args=None):
self._program = program
self._args = args or []
@propertydefprogram(self):
return self._program
@propertydefargs(self):
return self._args
classSequentialManager(QtCore.QObject):
started = QtCore.Signal()
finished = QtCore.Signal()
progressChanged = QtCore.Signal(int)
dataChanged = QtCore.Signal(str)
def__init__(self, parent=None):
super(SequentialManager, self).__init__(parent)
self._progress = 0
self._tasks = []
self._process = QtCore.QProcess(self)
self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self._process.finished.connect(self._on_finished)
self._process.readyReadStandardOutput.connect(self._on_readyReadStandardOutput)
defexecute(self, tasks):
self._tasks = iter(tasks)
self.started.emit()
self._progress = 0
self.progressChanged.emit(self._progress)
self._execute_next()
def_execute_next(self):
try:
task = next(self._tasks)
except StopIteration:
returnFalseelse:
self._process.start(task.program, task.args)
returnTrue
QtCore.Slot()
def_on_finished(self):
self._process_task()
ifnot self._execute_next():
self.finished.emit()
@QtCore.Slot()def_on_readyReadStandardOutput(self):
output = self._process.readAllStandardOutput()
result = output.data().decode()
self.dataChanged.emit(result)
def_process_task(self):
self._progress += 1
self.progressChanged.emit(self._progress)
classMainWindow(QtGui.QMainWindow):
def__init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self._button = QtGui.QPushButton("Start")
self._textedit = QtGui.QTextEdit(readOnly=True)
self._progressbar = QtGui.QProgressBar()
central_widget = QtGui.QWidget()
lay = QtGui.QVBoxLayout(central_widget)
lay.addWidget(self._button)
lay.addWidget(self._textedit)
lay.addWidget(self._progressbar)
self.setCentralWidget(central_widget)
self._manager = SequentialManager(self)
self._manager.progressChanged.connect(self._progressbar.setValue)
self._manager.dataChanged.connect(self.on_dataChanged)
self._manager.started.connect(self.on_started)
self._manager.finished.connect(self.on_finished)
self._button.clicked.connect(self.on_clicked)
@QtCore.Slot()defon_clicked(self):
self._progressbar.setFormat("%v/%m")
self._progressbar.setValue(0)
tasks = [
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
]
self._progressbar.setMaximum(len(tasks))
self._manager.execute(tasks)
@QtCore.Slot()defon_started(self):
self._button.setEnabled(False)
@QtCore.Slot()defon_finished(self):
self._button.setEnabled(True)
@QtCore.Slot(str)defon_dataChanged(self, message):
if message:
cursor = self._textedit.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(message)
self._textedit.ensureCursorVisible()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Post a Comment for "How To Update Ui With Output From Qprocess Loop Without The Ui Freezing?"