K.Mulier
K.Mulier

Reputation: 9620

How to make a terminal console (terminal emulator) in PyQt5?

I am building an application in Python, with a graphical user interface in PyQt5. I need to insert some sort of "Terminal Console" in the application. The user can start a batch file by clicking a button, and should see the output appear in a text field.

At this moment, I use the following approach. When the user presses a button, the program will start the batch script (say "myBat.bat"). The standard output gets extracted and written to a QTextEdit widget.

enter image description here

This works great, but there are two serious problems.


(PROBLEM 1) The output is shown at the end of the batch execution..

And that's sometimes really painful. Especially if the bat-file takes some time to execute, the user will get the impression that everything freezes.

(PROBLEM 2) The user cannot give commands..

Some bat-files require user input. I have no idea on how to do this.

(PROBLEM 3) When the batch file is finished, it's over..

Sometimes the user wants to keep giving commands to the terminal, even when the batch file has finished. For example a simple dir command, to list out the files in a directory. Anything should be possible.

To summarise everything, I just need to make a functional terminal console inside my application.

There is a guy who ported QTermWidget to PyQt4. This is the link: https://sourceforge.net/projects/qtermwidget/?source=typ_redirect . Unfortunately his code is not compiled for Windows (I'm working on a Windows 10 machine). And his port is made for PyQt4. My entire application is written in PyQt5. There are reasons why I cannot go back to PyQt4.

Another guy made this software: https://bitbucket.org/henning/pyqtermwidget/overview . Also very interesting, but no Windows support.


Please help..

EDIT :

This is the code I'm currently running:

    ###############################################
    ### This function gets called when the user ###
    ### pushes the button                       ###
    ###############################################

    def myBatFunction(self):

        # 1. Call the proper batch file
        p = Popen("C:\\..\\myFolder\\myBat.bat" , cwd=r"C:\\..\\myFolder", stdout = subprocess.PIPE, stderr = subprocess.PIPE)
        stdout, stderr = p.communicate()
        p.wait()
        if p.returncode == 0:
            pass
        else:
            return

        # 2. Capture the standard out stream from the batch file
        msg = stdout.decode("utf-8")
        errMsg = stderr.decode("utf-8")

        self.myTextArea.setText(msg + errMsg)

    ###############################################

EDIT : If you think this is a duplicate question, please verify first if the other question offers a solution to Windows 10 users, and works with PyQt5 :-)

Upvotes: 2

Views: 3598

Answers (1)

Kirill V. Lyadvinsky
Kirill V. Lyadvinsky

Reputation: 99565

In your code p.wait() is the point of synchronization with the opened process. After that line the external process is finished. So you need to read p.stout in a loop before that line. Check the following example:

#!/usr/bin/env python2
from subprocess import Popen, PIPE

p = Popen(["C:\\..\\myFolder\\myBat.bat"], stdout=PIPE, bufsize=1)
with p.stdout:
    for line in iter(p.stdout.readline, b''):
        print line,
p.wait() # wait for the subprocess to exit

Note that bufsize should be 1 to suppress buffering.

Upvotes: 1

Related Questions