2

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

K.Mulier
  • 8,069
  • 15
  • 79
  • 141
  • How are you executing the batch file? – Carson Myers Jul 20 '16 at 14:38
  • 1
    According to [the docs](https://docs.python.org/2/library/subprocess.html#subprocess.Popen.stdout), your `p` has a `.stdout` attribute, and you can read from it in a loop. The reads will block until some of the bat file output is available. This may be the way to do the real-time output updates you are looking for. – 9000 Jul 20 '16 at 15:45
  • So I should make some kind of loop that keeps reading from the p.stdout, and add the string to the text area? – K.Mulier Jul 20 '16 at 15:47
  • For the output problem, you can stream output from Popen using the process outlined [here](http://stackoverflow.com/questions/2715847/python-read-streaming-input-from-subprocess-communicate/17698359#17698359) – jyapayne Jul 20 '16 at 17:10
  • Please can you publish the PyQt5 terminal lib on Github? – SwimmingG Oct 19 '16 at 13:28

1 Answers1

1

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.

Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212