Python 在几个类中使用QProcess的一个实例(PyQt)

Python 在几个类中使用QProcess的一个实例(PyQt),python,pyqt4,qprocess,Python,Pyqt4,Qprocess,我有一个主窗口包装器类(比如a)和包装器中使用的另一个类(比如B)。 B有一个方法,该方法又有一个子进程。check_调用(command)调用。 我将其更改为使用QProcess,以便能够与此进程通信,并在主窗口QTextEdit中显示QProcess stdout和stderr,以及从主窗口QLineEdit将数据发送回QProcess stdin 为此,我有: class A(....): def __init__(self): .... QtCore.QOb

我有一个主窗口包装器类(比如a)和包装器中使用的另一个类(比如B)。 B有一个方法,该方法又有一个子进程。check_调用(command)调用。 我将其更改为使用QProcess,以便能够与此进程通信,并在主窗口QTextEdit中显示QProcess stdout和stderr,以及从主窗口QLineEdit将数据发送回QProcess stdin

为此,我有:

class A(....):
   def __init__(self):
      ....
      QtCore.QObject.connect(self.ui.actionNew, QtCore.SIGNAL("triggered()", self.selectedNew)
      self.qprocess = QtCore.QProcess()
      self.connect(self.qprocess, QtCore.SIGNAL("readyReadStandardOutput()", self.readStdOut)
      self.connect(self.qprocess, QtCore.SIGNAL("readyReadStandardError()", self.readStdErr)

    def readStdOut(self):
        self.ui.text_edit.append(QtCore.QString(self.qprocess.readAllStandardOutput()))

    def readStdErr(self):
        self.ui.text_edit.append(QtCore.QString(self.qprocess.readAllStandardError()))

    def selectedNew(self:)
        ...
        newB = B(self.qprocess)
        newB.doWork(params)

 class B():
     def __init__(self, qprocess):
         self.qp = qprocess

     def doWork(params):
         ...
         # creating a thread to not block the main thread
         class ConsoleThread(threading.Thread):

             def __init__(self, qprocess):
                self.tqp = qprocess
                threading.Thread.__init__(self)

             def run(self):
                 self.qtp.execute("script_that_waits_for_user_input_during_execution")

          # starting the thread and passing it the QProcess instance from B
          ConsoleThread(self.qp).start()
          print(self.qp.state()) # this returns 0, when I expected 2, obviously something wrong
最后,“在执行期间等待用户输入的脚本”的输出不会显示在QTextEdit中,但仍会打印在控制台中。我似乎没有在A中得到任何信号,也没有到达A.readStdOut()方法。 总体思路是让GUI应用程序包装不同的命令行脚本。因此,我需要一种方法来正确地从QProcess获取输出,并能够通过从GUI写入来进行通信。当然,如果我将函数从B移到A(将消除不必要的步骤),它可能不会那么复杂,但同时我认为GUI包装器应该与逻辑分离


谢谢

我认为您可能误解了使用
QProcess
,因为这个示例做了一些奇怪的事情:

  • 类A的实例获取作为成员创建的QProcess,并发送连接的信号
  • 每次调用
    selectedNew()
    ,它都会创建一个新的B实例,每次都传递相同的QProcess实例
  • 然后调用
    doWork
    ,每次都创建一个本地类定义,只是为了创建一个实例并丢弃该类。线程在QProcess实例上调用staticmethod,该实例创建一个自由运行的新QProcess
  • doWork
    中,您在启动asych线程后立即检查
    QProcess
    的状态,就好像QProcess实例在整个过程中发生了任何事情一样
  • 主要是,每次都有一个线程启动一个新的进程,然后检查一个值,该值在原始进程上没有改变,因为它从未启动过。信号永远不会被调用,因为它们与原始进程相连。为了简化整个情况,您只需在每次希望运行时使用一个新的QProcess即可

    首先,让我们把类
    A
    清理成实际可以正常工作的东西:

    class A():
        def __init__(self):
          ...
          self.ui.actionNew.triggered.connect(self.selectedNew)
    
        def readStdOut(self):
            self.ui.text_edit.append(str(self.qprocess.readAllStandardOutput()))
    
        def readStdErr(self):
            self.ui.text_edit.append(str(self.qprocess.readAllStandardError()))
    
        def selectedNew(self):
            ...
            qprocess = QtCore.QProcess(self)
            qprocess.readyReadStandardOutput.connect(self.readStdOut)
            qprocess.readyReadStandardError.connect(self.readStdErr)
    
            qprocess.start("script_that_waits_for_user_input", params)
    
            print qprocess.state() == qprocess.Running
    
    您不需要在init中创建QProcess,而是在需要运行某些东西时按需创建它。然后,将该实例的信号连接到插槽,并调用QProcess上的start。这将实际启动进程,并调用您的插槽

    我不知道您打算使用class
    B
    做什么,除非您希望将命令行脚本包装到不同的类中,在这种情况下,您将完全将QProcess创建移动到class
    B
    ,然后将class
    A
    信号连接到
    B_实例.QProcess
    成员

    更新

    为了将此QProcess职责下移到
    B
    类,以便您可以有许多不同的类类型来执行不同类型的工作,它可能如下所示:

    class A(QtGui.QMainWindow):
        ...
    
        def readStdOut(self):
            qprocess = self.sender()
            self.ui.text_edit.append(QtCore.QString(qprocess.readAllStandardOutput()))
    
        def readStdErr(self):
            qprocess = self.sender()
            self.ui.text_edit.append(QtCore.QString(qprocess.readAllStandardError()))
    
    
        def selectedNew(self):
            ...
            b = B()
            b.qprocess.readyReadStandardOutput.connect(self.readStdOut)
            b.qprocess.readyReadStandardError.connect(self.readStdErr)
    
            b.doWork(params)
    
            print b.qprocess.state() == qprocess.Running
    
    
    class B(QtCore.QObject):
        def __init__(self):
            ...
            self.qprocess = QtCore.QProcess(self)
    
        def doWork(params):
            ...
            self.qprocess.start("script_that_waits_for_user_input", params)
    

    A
    创建
    B
    的新实例,并将
    B
    进程的信号连接到它自己的插槽。然后使用命令的参数调用
    doWork
    B
    实际上启动线程并拥有它。

    非常感谢您详细的回答!我有两个问题:1。B类是一个包装一些与基础设施相关的设置步骤的程序。它是一个接受用户输入的脚本,通过调用子流程(即执行不同的脚本和程序)执行多个不同的任务。现在,用户输入被gui替换为我的脚本,但是底层的用户仍然期望一些用户交互。我不想把所有的B功能都转移到A类中。2.您能告诉我如何将A signals连接到B_instance.qprocess以能够对其进行读/写吗?刚刚添加了另一个示例,说明如何将流程功能下移到
    B
    ,再次感谢您提供的精彩信息-它几乎可以工作了!有一件事:在类B中启动进程后,我说self.qprocess.waitForFinished(),以便让底层程序完成所有的任务,这样我的脚本就可以继续了。当底层脚本要求用户确认继续时,我得到完成的信号!我希望能够启动基础脚本,当它要求确认时,我希望能够回答,以便它可以继续或取消。(基础脚本不是我的-无法编辑)。不过,它似乎只是在计时……首先,如果调用
    waitForFinished
    ,它将在主线程中阻止整个应用程序。首先,您计划如何与流程进行沟通?您的主UI元素是否正在读取标准输出,然后您有其他一些将写入进程的行编辑输入?你不能等待这个过程。您需要使用信号和输出读数。然后,您可以在进程上调用
    writeData()
    ,以发回用户输入。