user1976336
user1976336

Reputation: 217

Terminal like app in PySide

I am making an application in PySide and I want to add a Console/Terminal like screen, where you have a prompt and you can type commands. How would I be able to accomplish this. I am guessing some combination of QPlainTextEdit/QTextEdit for the output and QLineEdit for the actual prompt. Is there a better way to do this?

Upvotes: 4

Views: 5063

Answers (2)

justengel
justengel

Reputation: 6320

I did this with a custom QPlainTextEdit and custom QLineEdit. I also had an indicator label that would display ">>>" on the terminal to show user input. It needs more work. The best way would be to create your own custom widget based off of a QTextEdit and your own io handler. Below is an example of my execute method with self.input being the QLineEdit and self.view being the QTextEdit. It should get you the general idea.

import io, subprocess, shlex, code, rlcompleter, platform

def execute(self, currentText=None):
    """Execute runs the command given based on the console type.

    If the console type is "both" then execute will run python commands 
    unless the user give the input ::os::command or ("::(platform.syste())::command").
    Otherwise the console type will determine the what the input will execute with.

    Args:
        currentText(str): Text to execute. None will run the text from the QLineEdit self.input.
    """
    # Check for given text
    if currentText is None:
        currentText = self.input.text()
        self.input.clear()
        self.view.display(currentText, "input")
    else:
        cmd = re.search("^>>>", currentText) # search for start only 
        if cmd is None:
            currentText = ">>>" + currentText
        else:
            self.view.display(currentText, "input")
    # end

    # Variables
    self.completer.clear()
    cmdString = re.sub("^>>>", "", currentText)
    result = None
    displayType = "output"
    run, cmdString = self.getRunType(cmdString)

    try:
        # Check where the output is going
        sys.stdout = self.out = io.StringIO()
        sys.stderr = sys.stdout

        if run == "python": # Run python command
            result = self._runInterpreter(cmdString)
            displayType = "python"

        elif run == "os": # Run os command
            result = self._runSubprocess(cmdString)
            displayType = "os"
    except Exception as err:
        result = str(err)
        displayType = "Error"

        notFoundPython = "NameError:" in result and "is not defined" in result 
        notFoundWindows = "returned non-zero exit status" in result
        if notFoundPython or notFoundWindows:
            result = "Command not found"
    finally:
        sys.stdout = self.old_stdout
        sys.stderr = self.old_stdout

        self.display(result, displayType)
# end execute

def getRunType(self, cmdString):
    run = self._consoleType

    # Check the run type
    if self._consoleType == "both":
        if re.search("^::python::", cmdString) is not None:
            cmdString = re.sub("^::[a-z]*::", "", cmdString)
            run = "python"

        elif re.search("^(::os::|::"+platform.system()+"::)", cmdString) is not None:
            cmdString = re.sub("^::[a-z]*::", "", cmdString)
            run = "os"
        else:
            run = "python"
    # end

    return run, cmdString
# end getRunType

def _runInterpreter(self, cmdString, outIO=None, run=None):
    # Check for a proper console type
    if(self._consoleType != "both" and self._consoleType != "python"):
        return

    # Get the IO
    if outIO is None:
        outIO = sys.stdout

    # Run python command       

    self.interpreter.push(cmdString)

    # Check outIO
    result = "Unreadable buffer: Check python's sys.stdout"
    if isinstance(outIO, io.StringIO):
        result = outIO.getvalue()
    else:
        if outIO.readable():
            result = str(outIO.readlines())

    # Check for error
    if re.search("^Traceback", result) or re.search("[a-zA-z]*Error:", result):
        raise ValueError(result)

    return result
# end _runInterpreter

def _runSubprocess(self, cmdString, run=None):
    # Check for a proper console type
    if(self._consoleType != "both" and self._consoleType != "os"):
        return

    # Run OS command
    cmd = shlex.split(cmdString)
    result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT).decode("utf-8")

    # Check for error
    if re.search("^Traceback", result) or re.search("[a-zA-z]*Error:", result):
        raise ValueError(result)

    return result
# end _runSubprocess

Upvotes: 2

Garrett Berg
Garrett Berg

Reputation: 2635

You could check out Spyder. They use PyQt (which is similar) and have a terminal. I think you can import their terminal widget, but I haven't played with it.

https://code.google.com/p/spyderlib/

Also, it is my favorite python editor by far!

I've spent a fair deal of time trying to find something like this, but to no avail. Good luck!

Upvotes: 2

Related Questions