Question

I have this very basic application that intends to evaluate given Python code on one textEdit component and show results on the other one.

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()

This works well so far you don't ask for input values. Say you run the script from the console with python samplegui.py, if you happen to type something like this in the first text box:

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

and then push Evaluate, the application expects a value input from the console. After put some value and press enter it continues and evaluates correctly.

What a I want to achieve is to redirect the stdin to a component that I can manipulate from within the GUI itself.

How can I do that? Ideally I would like to do it in the Python interpreter style, where the output and input happens interactively. Another option could be to handle/capture somehow the signal when the application is waiting for input and show an input dialog.

EDIT:

It turned out that it was easier than I thought. Here you can see a version of the script with sys.stdin intercepted with a QInputDialog so the user can input values from 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()    
Was it helpful?

Solution

I'd replace sys.stdin with a an instance of a custom class that implements the file descriptor protocol. You can then simply decide what should happen when a user wants to read something via input() or sys.stdin.readline().

As for implementing a file descriptor: implement all the methods covered in the documentation. Additionally, you might want to implement the context manager protocol, so your custom sys.stdin can be used with a with statement.

Note, however, that users still can shoot themselves in the foot if they replace sys.stdin themselves again, but at least that seems more unlikely than someone calling input().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top