Здесь есть пример, как вы можете это сделать (я использую QWidget
, но вы также можете использовать QDialog
или что-то еще). Я не использую отдельный поток, потому что пользовательский интерфейс не должен быть интерактивным. Если вы хотите добавить кнопки и т. Д., То вы должны подумать о том, чтобы пойти на старый добрый QThread
, работающий по модели QObject
, предоставленной Qt.
#!/usr/bin/python
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class MyQProcess(QWidget):
def __init__(self):
super(QWidget, self).__init__()
# Add the UI components (here we use a QTextEdit to display the stdout from the process)
layout = QVBoxLayout()
self.edit = QTextEdit()
self.edit.setWindowTitle("QTextEdit Standard Output Redirection")
layout.addWidget(self.edit)
self.setLayout(layout)
# Add the process and start it
self.process = QProcess()
self.setupProcess()
# Show the widget
self.show()
def setupProcess(self):
# Set the channels
self.process.setProcessChannelMode(QProcess.MergedChannels)
# Connect the signal readyReadStandardOutput to the slot of the widget
self.process.readyReadStandardOutput.connect(self.readStdOutput)
# Run the process with a given command
self.process.start("df -h")
def __del__(self):
# If QApplication is closed attempt to kill the process
self.process.terminate()
# Wait for Xms and then elevate the situation to terminate
if not self.process.waitForFinished(10000):
self.process.kill()
@pyqtSlot()
def readStdOutput(self):
# Every time the process has something to output we attach it to the QTextEdit
self.edit.append(QString(self.process.readAllStandardOutput()))
def main():
app = QApplication(sys.argv)
w = MyQProcess()
return app.exec_()
if __name__ == '__main__':
main()
Обратите внимание, что команда, я использую (df -h
) выполняется один раз (это команда Linux, который отображает использование дискового пространства на жестком диске), а затем все кончено. Вы также можете заменить его на Execute.exe
, который может работать неограниченно долго. Я тестировал его с помощью htop
(расширенный диспетчер задач на терминале), который когда-то запускался, не останавливается, если пользователь не хочет его или система не останавливается (сбой, выключение и т. Д.).
Обратите внимание, что вы должны убедиться, что внешний процесс остановлен чистым способом.Это можно сделать внутри __del__
(деструктор) или другой функции, вызванной в конце срока службы данного виджета. То, что я сделал, в основном посылает SIGTERM
() по внешнему процессу и как только пройденное заданное количество времени, но процесс все еще работает. Поднимаю ситуацию до SIGKILL
(kill
).
Код требует больше работы, но этого должно быть достаточно, чтобы дать вам представление о том, как все работает.
Здесь такая же версия приведенного выше кода, но с дополнительной темой. Обратите внимание, что я перенаправляю вывод из внешнего процесса в слот моего рабочего. Вам не обязательно это делать, если вы не хотите работать над этим выходом. Таким образом, вы можете пропустить это и подключить свой технологический сигнал к слоту вашего виджета, который получает его и выводит его содержимое. Обработка выходных данных будет сделана снова в отдельном потоке, так что вы можете пойти расстояние вместо замораживания вашего пользовательского интерфейса (который произойдет, если вы будете следовать
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class Worker(QObject):
sendOutput = pyqtSignal(QString)
def __init__(self):
super(Worker, self).__init__()
self.process = QProcess()
self.setupProcess()
def __del__(self):
self.process.terminate()
if not self.process.waitForFinished(10000):
self.process.kill()
def setupProcess(self):
self.process.setProcessChannelMode(QProcess.MergedChannels)
self.process.readyReadStandardOutput.connect(self.readStdOutput)
self.process.start("htop")
@pyqtSlot()
def readStdOutput(self):
output = QString(self.process.readAllStandardOutput())
# Do some extra processing of the output here if required
# ...
self.sendOutput.emit(output)
class MyQProcess(QWidget):
def __init__(self):
super(QWidget, self).__init__()
layout = QVBoxLayout()
self.edit = QTextEdit()
self.thread = QThread()
self.setupConnections()
self.edit.setWindowTitle("QTextEdit Standard Output Redirection")
layout.addWidget(self.edit)
self.setLayout(layout)
self.show()
def setupConnections(self):
self.worker = Worker()
self.thread.finished.connect(self.worker.deleteLater)
self.worker.sendOutput.connect(self.showOutput)
self.worker.moveToThread(self.thread)
self.thread.start()
def __del__(self):
if self.thread.isRunning():
self.thread.quit()
# Do some extra checking if thread has finished or not here if you want to
#Define Slot Here
@pyqtSlot(QString)
def showOutput(self, output):
#self.edit.clear()
self.edit.append(output)
def main():
app = QApplication(sys.argv)
w = MyQProcess()
return app.exec_()
if __name__ == '__main__':
main()
Дальнейшего уточнению: Как я уже говорил @BrendanAbel в разделе комментариев его ответа проблема с использованием слотов с QThread
заключается в том, что слоты имеют одинаковое сходство потоков (= поток, которым они принадлежат), как сам экземпляр QThread
, который является тем же самым потоком, где QThread
был создан из Единственный - я повторяю только - вещь, которая работает в отдельном потоке, когда дело доходит до QThread
- это цикл событий, представленный QThread.run()
. Если вы посмотрите в Интернете, вы обнаружите, что этот способ делать вещи не рекомендуется (если вы действительно не знаете, что у вас есть подкласс QThread
), потому что ранние версии Qt 4 run()
были абстрактными, и вам приходилось подклассы QThread
в для использования QThread
. Позже реферат run()
получил конкретную реализацию, поэтому была устранена необходимость подкласса QThread
. О потокобезопасности и сигналах, которые написал @BrendanAbel, отчасти верно. Оно сводится к типу соединения (по умолчанию AutoConnection
). Если вы вручную укажете тип соединения, вы можете фактически сделать сигналы небезопасными. Узнайте больше об этом в Qt documentation.
почему вы дождались его завершения до того, как оно началось? – user3528438
Если вы хотите следить за чем-то, это, как правило, хорошая практика сделать это в отдельном потоке. Это позволяет пользовательскому интерфейсу оставаться интерактивным, в то время как мониторинг выполняется в фоновом режиме. Вы можете использовать сигнал 'QProcess.readyRead', чтобы подключить его к слоту в пользовательском интерфейсе, который будет запускаться каждый раз, когда подпроцесс что-то произведет. – rbaleksandar
@ user3528438, не уверен, что вы имеете в виду, но, как я уже говорил, если я не использую эту waitForFinished, внешняя программа даже не выполнит ... не могли бы вы уточнить, что вы имеете в виду? –