2017-02-19 49 views
0

мне нужен не блокирующий графический интерфейс решение перспективе неопределенное количество системных команд (например, Баш скрипт, который принимает некоторые параметры в качестве входных данных), монитор их положение дел. (например: запуск/завершение) и прекратить действие (убейте) процесс.Использование QProcess для запуска и мониторинга системных процессов с PyQt

Примером может быть:

1) выбрать программу из списка (QComboBox)

2) установить параметр (QLineEdit)

3) запустить его (QProcess)

, как она работает, добавьте:

  • команда
  • параметры
  • Статус

в строке в QTableWidget

.. Я ищу решение для мониторинга состояния каждой команды.

Приложение может быть простой скрипт, как этот:

import sys 
import os 
from PyQt5.QtWidgets import QApplication, \ 
          QWidget, \ 
          QComboBox, \ 
          QToolButton, \ 
          QTableWidget, \ 
          QTableWidgetItem, \ 
          QFormLayout, \ 
          QLineEdit, \ 
          QPushButton 

from PyQt5.QtGui import QStandardItemModel 
from PyQt5 import QtCore 


class runcommands(QWidget): 
    def __init__(self, parent=None): 
     super(runcommands, self).__init__(parent) 

     layout = QFormLayout() 
     self.commandlist = QComboBox() 
     self.param = QLineEdit() 
     self.runit = QToolButton() 
     self.runit.setText('run') 
     self.runit.clicked.connect(self.runcommand) 
     self.commandlist.addItems(['simplerun.py', 'simplerun2.py']) 
     self.table = QTableWidget() 
     self.table.setColumnCount(5) 
     self.model = QStandardItemModel() 
     self.table.setHorizontalHeaderLabels(['Process', 'Parameter', 'STDOut', 'Status', 'Kill']) 
     self.rowcount = 0 

     layout.addRow(self.commandlist) 
     layout.addRow(self.param) 
     layout.addRow(self.runit) 

     layout.addRow(self.table) 

     self.setLayout(layout) 
     self.setWindowTitle("Run & Monitor") 
     self.commandrunning=0 
     self.mylistofprocesses=[] 



    def runcommand(self): 
     # add a record in the QTableWidget 
     # updating its row number at each run 
     self.rowcount = self.rowcount + 1 
     self.table.setRowCount(self.rowcount) 

     # add column 0: command string 
     self.c1 = QTableWidgetItem() 
     self.c1.setText("%s" % os.path.join(os.getcwd(), self.commandlist.currentText())) 
     self.table.setItem(self.rowcount - 1, 0, self.c1) 

     # add column 1: parameter string 
     self.c2 = QTableWidgetItem() 
     self.c2.setText("%s" % self.param.text()) 
     self.table.setItem(self.rowcount - 1, 1, self.c2) 

     # add column 2 to store the Process StandardOutput 
     stdout_item = QTableWidgetItem() 
     self.table.setItem(self.rowcount - 1, 2, stdout_item) 

     # add column 3: index to store the process status (0: Not Running, 1: Starting, 2: Running) 
     status_item = QTableWidgetItem() 
     self.table.setItem(self.rowcount - 1, 3, status_item) 

     # add column 4: kill button to kill the relative process 
     killbtn = QPushButton(self.table) 
     killbtn.setText('Kill') 
     self.table.setCellWidget(self.rowcount - 1, 4, killbtn) 

     # Initiate a QProcess running a system command 
     process = QtCore.QProcess() 
     command = 'python3' + ' ' + os.getcwd() + '/' + self.commandlist.currentText() + ' ' + self.param.text() 
     process.setProcessChannelMode(QtCore.QProcess.MergedChannels) 
     # connect the stdout_item to the Process StandardOutput 
     # it gets constantly update as the process emit std output 
     process.readyReadStandardOutput.connect(lambda: stdout_item.setText(str(process.readAllStandardOutput().data().decode('utf-8')))) 
     # start the process 
     process.start(command) 
     # this was supposed to add the process status in the relative column ... BUT it DOESN'T do it 
     status_item.setText(str(process.ProcessState())) 
     # connect the kill button to the process.kill method, to stop the process 
     killbtn.clicked.connect(process.kill) 
     killbtn.clicked.connect(lambda: killbtn.setText('Killed')) 
     # this was supposed to 'UPDATE' the process status (from running to stoppted) in the relative column ... BUT it DOESN'T do it 
     killbtn.clicked.connect(lambda: status_item.setText(str(process.ProcessState()))) 
     # append the process to a list so that it doesn't get destroyed at each run 
     self.mylistofprocesses.append(process) 


def main(): 
    app = QApplication(sys.argv) 
    ex = runcommands() 
    ex.show() 
    sys.exit(app.exec_()) 


if __name__ == '__main__': 
    main() 

Благодаря помощи Аварисе на IRC я ​​установил главный вопрос подключения каждой строки в таблице в отдельном процессе.

После некоторого редактирования исходного вопроса я немного очистил код, и я смог добавить кнопку, чтобы остановить/убить процесс.

Для выполнения этой примерной задачи мне необходимо реализовать мониторинг всех активных процессов и обновить их статус «в реальном времени» в таблице в 4-м столбце (в дополнение к печати третьего столбца std-вывода).

Я пытался сделать это с помощью:

status_item.setText(str(process.ProcessState()))) 

, но я не могу заставить его работать.

+0

Проблема с этим кодом является то, что процесс будет замещаться (уничтожен) каждый раз, когда я выполните новую команду. Как было предложено в PyQt IRC от Avaris, решение может состоять в том, чтобы добавить процессы в список, чтобы он не разрушался. – user1013346

+0

Я обновил код до рабочей версии. теперь таблица получит обновление для каждой команды, добавленной в список процессов. Запись еще не завершена, так как мне нужно добавить к этому примеру «статус» процесса (запуск/завершение), а не вывод в режиме standrad.Также мне нужно добавить новый столбец для каждой строки с виджетами, чтобы «остановить/убить» процесс. – user1013346

ответ

1

Вы должны использовать stateChanged сигнал:

[...] 
self.mylistofprocesses.append(process) 

     status = {QProcess.NotRunning: "Not Running", 
        QProcess.Starting: "Starting", 
        QProcess.Running: "Running"} 

     process.stateChanged.connect(lambda state: status_item.setText(status[state])) 

Скриншоты:

enter image description here

enter image description here

+0

Спасибо! Eyllanesc! '' 'process.stateChanged''' именно то, чего я не видел! Thaks. – user1013346

+0

Я также добавил обновление к кнопке, чтобы обновить его текст и отключить его, когда процесс реминирован: '' 'process.stateChanged.connect (lambda state: killbtn.setText ('Terminated'), если status [state ] == «Не работает» else killbtn.setText ('Kill')) '' ' и: ' '' process.stateChanged.connect (lambda state: killbtn.setEnabled (False), если status [state] = = "Не работает" else killbtn.setText ('Kill')) '' ' – user1013346

+0

Если мой ответ помогает, отметьте его как правильно. – eyllanesc