PySide:将标准输出重定向到对话框

PySide: redirect stdout to a dialog

我有一个在 QThread 中运行函数的 pyside 应用程序。这个函数经常使用print。我如何将标准输出重定向到一个对话框(包含 qtextedit 或类似的),该对话框将在函数运行时显示。

这是一个最小的例子:

class Main(QtGui.QWindow):
  def __init__(self):
      super(Main, self).__init__()
      self.run_button = QtGui.QPushButton("Run")

      mainLayout = QtGui.QVBoxLayout()
      mainLayout.addWidget(self.run_button)
      self.setLayout(mainLayout)

      self.run_button.clicked.connect(self.run_event)


  def run_event(self):
      # Create the dialog to display output
      self.viewer = OutputDialog()

      # Create the worker and put it in a thread
      self.worker = Worker()
      self.thread = QtCore.QThread()
      self.worker.moveToThread(self.thread)
      self.thread.started.connect(self.worker.run)
      self.worker.finished.connect(self.thread.quit)

      self.thread.start()


class Worker(QtCore.QObject):
  finished = QtCore.Signal()

  def run(self):
    for i in range(10):
      print "Im doing stuff" 
      time.sleep(1)
    self.finished.emit()


class OutputDialog(QtGui.QDialog):
  def __init__(self, parent=None):

    super(OutputDialog, self).__init__(parent)
    self.text_edit = QtGui.QTextEdit()
    mainLayout = QtGui.QVBoxLayout()
    mainLayout.addWidget(self.text_edit)

    self.setLayout(vbox)

我可以修改 worker 以将 stdout 重定向到它自己,然后将文本作为信号发出:

class Worker(QtCore.QObject):
  finished = QtCore.Signal()
  message = QtCore.Signal(str)

 def __init__(self):
   super(Worker, self).__init__()

   sys.stdout = self

 def run(self):
    for i in range(10):
      print "Im doing stuff" 
      time.sleep(1)
    self.finished.emit()


 def write(self, text):
     self.message.emit(text)

但是当我将此信号连接到 OutputDialog 实例时,文本仅在工作人员完成后显示。

我也试过在这里实现这个方法: Redirecting stdout and stderr to a PyQt4 QTextEdit from a secondary thread

但这只会导致我的应用程序冻结。

有什么想法吗?

这似乎可以通过子类化轻松完成 QThread:

class Main(QtGui.QWindow):
  def __init__(self):
    super(Main, self).__init__()
    self.run_button = QtGui.QPushButton("Run")

    mainLayout = QtGui.QVBoxLayout()
    mainLayout.addWidget(self.run_button)
    self.setLayout(mainLayout)

    self.run_button.clicked.connect(self.run_event)

  def run_event(self):
    # Create the dialog to display output
    self.viewer = OutputDialog()

    # Create the worker thread and run it
    self.thread = WorkerThread()
    self.thread.message.connect(self.viewer.write)
    self.thread.start()

class WorkerThread(QtCore.QThread):
  message = QtCore.Signal(str)   

  def run(self):    
    sys.stdout = self


    for i in range(10):
      print "Im doing stuff"
      time.sleep(1)    

  def write(self, text):
    self.message.emit(text)

但是,网络上的大多数文档似乎都建议您不要将 QThread 子类化,而是使用 moveToThread 函数来处理任务。

另外,我不知道你在上面的方法中如何区分stdoutstderr(假设你都重定向到工作线程)

您的打印行仅在工作人员完成后才显示的原因由以下堆栈溢出答案解释:

总而言之,在跨线程处理 signals/slots 时,您需要用 @pyqtslot(types) 修饰插槽,以确保它们实际上是 运行 在您想要的线程中。