I have a PyQt5 GUI application and would like to execute an external program and display stdout, stderr and stdin of the external program on a QTextEdit widget. I have managed to do so for stdout and stderr. I need help for the stdin of the external process.
Imagine the following snippets
self.te = QTextEdit(self)
self.te.move(self.x0, 150)
self.te.resize(self.mainWinWidth - 100, self.mainWinHeight - 200)
And the snippet to get QProcess going ...
self.process = QtCore.QProcess(self)
self.process.setProcessChannelMode( QProcess.MergedChannels )
self.process.readyRead.connect(self.readReady)
# ... and elsewhere I start the sub process like
os.environ["PYTHONUNBUFFERED"] = "1"
self.process.start('./goo', [])
and readReady() implemented as:
def readReady(self):
cursor = self.te.textCursor()
cursor.movePosition(cursor.End)
cursor.insertText(str(self.process.readAll(), 'utf-8'))
self.te.ensureCursorVisible()
And goo(1) is a basic sub process implemented as
#!/usr/bin/python3
import time
import sys
for i in range(0,5):
print(f"============ {i} ===")
time.sleep(1)
sys.stderr.write("Testing stderr...\n")
print("Enter name:")
name = sys.stdin.readline()
print(f"Got {name}")
With that said, I do see stdout and stderr all working ok. I also see "Enter name:" but when I enter "joe" or "moe" on the QTextEdit, nothing happens, ie the back end sub process is still waiting.
So it seems like I need an event handler for the write. That is when the sub-process (via QProcess) waits for input on its stdin, I need to detect that and somehow read that from QTextEdit (from the user) and then feed that to the sub process (i.e write to its stdin).
Think ssh or telnet or xterm. Isn't there any on the shelf widget for this ?
You have to use the write() method of QProcess:
├── goo
└── main.py
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self._textedit = QtWidgets.QTextEdit(readOnly=True)
self._lineedit = QtWidgets.QLineEdit()
self._pushbutton = QtWidgets.QPushButton("Send")
self._pushbutton.clicked.connect(self.on_clicked)
lay = QtWidgets.QGridLayout(self)
lay.addWidget(self._textedit, 0, 0, 1, 2)
lay.addWidget(self._lineedit, 1, 0)
lay.addWidget(self._pushbutton, 1, 1)
self._process = QtCore.QProcess(self)
self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self._process.readyRead.connect(self.on_readReady)
current_dir = os.path.dirname(os.path.realpath(__file__))
self._process.start(os.path.join(current_dir, "goo"))
#QtCore.pyqtSlot()
def on_readReady(self):
cursor = self._textedit.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(str(self._process.readAll(), "utf-8"))
self._textedit.ensureCursorVisible()
#QtCore.pyqtSlot()
def on_clicked(self):
text = self._lineedit.text() + "\n"
self._process.write(text.encode())
if __name__ == "__main__":
os.environ["PYTHONUNBUFFERED"] = "1"
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Related
I am using QProcess to run a typical cli command such as 'ping' or 'netstat'. I would like it to run continuously until I tell it to stop. I've provided a cut down version of my code below that uses the "ping" command. If I run the command "ping -t 192.168.0.1 > test.txt" from a cmd prompt it works fine, but when I try to run this in my program below, it produces the error "Bad parameter > test.txt."
It appears that saving the output of cli commands does not count as a recognized argument/parameter for QProcess (the rest of the code works fine as far as I can tell).
from PyQt5 import QtCore, QtGui, QtWidgets
import os
import psutil
## Define origin path.
scriptpath = os.path.realpath(__file__)
scriptpath = scriptpath.replace(os.path.basename(__file__), "")
os.chdir(scriptpath)
origin = os.getcwd()
## Establish UI and interactions.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# event actions
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
process_btn = QtWidgets.QPushButton("process")
process_btn.clicked.connect(self.process)
end_btn = QtWidgets.QPushButton("end")
end_btn.clicked.connect(self.end)
lay.addWidget(process_btn)
lay.addWidget(end_btn)
self.process = QtCore.QProcess(self)
self._pid = -1
#QtCore.pyqtSlot()
def process(self):
program = 'ping'
arguments = ['-t', '192.168.0.1', '> test.txt'] # Ping 10 times to the router address and save output.
self.process.setProgram(program)
self.process.setArguments(arguments)
ok, pid = self.process.startDetached()
if ok:
self._pid = pid
#QtCore.pyqtSlot()
def end(self):
if self._pid > 0:
p = psutil.Process(self._pid)
p.terminate()
self._pid = -1
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Is there a way to save the output of cli commands run through QProcess to a text file?
Note: I have tried to implement the same feature with subprocess.call('ping -t 192.168.0.1 > test.txt', shell=True) but then I ran into issues with being able to stop the ping. The only way I could stop it was to use the exit command in the cli from where my PyQt5 program was run (closing the app just left the text file continuing to update), which isn't ideal for a program with a GUI. If anyone has a solution to this then perhaps I could go back to it.
Use QProcess setStandardOutputFile method to save process's result into a file.
I am trying to tail a file and output it continuously to a QTextEdit box. However, I have my subprocess and output located within a multiprocess. Here is my code:
shouldRun = True
wMain = QtGui.QWidget()
textboxSideA = QtGui.QTextEdit(wMain)
def tailLog():
subA = subprocess.Popen(["tail", "-F", "<FILENAME>", stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid)
pollA = select.poll()
pollA.register(subA.stdout)
while shouldRun:
if pollA.poll(1):
textboxSideA.append(subA.stdout.readline())
subA.kill()
os.killpg(subA.pid, signal.SIGKILL)
return
processSideA = multiprocessing.Process(target = tailLog)
processSideA.start()
wMain.show()
when the textboxSideA.append is called, the textbox doesn't show anything. I have tried just appending a direct string to it just to make sure it wasn't my readline that was bad. However, that wasn't the issue. I then tried to print out my readline directly to the terminal using print(subA.stdout.readline()) which has worked fine. So I concluded that the QTextEdit textbox GUI isn't being updated. I have tried everything and not even Google has given me an answer.
Also, I can type in the textbox and that shows up properly, and I can save what I have typed. My GUI just doesn't seem to like the multiprocess as I can call .append() outside of the multiprocess and it works just fine.
Qt does not support multiprocessing so it is dangerous to update the GUI from another process, the GUI can only and should be updated from the thread of the process where it was created.
On the other hand in this case it is not necessary to use multiprocessing since you can use QProcess:
import sys
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.process = QtCore.QProcess(self)
self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self.process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
self.textedit = QtGui.QTextEdit()
self.setCentralWidget(self.textedit)
def tail(self, filename):
self.process.kill()
self.process.start("tail", ["-F", filename])
#QtCore.pyqtSlot()
def on_readyReadStandardOutput(self):
msg = self.process.readAllStandardOutput().data().encode()
self.textedit.append(msg)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.tail("<FILENAME>")
w.show()
sys.exit(app.exec_())
Hi I am a newbie in PyQt.
Can someone suggest why the below program is not populating the stdout of print.py in real time to the textbox widget.This problem exists when it is running from command line.If we are running the samecode in pycharm we will get realtime outputs. Command line execution will show the data as a chunk(approx 4K) to the text box.
from PyQt4 import QtGui,QtCore
import sys
class gui(QtGui.QMainWindow):
def __init__(self):
super(gui, self).__init__()
self.initUI()
def dataReady(self):
cursor = self.output.textCursor()
cursor.movePosition(cursor.End)
cursor.insertText(str(self.process.readAll()))
self.output.ensureCursorVisible()
def initUI(self):
# Layout are better for placing widgets
layout = QtGui.QHBoxLayout()
self.output = QtGui.QTextEdit()
layout.addWidget(self.output)
centralWidget = QtGui.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
# QProcess object for external app
self.process = QtCore.QProcess(self)
# QProcess emits `readyRead` when there is data to be read
self.process.readyRead.connect(self.dataReady)
# Run program
self.process.start('python', ['./print.py'])
#Function Main Start
def main():
app = QtGui.QApplication(sys.argv)
ui=gui()
ui.show()
sys.exit(app.exec_())
#Function Main END
if __name__ == '__main__':
main()
Here is the code for print.py
import time
n=10
while(n):
print "Hello world"
time.sleep(1)
n = n-1
You should explicitly flush the output buffer of the print.py script (the child script) running in the subprocess. The default behaviour of print is to send the output to standard out (stdout). As such, you should explicitly call sys.sydout.flush() in the child script, after each print statement, to ensure the content of the print statements from the child script are made available to the parent script in a timely manner.
How can i start a separate script in a separate file using PyQt like a button signal or something.
from everywhere* import everthing*
def bJeff(driver):
...
def bLexi(driver):
...
def browser(url, id, user1, parolauser1, user2, parolauser2):
...
#starting 2 browsers and passing them for further processing
bLexi(driver)
...
bJeff(driver)
return
if __name__ == '__main__':
jobs = []
user_Lexi = "user1#mail.com"
parola_user_Lexi = "pass1"
user_Jeff = "user#mail.com"
parola_user_Jeff = "pass2"
sites = ["http://www.didi.com", "http://www.didi.com/"]
id = 0
for pagina in sites:
p = multiprocessing.Process(target=browser, args=(pagina, id, user_Lexi, parola_user_Lexi, user_Jeff, parola_user_Jeff))
id+=1
jobs.append(p)
p.start()
I read and saw how can i make a button but i didn't saw how can i start an outside function from that button.
Edit
I did it like this:
import os, sys, subprocess, filetoinclude
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import PyQt4.QtCore as QtCore
import PyQt4.QtGui as QtGui
class QButton(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
# create objects
self.name='me'
label = QLabel(self.tr("Enter command and press Return"))
self.le = QLineEdit()
self.te = QTextEdit()
self.button = QtGui.QPushButton('Button', self)
# layout
layout = QVBoxLayout(self)
layout.addWidget(label)
layout.addWidget(self.le)
layout.addWidget(self.button)
layout.addWidget(self.te)
self.setLayout(layout)
# create connection
self.button.clicked.connect(self.calluser)
self.connect(self.le, SIGNAL("returnPressed(void)"), self.run_command)
def calluser(self):
print(self.name)
filetoinclude.dadada()
def run_command(self):
cmd = str(self.le.text())
result = self.system_call(cmd)
self.te.setText(result)
def system_call(self, command):
p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
return p.stdout.read()
def demo_QButton():
app = QtGui.QApplication(sys.argv)
tb = QButton()
tb.show()
app.exec_()
if __name__=='__main__':
demo_QButton()
And i renamed the function from the file instead of
this: if __name__ == '__main__':<---
to this: --->def dadada():
Thank you very much everyone, i knew i will get the right answers here, as always.
You can do it by exactly that: using signals and slots. Signals can be emitted and received from anywhere and are thread safe. You can import your foreign script/function/whatever you need to run in the separate file, and connect your button's clicked() signal to that function. For example, if you need to run a script called myScript() when the button is clicked:
import myScript
...
self.myButton.clicked.connect(self.run_myScript)
def run_myScript(self):
myScript()
When the button is clicked, it'll run myScript. If you want to run it in a separate thread, you can do it like you've been doing.
There are 2 ways to do this.
import file and call the function as file.functionName(). Highly recommended. Note that if your file is called file.py, your import should not include the .py extension at the end.
Using a shell process: os.system('python file.py'). You need to import os for this one.
#If your are not expecting this answer, sorry.
def button_OutsideScript (self) :
import OutsideScript_Name as osScript
reload (osScript)
#If you have class inside the Outside Script use below line
osScript.className ()
Hi I have seen there are already many questions on this issue however none of them seems to answer my query .
As per below link i even tried winpexpect as i am using windows , however it dosent seems to e working for me .
Getting realtime output from ffmpeg to be used in progress bar (PyQt4, stdout)
I am running a subprogram with subprocess.Popen and want to see the real time result in a pyQt Widget. Currently it shows the result in the pyQt widget but only after the sub command is finished executing . I need to know if there s a way when we can get the output from a subprocess at real time into the window . See the code below which i tried for all this .
import sys
import os
from PyQt4 import QtGui,QtCore
from threading import Thread
import subprocess
#from winpexpect import winspawn
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class gui(QtGui.QMainWindow):
def __init__(self):
# ...
super(gui, self).__init__()
# Install the custom output stream
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
self.initUI()
def normalOutputWritten(self, text):
cursor = self.textEdit.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(text)
self.textEdit.ensureCursorVisible()
def callProgram(self):
command="ping 127.0.0.1"
#winspawn(command)
py=subprocess.Popen(command.split(),stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
result,_=py.communicate()
for line in result:
print line
print result
def initUI(self):
self.setGeometry(100,100,300,300)
self.show()
self.textEdit=QtGui.QTextEdit(self)
self.textEdit.show()
self.textEdit.setGeometry(20,40,200,200)
print "changing sys.out"
print "hello"
thread = Thread(target = self.callProgram)
thread.start()
#Function Main Start
def main():
app = QtGui.QApplication(sys.argv)
ui=gui()
sys.exit(app.exec_())
#Function Main END
if __name__ == '__main__':
main()
QProcess is very similar to subprocess, but it's much more convenient to use in (Py)Qt code. Because it utilizes signals/slots. Also, it runs the process asynchronously so you don't have use QThread.
I've modified (and cleaned) your code for QProcess:
import sys
from PyQt4 import QtGui,QtCore
class gui(QtGui.QMainWindow):
def __init__(self):
super(gui, self).__init__()
self.initUI()
def dataReady(self):
cursor = self.output.textCursor()
cursor.movePosition(cursor.End)
cursor.insertText(str(self.process.readAll()))
self.output.ensureCursorVisible()
def callProgram(self):
# run the process
# `start` takes the exec and a list of arguments
self.process.start('ping',['127.0.0.1'])
def initUI(self):
# Layout are better for placing widgets
layout = QtGui.QHBoxLayout()
self.runButton = QtGui.QPushButton('Run')
self.runButton.clicked.connect(self.callProgram)
self.output = QtGui.QTextEdit()
layout.addWidget(self.output)
layout.addWidget(self.runButton)
centralWidget = QtGui.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
# QProcess object for external app
self.process = QtCore.QProcess(self)
# QProcess emits `readyRead` when there is data to be read
self.process.readyRead.connect(self.dataReady)
# Just to prevent accidentally running multiple times
# Disable the button when process starts, and enable it when it finishes
self.process.started.connect(lambda: self.runButton.setEnabled(False))
self.process.finished.connect(lambda: self.runButton.setEnabled(True))
#Function Main Start
def main():
app = QtGui.QApplication(sys.argv)
ui=gui()
ui.show()
sys.exit(app.exec_())
#Function Main END
if __name__ == '__main__':
main()
Here is an adaptation of the accepted answer for PyQt6 and PyQt5.
PyQt6:
from PyQt6 import QtCore, QtWidgets
import sys
# On Windows it looks like cp850 is used for my console. We need it to decode the QByteArray correctly.
# Based on https://forum.qt.io/topic/85064/qbytearray-to-string/2
import ctypes
CP_console = f"cp{ctypes.cdll.kernel32.GetConsoleOutputCP()}"
class gui(QtWidgets.QMainWindow):
def __init__(self):
super(gui, self).__init__()
self.initUI()
def dataReady(self):
cursor = self.output.textCursor()
cursor.movePosition(cursor.MoveOperation.End)
# Here we have to decode the QByteArray
cursor.insertText(
str(self.process.readAll().data().decode(CP_console)))
self.output.ensureCursorVisible()
def callProgram(self):
# run the process
# `start` takes the exec and a list of arguments
self.process.start('ping', ['127.0.0.1'])
def initUI(self):
# Layout are better for placing widgets
layout = QtWidgets.QVBoxLayout()
self.runButton = QtWidgets.QPushButton('Run')
self.runButton.clicked.connect(self.callProgram)
self.output = QtWidgets.QTextEdit()
layout.addWidget(self.output)
layout.addWidget(self.runButton)
centralWidget = QtWidgets.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
# QProcess object for external app
self.process = QtCore.QProcess(self)
# QProcess emits `readyRead` when there is data to be read
self.process.readyRead.connect(self.dataReady)
# Just to prevent accidentally running multiple times
# Disable the button when process starts, and enable it when it finishes
self.process.started.connect(lambda: self.runButton.setEnabled(False))
self.process.finished.connect(lambda: self.runButton.setEnabled(True))
# Function Main Start
def main():
app = QtWidgets.QApplication(sys.argv)
ui = gui()
ui.show()
sys.exit(app.exec())
# Function Main END
if __name__ == '__main__':
main()
PyQt5:
from PyQt5 import QtCore, QtWidgets
import sys
# On Windows it looks like cp850 is used for my console. We need it to decode the QByteArray correctly.
# Based on https://forum.qt.io/topic/85064/qbytearray-to-string/2
import ctypes
CP_console = f"cp{ctypes.cdll.kernel32.GetConsoleOutputCP()}"
class gui(QtWidgets.QMainWindow):
def __init__(self):
super(gui, self).__init__()
self.initUI()
def dataReady(self):
cursor = self.output.textCursor()
cursor.movePosition(cursor.End)
# Here we have to decode the QByteArray
cursor.insertText(
str(self.process.readAll().data().decode(CP_console)))
self.output.ensureCursorVisible()
def callProgram(self):
# run the process
# `start` takes the exec and a list of arguments
self.process.start('ping', ['127.0.0.1'])
def initUI(self):
# Layout are better for placing widgets
layout = QtWidgets.QVBoxLayout()
self.runButton = QtWidgets.QPushButton('Run')
self.runButton.clicked.connect(self.callProgram)
self.output = QtWidgets.QTextEdit()
layout.addWidget(self.output)
layout.addWidget(self.runButton)
centralWidget = QtWidgets.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
# QProcess object for external app
self.process = QtCore.QProcess(self)
# QProcess emits `readyRead` when there is data to be read
self.process.readyRead.connect(self.dataReady)
# Just to prevent accidentally running multiple times
# Disable the button when process starts, and enable it when it finishes
self.process.started.connect(lambda: self.runButton.setEnabled(False))
self.process.finished.connect(lambda: self.runButton.setEnabled(True))
# Function Main Start
def main():
app = QtWidgets.QApplication(sys.argv)
ui = gui()
ui.show()
sys.exit(app.exec_())
# Function Main END
if __name__ == '__main__':
main()