RealTime output from a subprogram to a pyQT4 Widget - python-2.7

I'm trying to redirect the stdout from a subprogram to the QTextBrowser widget. (Python 2.7, Window 7, pyQT4)
This is the sub program, it will be in an executable file:
#test.py
import time
print ("ABC")
time.sleep(1)
print ("DEF")
time.sleep(1)
print ("GHI")
time.sleep(1)
print ("JKL")
My pyQT4 program:
from PyQt4 import QtGui
import subprocess, time, os, sys
from subprocess import Popen, PIPE
class GUI (QtGui.QWidget):
def __init__(self):
...
self.initUI()
def initUI(self):
...
self.edit = QtGui.QTextBrowser()
grid.addWidget (self.edit, 7, 0, 5, 7)
def run(self):
p = Popen (['C:\\...\\test.exe'], stdout=PIPE, stderr=subprocess.STDOUT)
while True:
line = p.stdout.readline()
if not line:
break
self.append_edit('>>>' + line)
def append_edit(self, string):
self.edit.append(string)
def main():
app = QtGui.QApplication(sys.argv)
upgrade = GUI()
sys.exit (app.exec_())
The program I have above will wait until the subprogram finish running then print everything into the widget. What I want is for the program to output ABC then DEF so on with one second in between into the widget WHILE the subprogram is running.
Edit: I can have the subprocess output to cmd just fine, however, if I were print it into the QTextBrowser, it will not work.
I have seen many questions about this issue, but none of them seems to answer my question.
Thanks in advance.
Edit: I'm still new to python. I think this is maybe the problem: Fixed by adding QtGui.QApplication.processEvents()
def initUI(self):
...
running = QtGui.QPushButton('Run')
running.clicked.connect(self.run)
def run(self):
time.sleep(1)
append_edit('Before')
time.sleep(2)
append_edit('After')
So when I hit run in my program, instead of printing out Before first then wait 1 sec, it wait 3 seconds in total then print both Before and After. What cause this problem?
Edit: Even with QtGui.QApplication.processEvents() in my code, I'm still having the same issue. it will run the whole subprogram before showing the output to QTextBrowser.

Related

Abaqus and Tkinter windows freeze with start of mainloop

I'm working on numercial simulations in Abaqus 2020 and coded a GUI with Tkinter.
My problem is, that while running the Tkinter code everything works well unitl the start of the mainloop. Then both windows of Tkinter and Abaqus freeze. When I first close the Abaqus window, the Tkinter Window works correctly. I tried sth with threads but wihtout success..
Setup: Windows 10, Abaqus 2020, Python 2.7
here you find a minimal example:
import Tkinter as tk
from Tkinter import *
# Creating master Tkinter window
window = tk.Tk()
window.title('TKINTER-mini')
# window.attributes("-fullscreen", True)
width=1200
window.geometry("1800x1000")
# Frames
frame0 = Frame(window, width=width)
label_Titel = tk.Label(master=frame0, text ='Test', font=('Aerial 15 bold'))
label_Titel.grid(row=0, column = 0)
frame0.grid(row=0, column=2, padx=20, pady=20, ipadx = 20, ipady=20)
window.mainloop()
# window.update()
I'm looking forward to get some help. Thank you in advance!
Greets JBKingPile
I tried
#1
def __init__(self):
import threading
threading.Thread.__init__(self)
def shutdown_ttk_repeat(self):
self.mainroot.eval('::ttk::CancelRepeat')
self.mainroot.destroy()
def __init__(self, parent):
self.mainroot = parent
# self.mainroot.protocol("WM_DELETE_WINDOW", self.shutdown_ttk_repeat)
# self.tabpage()
self.root.after(1000, self.refresh())
self.root.mainloop()
#2
try:
# Tk will crash if pythonw.exe has an XP .manifest
# file and the root has is not destroyed explicitly.
# If the problem is ever fixed in Tk, the explicit
# destroy can go.
try:
gui = gui(window)
window.mainloop()
finally:
window.destroy()
except KeyboardInterrupt:
pass
#3
if __name__ == "__main__":
app = simpleapp_tk(window)
window.mainloop()

Non-blocking print redirection to Tkinter text widget

