Skip to content Skip to sidebar Skip to footer

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?"