Pausing Python Code When Console Output Has Been Re-Routed to GUI - python

I borrowed a design that I found on stackoverflow to redirect console output to a PyQt5 GUI textEdit widget. This works fine, but the text is not displayed in "real-time". It seems to output the text to the GUI once a process has completed. This has not been a problem until I tried to use time.sleep(secs) to print something, pause, then print something else. What ends up happening is that the program pauses for secs, then it prints all of the statements at once.
This class is in the mainWindow file for the GUI:
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
This is in the __init__ method of my event handling file:
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
self.case_setup_console.setReadOnly(True)
self.main_console.setReadOnly(True)
This function is in the main class of event handling file (outside __init__):
def normalOutputWritten(self, text):
"""Append text to the QTextEdit."""
# Maybe QTextEdit.append() works as well, but this is how I do it:
cursor = self.case_setup_console.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(text)
self.case_setup_console.setTextCursor(cursor)
self.case_setup_console.ensureCursorVisible()
This works as intended to re-route the output to the text edit widget self.case_setup_console. But, when I try to run a code such as:
print('This is the first message')
time.sleep(5)
print('This should print 5 seconds later')
What happens is that the program waits 5 seconds, then it prints both statements together.

When programing for GUI code, there is a fundamental shift in how the program is designed. To make it short: after building and initialisation, the program is all the time running in an "event loop" provided by the GUI framework, and your code is only called when specific events take place.
That is in contrast with a terminal application where your code is running all the time, and you tell when to do "print"s, "input"s and pauses with "time.sleep".
The GUI code is responsible for taking notes of events (keyboard, UI, network, etc...), redrawing window contents and calling your code in response to events, or just when it is time to redraw a content that is defined in your code (like updating a background image).
So, it can only render the text that is supposed to show up in a window, with your redirected "print", when the control is passed back to its event loop. When you do time.sleep you pause the return - no code in the event loop is run, and it can't, of course, do any screen drawing.
What is needed is that you write your pauses in the program in a way that during the pause, the GUI event loop is running - not "time.sleep", that just suspends your whole thread.
In Qt the way to do that is create a QTimer object to call the code you want to use to print text at a particular moment, and then just surrender the execution to the the QtMainloop by returning from your function.
Thanks to Python's support for nested functions, that can be done in painless ways, even using lambda functions when setting the timer itself.
...
print('This is the first message')
timer = QtCore.QTimer
timer.singleShot(5000, lambda *_: print('This should print 5 seconds later'))
Should work for the given example. (The call, as usual for UIs, takes the pause time in miliseconds rather than in seconds).
If you will need to schedule more text to be printed after each phrase is output, you will need to call the scheduling inside the callback itself, and will need a little more sophistication, but it still could be as simple as:
phrases = iter(("hello", "world", "I", "am", "talking", "slowly!"))
timer = QtCore.QTimer()
def talker(*_):
phrase = next(phrases, None)
if not phrase:
return
print(phrase)
timer.singleShot(1000, talker)
timer.singleShot(1000, talker)
(Note that there is nothing special about the *_ parameter name either: I am just indicating that there might be any number of positional arguments for the callback - (The "*" part, that is Python syntax) - and that I won't care about then (I call the argument sequence as "_" to indicate I don't care how this is called, as it won't be used anyway - that is a coding convention) )
The iter and next calls are more "Python dialect" than one might be used, but one could just use a list and a counter up to the list length all the same.

Related

Which code does the mainloop() processes infinitely until any event occurs?

To understand my question kindly follow the paragraphs written below:
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
consider the code:
from tkinter import *
window = Tk()
print("lol")
print("Hello World")
window.mainloop()
the output didn't print "Hello World" or "lol" infinite number of times, so the mainloop() doesn't loop the code of the current module.
Now consider this code:
from tkinter import *
print("lol")
window = Tk()
print("Hello World")
while True:
window.update()
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
Now the first question arises what does the window.update() function do to update the values in the GUI, does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
The second question is :
I read this article
"Mainloop in Python Tkinter is an infinite loop of the application window which runs forever so that we can see the still screen.
The application window is like a frame that keeps on destroying every microsecond but the main loop keeps on creating a new updated window.
This process of destroying old window screens and creating a new one happens so fast that human eyes don’t even realize it.
Since the process runs infinite time that is why we are able to see the application in front of us and when we close the window then the loop terminates or exits."
Now if this is true then to recreate an updated window the root.mainloop() must read the entire root GUI code again and again entirely or is there another explanation to it.
I have been trying to understand this for the past 6hrs and I have visited every site and I cannot find the solution for the life of me.
Regards,
Rashik
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
No.
Via this function, it calls this C code which has the embedded Tcl interpreter process one event, or wait for Tkinter_busywaitinterval before trying to process another event
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
window.update() calls TCL update, which is described to
[...] bring the application “up to date” by entering the event loop repeatedly until all pending events (including idle callbacks) have been processed.
Your infinite loop doesn't have a sleep, so it's spinning your CPU as hard as possible to do practically nothing.
[...] Does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
It certainly doesn't re-read your code. It processes any pending widget updates, which may have happened by running e.g. window.text("...") in e.g. a click callback or an .after() timeout, etc.
I read this article [...]
That article seems wrong and/or at least over-simplifies things.
This simple example clock should clarify how things work:
import time
import tkinter as tk
root = tk.Tk()
text = tk.Label(root)
text.pack()
def tick():
text["text"] = time.ctime() # update `text` widget's content
root.after(1000, tick) # schedule for this function to be called after 1 second
if __name__ == '__main__':
tick() # call the `tick` function once before entering main loop
root.mainloop()

Runtime progress status (like statusbar) in python

I'm trying to create simple progress status label for my Pyqt5 Python code and update it after every iteration of a loop in which a function does a bunch of stuff. The label I want to be updated is "status_label_counter". The below code only shows the part for creating labels and the exact place where I want to use the functionality I've mentioned.
#initialisation code, etc...
self.status_label_counter = QLabel()
self.status_label_from = QLabel(' from: ')
self.status_label_total = QLabel()
status_hbox = QHBoxLayout()
status_hbox.addStretch()
status_hbox.addWidget(self.status_label_counter)
status_hbox.addWidget(self.status_label_from)
status_hbox.addWidget(self.status_label_total)
status_hbox.addStretch()
#bunch of other code...
def create_ics(self):
counter = 0
self.status_label_total.setText(str(len(self.groups)))
for group in self.groups:
#does a bunch of stuff inside
group_manager.create_calendar_for(self.rows, group, self.term)
counter += 1
#for console output
print('iteration: ', counter)
#trying to update status counter
self.status_label_counter.setText(str(counter))
The problem is that I only see the update of both labels when the loop is done with the nested function. When I click a button that calls for "create_ics" function window becomes inactive for about 5 seconds, I see logs on console with count of iterations, but nothing happens in view.
The view (Qt) is locked in your main thread and never gets its chance to process its event loop and thus redraw itself. If you really want to do it this way, call:
self.status_label_counter.repaint()
After you set the text (and if you have some complex layout measuring call QApplication.processEvents() instead).
However, much better option would be to run your create_ics() function in a separate thread leaving your main thread to deal with the view and Qt's event processing. You can do it either through standard Python's threading module, or using Qt's own QThread: https://nikolak.com/pyqt-threading-tutorial/ .

How does the MainDialog() cycle work

I'm trying to understand how the cycle of my "main.py" works. It's based on examples found on the net, about the PySide and Qt Designer, to implement a Python GUI.
The code is:
#***********************************#
# Python Libraries #
#***********************************#
from PySide.QtCore import *
from PySide.QtGui import *
import sys
import time
#***********************************#
# Python files #
#***********************************#
import Gui
from server import *
class MainDialog(QDialog, Gui.Ui_TCPServer):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.connect(self.ConnectBt, SIGNAL("clicked()"), self.ConnectBt_clicked)
self.connect(self.QuitBt, SIGNAL("clicked()"), self.QuitBt_clicked)
self.connect(self.DisconnectBt, SIGNAL("clicked()"), self.DisconnectBt_clicked)
print "NOW HERE\r\n"
def ConnectBt_clicked(self):
self.ConnectBt.setText("Connecting...")
self.server_connect()
print "THEN HERE\r\n"
def QuitBt_clicked(self):
self.close()
def DisconnectBt_clicked(self):
self.ConnectBt.setText("Connect")
self.server_off = ChronoRequestHandler()
self.server_off.finish()
def server_connect(self):
self.server_on = ServerStart()
self.server_on.try_connect()
if __name__ == '__main__':
app = QApplication(sys.argv)
form = MainDialog()
print "HERE\r\n"
form.show()
app.exec_()
print "END\r\n"
When I call the "main.py" I get a print of "NOW HERE" and "THEN HERE". When I press the 'ConnectBt', I get the print of "THEN HERE".
But and after this, where the cycle remains? Does it returns to init, if so, shouldn't I get again the print of "NOW HERE"? Does it returns to main, if so, shouldn't I get the print of "HERE"? Please explain me...
When I press the 'QuitBt' I get the print of "END"... I'm confused!
Thanks.
I think you should get more clear on how object programming and events work.
In the last if-statement (the code on the bottom that runs when you call your script from e.g. terminal) you create an app object instance of QApplication.
After that you create form, instance of MainDialog which is the class you define above (inheriting methods, properties, etc from two classes, QDialog, Gui.Ui_TCPServer).
By doing
form = MainDialog()
you run __init__, print "NOW HERE" and go out of that method. Please check what __init__ does in Python. why-do-we-use-init-in-python-classes
Before the end you call the exec() method of the app instance. This contains a loop so that your interface gathers and processes events. See the documentation on QApplication.exec() below.
When you press the 'ConnectBt' button you call the ConnectBt_clicked() method, which does stuff (connects with the server) and prints "THEN HERE".
In the same way, when you press QuitBt you call QuitBt_clicked(), which closes the connection and lets the code print "END".
I also suggest you read more documentation about the classes you are using. They will explain how come that the different buttons are "linked"/have as callbacks the methods ConnectBt_clicked(), def QuitBt_clicked(), and DisconnectBt_clicked(). The mechanisms by which the buttons trigger these callbacks is kind of implicit in the code implemented in those classes.
QApplication Class Reference: exec_
int QApplication.exec_ ()
Enters the main event loop and waits until exit() is called, then
returns the value that was set to exit() (which is 0 if exit() is
called via quit()).
It is necessary to call this function to start event handling. The
main event loop receives events from the window system and dispatches
these to the application widgets.
Generally, no user interaction can take place before calling exec().
As a special case, modal widgets like QMessageBox can be used before
calling exec(), because modal widgets call exec() to start a local
event loop.
To make your application perform idle processing, i.e., executing a
special function whenever there are no pending events, use a QTimer
with 0 timeout. More advanced idle processing schemes can be achieved
using processEvents().
We recommend that you connect clean-up code to the aboutToQuit()
signal, instead of putting it in your application's main() function.
This is because, on some platforms the QApplication.exec() call may
not return. For example, on the Windows platform, when the user logs
off, the system terminates the process after Qt closes all top-level
windows. Hence, there is no guarantee that the application will have
time to exit its event loop and execute code at the end of the main()
function, after the QApplication.exec() call.
See also quitOnLastWindowClosed, quit(), exit(), processEvents(), and
QCoreApplication.exec().

PySide / Python GUI freezes

I'm currently writing a GUI for rvplayer that shall enable artists to automatically render dailies with slate and burn-in information. The GUI is written with PySide and scripted in Python 2.7. My problem is that upon calling my process and updating my QProgressBar with the stdout the GUI freezes. I know that this is a common problem and that it can probably be solved with processEvents() somehow, but I know far too little about threading and process loops to get my head around this issue. Since my code is a little lengthy already, here's the part that causes the issue:
def rv(self, args):
p = subprocess.Popen(["C:/Program Files/Tweak/RV-4.0.10-64/bin/rvio_hw.exe"]+[x for x in args], stdout=subprocess.PIPE)
while True:
line = p.stdout.readline()
if line != "":
progressStr=re.search(r"([0-9]+.[0-9]+%)", line.rstrip())
if progressStr == None:
print line.rstrip()
else:
progressInt=int(float(re.sub("[^0123456789\.]", "", progressStr.group())))
self.prog_QProgressBar.setValue(progressInt)
print progressStr.group()
else:
break
and here is the part that starts my QApplication:
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
finalForm = MainWindow()
finalForm.show()
sys.exit(app.exec_())
I'm calling the function rv upon pressing a button and although the progress bar keeps updating normally, the window starts to get nonresponsive after some time. I do not understand at which point I could use app.processEvents() to tell my QApplication to run the process in a separate thread or in the background.
Since it looks like you're not using threads, it may be that all that's required is to call processEvents after updating the progress bar, like this:
self.prog_QProgressBar.setValue(progressInt)
QtGui.qApp.processEvents()
However, the effectiveness of this may depend on how long it takes the process to produce the output. All that the processEvents call does is to immediately handle any pending events (e.g. widget painting, mouse clicks, etc) that are currently in the application's event queue. In between those calls, the GUI will continue to freeze (i.e. the executing code is not run in a separate thread or in the background, as you suggested). So the extent to which this technique can keep the GUI responsive depends on how frequently processEvents can be called within the rv() method.
The issue is that it's not as if your app is frozen, but Windows thinks that the app is frozen as it's ignoring events (mouse over, click etc etc), so Windows, in its wisdom, gives you that dialogue.
You need to start the thread off after the show() and then run the processEvents function, and obviously only call sys.exit once your thread has finished.

python get arrow keys from command line

i have a script which should interact with the users input (pressing the arrow keys), but i cannot get the keys. i tried raw_input and some other functions, but they didnt work. this is my sample code, how it should look like (running bool can be set to False in another function)
running = True
while running:
#if input == Arrow_UP:
# do_Sth
#elif ...
display()
time.sleep(1)
another question is, how can i call the display function only once every second, but react on the input immediately?
There are different situations:
If you use a graphical frontend such as TKinter or PyGame, you can bind an event to the arrow key and wait for this event.
Example in Tkinter taken from this answer:
from Tkinter import *
main = Tk()
def leftKey(event):
print "Left key pressed"
def rightKey(event):
print "Right key pressed"
frame = Frame(main, width=100, height=100)
main.bind('<Left>', leftKey)
main.bind('<Right>', rightKey)
frame.pack()
main.mainloop()
If your application stays in the terminal, consider using curses as described in this answer
Curses is designed for creating interfaces that run in terminal (under linux).
If you use curses, the content of the terminal will be cleared when you enter the application, and restored when you exit it. If you don't want this behavior, you can use a getch() wrapper, as described in this answer. Once you have initialized getch with getch = _Getch(), you can store the next input using key = getch()
As to how to call display() every second, it again depends on the situation, but if you work in a single process in a terminal, the process won't be able to call your display() function while it waits for an input. The solution is to use a different thread for the display() function, as in
import threading;
def display ():
threading.Timer(1., display).start ();
print "display"
display ()
Here display schedules itself one second in the future each time it is called. You can of course put some conditions around this call so that the process stops when some conditions are met, in your case when an input has been given. Refer to this answer for a more thoughout discussion.

Categories