通过GUI重定向stdin

时间:2014-03-17 03:18:31

标签: python pyqt pyqt4 stdin io-redirection

我有这个非常基本的应用程序,打算在一个textEdit组件上评估给定的Python代码,并在另一个组件上显示结果。

import sys                                                                                                                                                                                      
from PyQt4 import QtGui
from cStringIO import StringIO

class SampleGUI(QtGui.QWidget):
    def __init__(self):
        super(SampleGUI, self).__init__()
        self.initGUI()

    def initGUI(self):
        self.code = QtGui.QTextEdit()
        self.result = QtGui.QTextEdit()

        btn = QtGui.QPushButton('Evaluate')
        btn.clicked.connect(self.evaluate)

        vbox = QtGui.QVBoxLayout()
        vbox.addWidget(self.code)
        vbox.addWidget(btn)
        vbox.addWidget(self.result)

        self.setLayout(vbox)
        self.show()

    def evaluate(self):
        source_code = str(self.code.toPlainText())
        old_stdout = sys.stdout
        redirected_output = sys.stdout = StringIO()
        exec source_code
        sys.stdout = old_stdout
        self.result.setText(redirected_output.getvalue())

def main():
    app = QtGui.QApplication([])
    s = SampleGUI()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

到目前为止,这项工作效果很好,您不会要求输入值。假设您在控制台中使用python samplegui.py运行脚本,如果您碰巧在第一个文本框中输入了类似的内容:

a = 3
print 5 + a
b = input()
print 1 + b

然后按Evaluate,应用程序需要从控制台输入值 。输入一些值并按回车键后,它会继续并正确评估。

我想要实现的是将stdin重定向到我可以在GUI内部操作的组件。

我该怎么做?理想情况下,我想在Python解释器样式中执行此操作,其中输出和输入以交互方式发生。另一种选择可能是在应用程序等待输入并显示输入对话框时以某种方式处理/捕获信号。

修改

事实证明它比我想象的要容易。在这里,您可以看到使用sys.stdin拦截QInputDialog的脚本版本,以便用户可以从GUI输入值。

import sys 
from PyQt4 import QtGui
from cStringIO import StringIO

class InputGUI():
    def __init__(self, parentWidget):
        self.parentWidget = parentWidget

    def readline(self):
        text, ok = QtGui.QInputDialog.getText(self.parentWidget, 'Introduce value', 'Value:')
        if ok: 
            return str(text)
        else:
            return ''

class SampleGUI(QtGui.QWidget):
    def __init__(self):
        super(SampleGUI, self).__init__()
        self.initGUI()

    def initGUI(self):
        self.code = QtGui.QTextEdit()
        self.result = QtGui.QTextEdit()

        btn = QtGui.QPushButton('Evaluate')
        btn.clicked.connect(self.evaluate)

        vbox = QtGui.QVBoxLayout()
        vbox.addWidget(self.code)
        vbox.addWidget(btn)
        vbox.addWidget(self.result)

        self.setLayout(vbox)
        self.show()

    def evaluate(self):
        source_code = str(self.code.toPlainText())
        streams = sys.stdin, sys.stdout
        sys.stdin = InputGUI(self)                                                                                                                                                              
        redirected_output = sys.stdout = StringIO()
        exec source_code
        sys.stdin, sys.stdout = streams
        self.result.setText(redirected_output.getvalue())

def main():
    app = QtGui.QApplication([])
    s = SampleGUI()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()    

1 个答案:

答案 0 :(得分:2)

我将sys.stdin替换为实现文件描述符协议的自定义类的实例。然后,您可以简单地决定当用户想要通过input()sys.stdin.readline()阅读内容时会发生什么。

至于实现文件描述符:实现documentation中涵盖的所有方法。此外,您可能希望实施context manager protocol,因此您的自定义sys.stdin可以与with语句一起使用。

但是,请注意,如果用户再次替换sys.stdin,他们仍然可以用脚射击,但至少这似乎比调用input()的人更不可能。