Can't Update QTextEdit While in a Multiprocess - python

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

Related

How to print console output in pyQt

I need to display adb logs in a Pyqt window, i tried like this. When i click the button log_display function is called, which set the console output to textBrowser. I tried to use subprocess but it didn't help, the window is just freezing and not responding. What is the way to do it? Maybe i need to use new thread for it?
def log_display(self):
result = subprocess.run('adb logcat *:I', stdout=subprocess.PIPE)
self.textBrowser.setText(subprocess.run('result.stdout'))
You have to use QProcess since it launches the application but does not block the GUI since it notifies the log through signals:
from PyQt5 import QtCore, QtGui, QtWidgets
class LogView(QtWidgets.QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent)
self.setReadOnly(True)
self._process = QtCore.QProcess()
self._process.readyReadStandardOutput.connect(self.handle_stdout)
self._process.readyReadStandardError.connect(self.handle_stderr)
def start_log(self, program, arguments=None):
if arguments is None:
arguments = []
self._process.start(program, arguments)
def add_log(self, message):
self.appendPlainText(message.rstrip())
def handle_stdout(self):
message = self._process.readAllStandardOutput().data().decode()
self.add_log(message)
def handle_stderr(self):
message = self._process.readAllStandardError().data().decode()
self.add_log(message)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = LogView()
w.resize(640, 480)
w.show()
w.start_log("adb", ["logcat", "*:I"])
sys.exit(app.exec_())

PyQt program running from command line not routing the console output in real time

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.

PyQt4: QProcess readyRead does not always emit correctly

When I use pyqt to run a program I cannot get the output correctly every time.
Here is an example:
from PyQt4 import QtCore, QtGui
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QWidget.__init__(self)
program = "ping"
self.process = QtCore.QProcess()
self.process.readyRead.connect(self.readoutput)
self.process.start(program)
def readoutput(self):
print str(self.process.readAll())
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
In this case, the output is the helper of the ping command, as I would expect. Although if I change the program variable to some other value it doesn't always work, for example if i do:
program = "pyinstaller"
it does not print the helper of pyinstaller as it happens in the console.
How am I supposed to get the output in this case?
pyinstaller might be printing to stderr instead of stdout. You can cause QProcess.readAll() to return both outputs by calling (before self.process.start(program))
setProcessChannelMode(QProcess.MergedChannels)

PyQt button start another script from another file

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

interactive python - keeping console interactive with a GUI mainloop

I am wondering how one would create a GUI application, and interact with it from the console that started it.
As an example, I would like to create a GUI in PyQt and work with it from the console. This could be for testing settings without restarting the app, but in larger projects also for calling functions etc.
Here is a simple example using PyQt:
import sys
from PyQt4 import QtGui
def main():
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
when this is run with python -i example.py the console is blocked as long as the main-loop is executed.
How can I call w.resize(100,100) while the GUI is running?
ops, posted wrong answer before
there is a post in Stack about that
Execute Python code from within PyQt event loop
The following example uses the code module to run a console in the command prompt (be sure to run the script from the command line). Subclassing QThread provides a route by which the console can be run in a separate thread from that of the main GUI and enables some interaction with it. The stub example below should be easy enough to incorporate into a larger packaged PyQt program.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import threading #used only to id the active thread
import code
import sys
class Worker(QThread): #Subclass QThread and re-define run()
signal = pyqtSignal()
def __init__(self):
super().__init__()
def raise_sys_exit(self): #more gracefully exit the console
print('(Deactivated Console)')
raise SystemExit
def setup_console(self,global_dict):
console_exit = {'exit': self.raise_sys_exit}
self.console = code.InteractiveConsole(locals=dict(global_dict,**console_exit))
def run(self):
try:
print('worker', threading.get_ident())
self.console.interact()
except SystemExit:
self.signal.emit()
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args,**kwargs)
self.data = [1,2,3,4] #some data we might want to look at
layout = QVBoxLayout()
self.b = QPushButton("Interact")
self.b.clicked.connect(self.b_clicked)
layout.addWidget(self.b)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.worker = Worker()
self.worker.signal.connect(self.finished)
def finished(self):
self.b.setEnabled(True)
def b_clicked(self):
print('main',threading.get_ident())
self.worker.setup_console(globals()) #pass the global variables to the worker
self.worker.start()
self.b.setEnabled(False) #disable the GUI button until console is exited
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
The easiest way is to use IPython:
ipython --gui=qt4
See ipython --help or the online documentation for more options (e.g. gtk, tk, etc).

Categories