I've been able to get print statements redirected to a Tkinter text widget based on the answer to this question. It describes a class to redirect stdout:
class TextRedirector(object):
def __init__(self, widget, tag="stdout"):
self.widget = widget
self.tag = tag
def write(self, str):
self.widget.configure(state="normal")
self.widget.insert("end", str, (self.tag,))
self.widget.configure(state="disabled")
Utilizing the TextRedirector class, I have the following code
import sys
import time
from tkinter import *
root = Tk()
t = Text(root, width = 20, height = 5)
t.grid()
old_stdout = sys.stdout
sys.stdout = TextRedirector(t)
for i in range(5):
print i + 1
time.sleep(1)
sys.stdout = old_stdout
However, this solution seems to be blocking (the text shows up all at once once the for-loop ends).
I have successfully put together a non-blocking solution using Popen and threading, but this isn't an ideal solution due to some unrelated constraints.
Is there a way to have the print statements show up in the text box in real-time?

PyQt4/QProcess issues with Nuke v9

PyQt4/QProcess issues with Nuke v9...
I am trying to utilize a QProcess to run renders in Nuke at my workplace. The reason why I want to use a QProcess is because I've setup this Task Manager with the help of the community at stackoverflow, which takes a list of commands and sequentially runs it one by one, and also allows me to display an output. You can view the question I posted here:
How to update UI with output from QProcess loop without the UI freezing?
Now I am trying to basically run Nuke renders through this "Task Manager". But every time I do it just gives me an error that the QProcess is destroyed while still running. I mean I tested this with subprocess and that worked totally fine. So i am not sure why the renders are not working through QProcess.
So to do more testing I just wrote a simplified version at home. The first issue I ran into though is that apparently PyQt4 couldn't be found from Nuke's python.exe. Even though I have PyQt4 for my main Python version. However apparently there is a compatibility issue with my installed PyQt4 since my main Python version is 2.7.12, while my Nuke's python version is 2.7.3. So i thought "fine then i'll just directly install PyQt4 inside my Nuke directory". So i grabbed this link and installed this PyQt version into my Nuke directory:
http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.10.3/PyQt4-4.10.3-gpl-Py2.7-Qt4.8.5-x64.exe
So i run my little test and seems to be doing the same thing as it does in my workplace, where the QProcess just gets destoryed. So i thought maybe adding "waitForFinished()" would maybe do something different, but then it gives me this error that reads:
The procedure entry point ??4QString##QEAAAEAV0#$$QEAV0##Z could not be located in the dynamic link library QtCore4.dll
And gives me this error as well:
ImportError: Failed to load C:\Program Files\Nuke9.0v8\nuke-9.0.8.dll
Now at this point I can't really do any more testing at home, and my studio is closed for the holidays. So i have two questions i'd like to ask:
1) What is this error I am seeing about "procedure entry point"? It only happens when i try to call something in a QProcess instance.
2) Why is my QProcess being destroyed before the render is finished?? How come this doesn't happen with subprocess? How can I submit a Nuke render job while acheiving the same results as subprocess?
Here is my test code:
import os
import sys
import subprocess
import PyQt4
from PyQt4 import QtCore
class Task:
def __init__(self, program, args=None):
self._program = program
self._args = args or []
#property
def program(self):
return self._program
#property
def args(self):
return self._args
class SequentialManager(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
progressChanged = QtCore.pyqtSignal(int)
dataChanged = QtCore.pyqtSignal(str)
#^ this is how we can send a signal and can declare what type
# of information we want to pass with this signal
def __init__(self, parent=None):
# super(SequentialManager, self).__init__(parent)
# QtCore.QObject.__init__(self,parent)
QtCore.QObject.__init__(self)
self._progress = 0
self._tasks = []
self._process = QtCore.QProcess(self)
self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self._process.finished.connect(self._on_finished)
self._process.readyReadStandardOutput.connect(self._on_readyReadStandardOutput)
def execute(self, tasks):
self._tasks = iter(tasks)
#this 'iter()' method creates an iterator object
self.started.emit()
self._progress = 0
self.progressChanged.emit(self._progress)
self._execute_next()
def _execute_next(self):
try:
task = next(self._tasks)
except StopIteration:
return False
else:
print 'starting %s' % task.args
self._process.start(task.program, task.args)
return True
def _on_finished(self):
self._process_task()
if not self._execute_next():
self.finished.emit()
def _on_readyReadStandardOutput(self):
output = self._process.readAllStandardOutput()
result = output.data().decode()
self.dataChanged.emit(result)
def _process_task(self):
self._progress += 1
self.progressChanged.emit(self._progress)
class outputLog(QtCore.QObject):
def __init__(self, parent=None, parentWindow=None):
QtCore.QObject.__init__(self)
self._manager = SequentialManager(self)
def startProcess(self, tasks):
# self._manager.progressChanged.connect(self._progressbar.setValue)
self._manager.dataChanged.connect(self.on_dataChanged)
self._manager.started.connect(self.on_started)
self._manager.finished.connect(self.on_finished)
self._manager.execute(tasks)
#QtCore.pyqtSlot()
def on_started(self):
print 'process started'
#QtCore.pyqtSlot()
def on_finished(self):
print 'finished'
#QtCore.pyqtSlot(str)
def on_dataChanged(self, message):
if message:
print message
def nukeTestRender():
import nuke
nuke.scriptOpen('D:/PC6/Documents/nukeTestRender/nukeTestRender.nk')
writeNode = None
for node in nuke.allNodes():
if node.Class() == 'Write':
writeNode = node
framesList = [1, 20, 30, 40]
fr = nuke.FrameRanges(framesList)
# nuke.execute(writeNode, fr)
for x in range(20):
print 'random'
def run():
nukePythonEXE = 'C:/Program Files/Nuke9.0v8/python.exe'
thisFile = os.path.dirname(os.path.abspath("__file__"))
print thisFile
cmd = '"%s" %s renderCheck' %(nukePythonEXE, __file__)
cmd2 = [__file__, 'renderCheck']
cmdList = [Task(nukePythonEXE, cmd2)]
# subprocess.call(cmd, stdin=None, stdout=None, stderr=None, shell=False)
taskManager = outputLog()
taskManager.startProcess(cmdList)
taskManager._manager._process.waitForFinished()
if __name__ == "__main__":
print sys.argv
if len(sys.argv) == 1:
run()
elif len(sys.argv) == 2:
nukeTestRender()
I have managed to come up with an answer, so I will write in the details below:
Basically, I was getting the error with the installed PyQt4 because it was not compatible with my version of Nuke, so it is apparently more recommended to use PySide included in Nuke. However Nuke's Python executable cannot natively find PySide, a few paths needed to be added to the sys.path:
paths = ['C:\\Program Files\\Nuke9.0v8\\lib\\site-packages,
C:\\Users\\Desktop02\\.nuke',
'C:\\Program Files\\Nuke9.0v8\\plugins',
'C:\\Program Files\\Nuke9.0v8\\pythonextensions\\site-packages\\setuptools-0.6c11-py2.6.egg',
'C:\\Program Files\\Nuke9.0v8\\pythonextensions\\site-packages\\protobuf-2.5.0-py2.6.egg',
'C:\\Program Files\\Nuke9.0v8\\pythonextensions\\site-packages',
'C:\\Program Files\\Nuke9.0v8\\plugins\\modules',
'C:\\Program Files\\Nuke9.0v8\\configs\\Python\\site-packages',
'C:\\Users\\Desktop02\\.nuke\\Python\\site-packages']
for path in paths:
sys.path.append(path)
I found the missing paths by opening up both Nuke in GUI mode and the Python executable, and comparing both sys.path to see what the Python executable was lacking.
And to answer my own main question: if I call waitForFinished(-1) on the QProcess instance, this ignores the default 30sec limit of this function... Answer came from this thread:
QProcess and shell : Destroyed while process is still running
So here is my resulting working code:
import os
import sys
import subprocess
sysArgs = sys.argv
try:
import nuke
from PySide import QtCore
except ImportError:
raise ImportError('nuke not currently importable')
class Task:
def __init__(self, program, args=None):
self._program = program
self._args = args or []
#property
def program(self):
return self._program
#property
def args(self):
return self._args
class SequentialManager(QtCore.QObject):
started = QtCore.Signal()
finished = QtCore.Signal()
progressChanged = QtCore.Signal(int)
dataChanged = QtCore.Signal(str)
#^ this is how we can send a signal and can declare what type
# of information we want to pass with this signal
def __init__(self, parent=None):
# super(SequentialManager, self).__init__(parent)
# QtCore.QObject.__init__(self,parent)
QtCore.QObject.__init__(self)
self._progress = 0
self._tasks = []
self._process = QtCore.QProcess(self)
self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self._process.finished.connect(self._on_finished)
self._process.readyReadStandardOutput.connect(self._on_readyReadStandardOutput)
def execute(self, tasks):
self._tasks = iter(tasks)
#this 'iter()' method creates an iterator object
self.started.emit()
self._progress = 0
self.progressChanged.emit(self._progress)
self._execute_next()
def _execute_next(self):
try:
task = next(self._tasks)
except StopIteration:
return False
else:
print 'starting %s' % task.args
self._process.start(task.program, task.args)
return True
def _on_finished(self):
self._process_task()
if not self._execute_next():
self.finished.emit()
def _on_readyReadStandardOutput(self):
output = self._process.readAllStandardOutput()
result = output.data().decode()
self.dataChanged.emit(result)
def _process_task(self):
self._progress += 1
self.progressChanged.emit(self._progress)
class outputLog(QtCore.QObject):
def __init__(self, parent=None, parentWindow=None):
QtCore.QObject.__init__(self)
self._manager = SequentialManager(self)
def startProcess(self, tasks):
# self._manager.progressChanged.connect(self._progressbar.setValue)
self._manager.dataChanged.connect(self.on_dataChanged)
self._manager.started.connect(self.on_started)
self._manager.finished.connect(self.on_finished)
self._manager.execute(tasks)
#QtCore.Slot()
def on_started(self):
print 'process started'
#QtCore.Slot()
def on_finished(self):
print 'finished'
#QtCore.Slot(str)
def on_dataChanged(self, message):
if message:
print message
def nukeTestRender():
import nuke
nuke.scriptOpen('D:/PC6/Documents/nukeTestRender/nukeTestRender.nk')
writeNode = None
for node in nuke.allNodes():
if node.Class() == 'Write':
writeNode = node
framesList = [1, 20, 30, 40]
fr = nuke.FrameRanges(framesList)
nuke.execute(writeNode, fr)
# nuke.execute(writeNode, start=1, end=285)
for x in range(20):
print 'random'
def run():
nukePythonEXE = 'C:/Program Files/Nuke9.0v8/python.exe'
thisFile = os.path.dirname(os.path.abspath("__file__"))
print thisFile
cmd = '"%s" %s renderCheck' %(nukePythonEXE, sysArgs[0])
cmd2 = [sysArgs[0], 'renderCheck']
cmdList = [Task(nukePythonEXE, cmd2)]
# subprocess.call(cmd, stdin=None, stdout=None, stderr=None, shell=False)
taskManager = outputLog()
taskManager.startProcess(cmdList)
taskManager._manager._process.waitForFinished(-1)
if __name__ == "__main__":
print sys.argv
if len(sysArgs) == 1:
run()
elif len(sysArgs) == 2:
nukeTestRender()
For whatever reason, PySide refuses to load for me without the nuke module imported first. and also theres a known error when importing nuke it deletes all sys.argv arguments so thats gotta be stored somewhere first before the nuke import...

PyQt4 - can't get video to run with QMovie or Phonon

I'm having problems getting any video player to work with my PyQt4 setup (having tried both phonon and QMovie). The below QMovie script is from an example where several users commented it as functional. For me, it runs but only opens a window (with Loading... centered) that never actually plays the .gif (I've tried several working .gif files from numerous examples online, so the file is not the problem). I've commented out the results from running the three debugging steps as well.
What can I do next?
import sys
import os
import sip
sip.setapi('QVariant', 2)
from PyQt4 import QtGui, QtCore
class BusyLabel(QtGui.QWidget):
def __init__(self, gif, parent = None, text = None):
QtGui.QWidget.__init__(self, parent)
self.hlayout = QtGui.QHBoxLayout(self)
self.hlayout.setSpacing(0)
self.hlayout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.hlayout)
# Movie
self.movieLabel = QtGui.QLabel(self)
self.movieLabel.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.movieLabel.setAlignment(QtCore.Qt.AlignCenter)
self.movie = QtGui.QMovie(gif, QtCore.QByteArray(), self)
self.movie.setScaledSize(QtCore.QSize(20,20))
self.movie.setCacheMode(QtGui.QMovie.CacheAll)
self.movie.setSpeed(100)
print self.movie.isValid() #output = False
print self.movie.supportedFormats() #output = []
self.movieLabel.setMovie(self.movie)
self.hlayout.addWidget(self.movieLabel)
# Label
self.label = QtGui.QLabel(text)
self.hlayout.addWidget(self.label)
self.movie.start()
def setText(self, text):
self.label.setText(text)
def start(self):
self.show()
self.movie.start()
def stop(self):
self.hide()
self.movie.stop()
if __name__ == "__main__":
gif = 'test1.gif'
print os.path.exists(gif) #output = True
app = QtGui.QApplication(sys.argv)
player = BusyLabel(gif)
player.setText('Loading...')
player.start()
player.show()
sys.exit(app.exec_())
output:
True
False
[]
(For those curious about my other attempts, running a popular Phonon script gave the error: phonon backend plugin could not be loaded... I'll take anything at this point)
I'm providing here complete, working code that I've written to answer this (my) problem. All you need is PyQt4 and Matplotlib, I hope this might help someone else facing similar troubles down the road:
https://github.com/evanseitz/PyQt4_VideoPlayer

PyQt :Parent Window not waiting until child window closes.

PyQt :Parent Window not waiting until child window closes. with reference to code shared below ,My welcome class object should wait till first_time class object completely finishes executing , but instead goes ahead and closes it self before first_time object finishes executing .
code :
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PySide.QtCore import QSettings
import sys
from PyQt4 import uic
#importing first configuration class
import configure as config_first
#loading initial settings
settings=QSettings('settings.ini',QSettings.IniFormat)
#loading the ui screens
form_class=uic.loadUiType("screens/firstscreen.ui")[0]
class welcome(QDialog,form_class):
#this signal is emitted when first configuration is done and ready to go
done_and_go_to_use = pyqtSignal()
def __init__(self):
super(welcome, self).__init__()
self.setupUi(self)
self.done_and_go_to_use.connect(self.close)
self.ready_btn.clicked.connect(self.ready)
def ready(self):
if_configured = settings.value('isConfigured', False)
if not if_configured :
first_time=config_first.configureFirst(self)
first_time.show()
self.close()
app = QApplication(sys.argv)
p = welcome()
p.show()
app.exec_()
below is the code for configure.py
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sqlite3
import csv
from PySide.QtCore import QSettings
from PyQt4 import uic
#loading initial settings
settings=QSettings('settings.ini',QSettings.IniFormat)
#loading ui screens
form_class=uic.loadUiType("screens/config_first.ui")[0]
#database connecting
con = sqlite3.connect("local.db")
cur = con.cursor()
cur.execute("CREATE TABLE if not exists marks (student_id int,student_name varchar(200));")
class configureFirst(QDialog,form_class):
done_configuring=pyqtSignal()
try_again=pyqtSignal()
def __init__(self,parent=None):
super(configureFirst, self).__init__(parent)
self.setupUi(self)
self.ok_btn.clicked.connect(self.ok_clicked)
self.cancel_btn.clicked.connect(self.cancel_clicked)
self.try_again.connect(self.ok_clicked)
self.done_configuring.connect(self.cancel_clicked)
self.show()
def ok_clicked(self):
file_select=QFileDialog.getOpenFileName(self,"open file","/")
if file_select:
with open(file_select, 'rb') as f:
reader = csv.reader(f)
ed = list(reader)
for row in ed:
if "name" not in row or "id" not in row:
cur.execute("Insert into marks Values (?,?);",(row[0],row[1]))
con.commit()
settings.setValue("isConfigured",True)
self.done_configuring.emit()
else:
#if recurssion is used the no of time it has to close increases and leads to integration problems
self.try_again.emit()
def cancel_clicked(self):
if_configured=settings.value("isConfigured")
if if_configured:
self.close()
else:
QMessageBox.critical(self,"PerfAnalyser","You Need to Configure For PerfAnalyser To Work")
def closeEvent(self,event):
#this method is triggered when 'X' is clicked i.e close button is clicked at the upper right corner
if_configured = settings.value("isConfigured")
if if_configured:
event.accept()
else:
QMessageBox.critical(self, "PerfAnalyser", "You Need to Configure For PerfAnalyser To Work")
event.ignore()
Thanks for the help in advance ...
I will try to help out since I notice few people have seen your post. I have had this happen a long time ago so I need a reminder, but I was unable to get your code running, I also tried to recreate your ui files and the screens directory but I was not successful. However, maybe the following is still useful.
In my working code, any time I needed to create a subwindow, I executed subwindows as follows from the main window's module:
dlg = SubWindowModuleName.StartSub()
dlg.exec_()
This will execute the subwindow and waits for it to close. Then, on the subwindow module (SubWindowModuleName in the above code, "configure" for you), I did this:
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(982, 521)
... # here I build the window (I noticed that you import UI files which is a much better way of doing this)
...
class StartSub(QtGui.QDialog, Ui_Dialog):
def __init__(self,parent=None):
QtGui.QDialog.__init__(self,parent)
self.setupUi(self)