Python 2.7 QTextEdit小部件中的重复stdout、stderr
几天来,我的PySide程序一直有困难。我不认为这个问题是难以置信的困难,因为有答案。我的问题是,它们似乎都不适合我 我想在PySide程序运行时“侦听”文件对象stdout和stderr,并将内容输出到QText Edit小部件。现在,我已经意识到这个问题(或类似的问题)以前在这里被问过,但正如我所说的,由于某种原因,我无法让它对我起作用,而其他大多数解决方案都是基于我无法起作用的解决方案,所以最后几天对我来说非常令人沮丧。这个解决方案(OutLog)包含在我下面的代码片段中,以防你们中的一个人看到我这方面的一个拙劣的实现 要记住的事情: 我在Windows7上做这个(嘟嘟,嘟嘟,嘟嘟) 2我正在使用eclipse,并从IDE内部运行它(duh,da,da,duh,DUUUUH:如果这些建议与命令行或IDE一起使用,那将非常方便) 3我真的只想在程序运行时将stdout和stderr的输出复制到小部件。一行一行地实现这一点将是一个梦想,但即使它在循环或其他事情的末尾以块的形式出现,那也太棒了 4哦,还有关于OutLog,有人能告诉我,如果self.out在init中设置为'None',这个类实际上可以工作吗?我的意思是,self.out总是一个非类型的对象,对吧 任何帮助都将不胜感激,即使它只是指向我可以找到更多信息的地方。我一直在尝试构建自己的解决方案(我有点像虐待狂),但我发现很难找到关于这些对象如何工作的相关信息 不管怎样,发牢骚吧。这是我的密码:Python 2.7 QTextEdit小部件中的重复stdout、stderr,python-2.7,pyqt,stdout,pyside,stderr,Python 2.7,Pyqt,Stdout,Pyside,Stderr,几天来,我的PySide程序一直有困难。我不认为这个问题是难以置信的困难,因为有答案。我的问题是,它们似乎都不适合我 我想在PySide程序运行时“侦听”文件对象stdout和stderr,并将内容输出到QText Edit小部件。现在,我已经意识到这个问题(或类似的问题)以前在这里被问过,但正如我所说的,由于某种原因,我无法让它对我起作用,而其他大多数解决方案都是基于我无法起作用的解决方案,所以最后几天对我来说非常令人沮丧。这个解决方案(OutLog)包含在我下面的代码片段中,以防你们中的一个
#!/usr/bin/env python
import sys
import logging
import system_utilities
log = logging.getLogger()
log.setLevel("DEBUG")
log.addHandler(system_utilities.SystemLogger())
import matplotlib
matplotlib.use("Qt4Agg")
matplotlib.rcParams["backend.qt4"] = "PySide"
import subprocess
import plot_widget
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PySide import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
"""This is the main window class and displays the primary UI when launched.
Inherits from QMainWindow.
"""
def __init__(self):
"""Init function.
"""
super(MainWindow, self).__init__()
self.x = None
self.y = None
self.data_plot = None
self.plot_layout = None
self.terminal = None
self.setup_plot()
self.setup_interface()
def setup_plot(self):
"""Member function to setup the graph window in the main UI.
"""
#Create a PlotWidget object
self.data_plot = plot_widget.PlotWidget()
#Create a BoxLayout element to hold PlotWidget
self.plot_layout = QtGui.QVBoxLayout()
self.plot_layout.addWidget(self.data_plot)
def setup_interface(self):
"""Member function to instantiate and build the composite elements of the
UI."""
#Main widget houses layout elements (Layout cannot be placed directly in a QMainWindow).
central_widget = QtGui.QWidget()
test_splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
button_splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
#UI BoxLayout elements
central_layout = QtGui.QVBoxLayout()
#button_layout = QtGui.QHBoxLayout()
#UI PushButton elements
exit_button = QtGui.QPushButton("Close")
run_button = QtGui.QPushButton("Run...")
#UI Text output
self.editor = QtGui.QTextEdit()
self.editor.setReadOnly(True)
self.terminal = QtGui.QTextBrowser()
self.terminal.setReadOnly(True)
#UI PushButton signals
run_button.clicked.connect(self.run_c_program)
run_button.clicked.connect(self.data_plot.redraw_plot)
exit_button.clicked.connect(QtCore.QCoreApplication.instance().quit)
#Build the UI from composite elements
central_layout.addLayout(self.plot_layout)
central_layout.addWidget(self.editor)
button_splitter.addWidget(run_button)
button_splitter.addWidget(exit_button)
test_splitter.addWidget(button_splitter)
test_splitter.addWidget(self.terminal)
test_splitter.setCollapsible(1, True)
central_layout.addWidget(test_splitter)
central_widget.setLayout(central_layout)
self.setCentralWidget(central_widget)
self.show()
class OutLog:
def __init__(self, edit, out=None, color=None):
"""(edit, out=None, color=None) -> can write stdout, stderr to a
QTextEdit.
edit = QTextEdit
out = alternate stream ( can be the original sys.stdout )
color = alternate color (i.e. color stderr a different color)
"""
self.edit = edit
self.out = None
self.color = color
def write(self, m):
if self.color:
tc = self.edit.textColor()
self.edit.setTextColor(self.color)
self.edit.moveCursor(QtGui.QTextCursor.End)
log.debug("this is m {}".format(m))
self.edit.insertPlainText( m )
if self.color:
self.edit.setTextColor(tc)
if self.out:
self.out.write(m)
def main():
app = QtGui.QApplication(sys.argv)
log.debug("Window starting.")
window = MainWindow()
sys.stdout = OutLog(window.terminal, sys.stdout)
sys.stderr = OutLog(window.terminal, sys.stderr, QtGui.QColor(255,0,0))
window.show()
sys.exit(app.exec_())
log.info("System shutdown.")
if __name__ == '__main__':
main()
“帮助我,欧比万……”
提前感谢各位(和女士:-)
self.out=None
可能是一个打字错误,应该是self.out=out
。这样,您也可以看到控制台中打印的任何内容。这是确保代码打印任何内容的第一步
下一件事是,您需要了解您正在重定向哪个输出。子进程有自己的stdio,因此对父进程stdout的重定向不会产生任何影响
用子流程纠正stdio并不是一件小事。我建议从
subprocess.communicate()
开始,它将所有输出作为单个字符串提供。这通常就足够了。self.out=None
可能是一个打字错误,应该是self.out=out
。这样,您也可以看到控制台中打印的任何内容。这是确保代码打印任何内容的第一步
下一件事是,您需要了解您正在重定向哪个输出。子进程有自己的stdio,因此对父进程stdout的重定向不会产生任何影响
用子流程纠正stdio并不是一件小事。我建议从
subprocess.communicate()
开始,它将所有输出作为单个字符串提供。这通常就足够了。似乎您所需要做的就是用一个包装器对象覆盖sys.stderr
和sys.stdout
,该对象在写入输出时发出信号
下面是一个演示脚本,它应该或多或少地满足您的需求。请注意,包装类不会从sys.\uu stdout\uuuu/sys.\uu stderr\uuuu
还原sys.stdout/sys.stderr
,因为后者可能与最初替换的对象不同
import sys
from PyQt4 import QtGui, QtCore
class OutputWrapper(QtCore.QObject):
outputWritten = QtCore.pyqtSignal(object, object)
def __init__(self, parent, stdout=True):
QtCore.QObject.__init__(self, parent)
if stdout:
self._stream = sys.stdout
sys.stdout = self
else:
self._stream = sys.stderr
sys.stderr = self
self._stdout = stdout
def write(self, text):
self._stream.write(text)
self.outputWritten.emit(text, self._stdout)
def __getattr__(self, name):
return getattr(self._stream, name)
def __del__(self):
try:
if self._stdout:
sys.stdout = self._stream
else:
sys.stderr = self._stream
except AttributeError:
pass
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
widget = QtGui.QWidget(self)
layout = QtGui.QVBoxLayout(widget)
self.setCentralWidget(widget)
self.terminal = QtGui.QTextBrowser(self)
self._err_color = QtCore.Qt.red
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout.addWidget(self.terminal)
layout.addWidget(self.button)
stdout = OutputWrapper(self, True)
stdout.outputWritten.connect(self.handleOutput)
stderr = OutputWrapper(self, False)
stderr.outputWritten.connect(self.handleOutput)
def handleOutput(self, text, stdout):
color = self.terminal.textColor()
self.terminal.setTextColor(color if stdout else self._err_color)
self.terminal.moveCursor(QtGui.QTextCursor.End)
self.terminal.insertPlainText(text)
self.terminal.setTextColor(color)
def handleButton(self):
if QtCore.QTime.currentTime().second() % 2:
print('Printing to stdout...')
else:
sys.stderr.write('Printing to stderr...\n')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 200)
window.show()
sys.exit(app.exec_())
NB:
应尽早创建OutputWrapper的实例,以确保需要
sys.stdout/sys.stderr
的其他模块(例如日志记录
模块)必要时使用包装版本。似乎您只需使用包装对象覆盖sys.stderr
和sys.stdout
,每当写入输出时,包装对象都会发出信号
下面是一个演示脚本,它应该或多或少地满足您的需求。请注意,包装类不会从sys.\uu stdout\uuuu/sys.\uu stderr\uuuu
还原sys.stdout/sys.stderr
,因为后者可能与最初替换的对象不同
import sys
from PyQt4 import QtGui, QtCore
class OutputWrapper(QtCore.QObject):
outputWritten = QtCore.pyqtSignal(object, object)
def __init__(self, parent, stdout=True):
QtCore.QObject.__init__(self, parent)
if stdout:
self._stream = sys.stdout
sys.stdout = self
else:
self._stream = sys.stderr
sys.stderr = self
self._stdout = stdout
def write(self, text):
self._stream.write(text)
self.outputWritten.emit(text, self._stdout)
def __getattr__(self, name):
return getattr(self._stream, name)
def __del__(self):
try:
if self._stdout:
sys.stdout = self._stream
else:
sys.stderr = self._stream
except AttributeError:
pass
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
widget = QtGui.QWidget(self)
layout = QtGui.QVBoxLayout(widget)
self.setCentralWidget(widget)
self.terminal = QtGui.QTextBrowser(self)
self._err_color = QtCore.Qt.red
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout.addWidget(self.terminal)
layout.addWidget(self.button)
stdout = OutputWrapper(self, True)
stdout.outputWritten.connect(self.handleOutput)
stderr = OutputWrapper(self, False)
stderr.outputWritten.connect(self.handleOutput)
def handleOutput(self, text, stdout):
color = self.terminal.textColor()
self.terminal.setTextColor(color if stdout else self._err_color)
self.terminal.moveCursor(QtGui.QTextCursor.End)
self.terminal.insertPlainText(text)
self.terminal.setTextColor(color)
def handleButton(self):
if QtCore.QTime.currentTime().second() % 2:
print('Printing to stdout...')
else:
sys.stderr.write('Printing to stderr...\n')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 200)
window.show()
sys.exit(app.exec_())
NB:
OutputWrapper的实例应该尽早创建,以确保需要
sys.stdout/sys.stderr
的其他模块(如日志记录
模块)在必要时使用包装版本。很清楚,在我运行此程序时,QTextEdit中没有显示我希望输出转到(self.terminal)的内容。再次感谢可能的重复,很明显,在我运行这个程序的时候,QTextEdit中没有显示我希望输出转到(self.terminal)的内容。再次感谢self.out上Hi Aaron可能的副本,是的,我也这么想,但是在互联网上使用这个代码(或派生代码)的地方,没有人指出这一点,所以我开始认为我遗漏了什么。关于第二点,我如何使用subprocess从运行PySide程序中捕获stdio?例如,我是否能够将sys.argv传递给subprocess.communicate()以侦听正在运行的程序的输出?(因为您需要传递命令行程序名,以及从子流程获取输出所需的参数,据我所知…subprocess.communicate()
将为您完成一切。当它返回时,您将以字符串形式获得子流程的输出,作为方法的返回值。请看一下子流程模块文档中的示例。我仍然有点困惑。如果使用communicate(),则执行以下操作:return=subprocess.Popen(system\u cmd),然后:return.communicate()。我不确定我有什么价值