1

ここ数日間、PySide プログラムで問題が発生しています。そこに答えがあるので、問題が信じられないほど難しいとは思いません。私が抱えている問題は、どれもうまくいかないようです。

PySide プログラムの実行中に、ファイル オブジェクトの stdout および stderr を「リッスン」し、コンテンツを QText Edit ウィジェットに出力したいと考えています。今、私はすでにこの質問(または同様のもの)がここで以前に尋ねられたことを認識していますが、私が言ったように、何らかの理由でそれを機能させることができず、そこにある他のほとんどの解決策は私ができるものに基づいています。仕事が始まらないので、ここ数日はとてもイライラしました。このソリューション (OutLog) は、以下のコード スニペットに含まれています。

覚えておくべきこと:

1 私は Windows 7 でこれをやっています (duuuh, da, da, duh)

2 私は Eclipse を使用しており、IDE 内から実行しています (duh、da、da、duh、DUUUUH: 提案がコマンドラインまたは IDE のいずれかで機能する場合、それは非常に便利です)

3 プログラムの実行中に、stdout と stderr の出力をウィジェットに複製したいだけです。これが 1 行ずつ行われるのは夢のような話ですが、ループの最後などですべてがチャンクとして出てきたとしても、それはすばらしいことです。

4 ああ、また OutLog に関して、 initでself.out が 'None' に設定されている場合、このクラスが実際に機能する方法を教えてもらえますか? つまり、self.out は常にNoneType オブジェクトですよね?

詳細情報を見つけることができる場所への単なるポインタであっても、助けていただければ幸いです。私は独自のソリューションを構築しようとしてきました (私は少しサディストです) が、これらのオブジェクトがそれを行うためにどのように機能するかについての関連情報を見つけるのが難しいことがわかりました。

とにかく、泣き言を言う。これが私のコードです:

#!/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()

「オビワン助けて…」

前もってありがとうみんな(そしてギャル:-))

4

2 に答える 2

6

あなたがする必要があるのは、オーバーライドsys.stderrsys.stdout、出力が書き込まれるたびにシグナルを発するラッパーオブジェクトを使用することだけのようです。

以下は、多かれ少なかれ必要なことを行うデモ スクリプトです。後者のオブジェクトは最初に置き換えられたものと同じではない可能性があるため、ラッパー クラスはsys.stdout/sys.stderrから復元しないことに注意してください。sys.__stdout__/sys.__stderr__

PyQt5 :

import sys
from PyQt5 import QtWidgets, QtGui, QtCore

class OutputWrapper(QtCore.QObject):
    outputWritten = QtCore.pyqtSignal(object, object)

    def __init__(self, parent, stdout=True):
        super().__init__(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(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__(   )
        widget = QtWidgets.QWidget(self)
        layout = QtWidgets.QVBoxLayout(widget)
        self.setCentralWidget(widget)
        self.terminal = QtWidgets.QTextBrowser(self)
        self._err_color = QtCore.Qt.red
        self.button = QtWidgets.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.moveCursor(QtGui.QTextCursor.End)
        self.terminal.setTextColor(color if stdout else self._err_color)
        self.terminal.insertPlainText(text)
        self.terminal.setTextColor(color)

    def handleButton(self):
        if QtCore.QTime.currentTime().second() % 2:
            print('Printing to stdout...')
        else:
            print('Printing to stderr...', file=sys.stderr)

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(500, 300, 300, 200)
    window.show()
    sys.exit(app.exec_())

PyQt4 :

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.moveCursor(QtGui.QTextCursor.End)
        self.terminal.setTextColor(color if stdout else self._err_color)
        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_())

:

OutputWrapper のインスタンスは、必要に応じてsys.stdout/sys.stderr(loggingモジュールなどの) 必要な他のモジュールがラップされたバージョンを使用できるように、できるだけ早く作成する必要があります。

于 2013-11-09T19:21:56.950 に答える
0

self.out = Noneはおそらくタイプミスであり、そうあるべきですself.out = out。そうすれば、コンソールに出力されたものもすべて見ることができます。これは、コードが何かを出力することを確認するための最初のステップです。

次に、どの出力をリダイレクトしているかを認識する必要があります。サブプロセスは独自の stdio を取得するため、親の stdout のリダイレクト量は影響しません。

サブプロセスでstdioを正しくすることは簡単ではありません。subprocess.communicate()すべての出力を単一の文字列として提供するものから始めることをお勧めします。通常はそれで十分です。

于 2013-11-08T09:27:15.010 に答える