How does the MainDialog() cycle work - python

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

Related

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

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.

Starting a Qthread on a button click in PyQt5 [duplicate]

This question already has answers here:
QThread: Destroyed while thread is still running
(2 answers)
Closed 4 years ago.
I'm currently trying to make an API call when my button is clicked without pausing my GUI for the duration of the call. I was using threads to set the text of my Qlable to the response of the API call. This worked however it was unsafe as I was accessing GUI elements from a separate thread.
Currently, I'm attempting to use QThreads to make the API call and then emit the response to the GUI thread however, when I create the Qthread object my program finishes with exit code 3. I've simplified the problem for more clarity.
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("TestWindow")
self.setFixedSize(300,75)
self.main_layout = QtWidgets.QGridLayout()
self.setLayout(self.main_layout)
self.txt_0 = QtWidgets.QLabel()
self.btn_0 = QtWidgets.QPushButton('Press Me')
self.btn_0.clicked.connect(self.btn_0_clicked)
self.main_layout.addWidget(self.txt_0, 0, 0)
self.main_layout.addWidget(self.btn_0, 1, 0)
self.show()
def btn_0_clicked(self):
temp_thread = StringThread("name")
temp_thread.start()
class StringThread(QtCore.QThread):
str_signal = QtCore.pyqtSignal(str)
_name = ''
def __init__(self, name):
QtCore.QThread.__init__(self)
self._name = name
print("Thread Created")
def run(self):
self.str_signal.emit('Emitted message from StringThread. Name = ' + self._name)
print("Done run")
My intention is to set the text of my Qlable to the message emitted from the pyqtSignal in the StringThread class however, as soon as I click the button my program finishes with exit code 3.
edit:
I made the following changes to the btn_0_clicked method
def btn_0_clicked(self):
self.temp_thread = StringThread("hello")
self.temp_thread.str_signal.connect(self.txt_0.setText)
self.temp_thread.start()
It's working now.
You should probably read up on the Qt C++ documentation to better understand what is happening:
http://doc.qt.io/qt-5/qobject.html
http://doc.qt.io/qt-5/qthread.html
https://wiki.qt.io/QThreads_general_usage
In general you have two problems (one of which is worth listing twice, to examine it from different angles):
Your QThread is owned/associated by/with the thread that created it for the purpose of emitting events. So all code trying to emit events is actually trying to access the event loop of your main thread (UI). Read up on QObject's thread() and moveToThread() methods to understand this more fully.
You overrode the run() method of QThread which is what is invoked on the actual thread represented by QThread (i.e. the code that winds up getting called eventually once you call start() on the thread). So you are effectively trying to access the main event loop in an unsafe manner from a different thread.
You overrode the run() method of QThread. The default implementation of QThread sets up a QEventLoop, runs it and that is what gives QObject's associated/owned with/by that thread the ability to use signal/slots freely. You probably did not intend to lose that default implementation.
Basically what you want to do instead is this:
Do not subclass QThread. Instead write a custom QObject worker subclass with appropriate events
Construct a QThread with an eventloop (the default IIRC, but check the docs)
Construct an instance of your custom worker object.
Move your worker object to the newly created QThread with moveToThread().
Wire up the signals and slots, make sure you are using the QueuedConnection type (the default is DirectConnection which is not suitable for communicating across thread boundaries).
Start your thread which should just run its own QEventLoop.
Emit the signal to kickstart your worker object logic.
Receive notification of the results via signals on your main thread (eventloop), where it is safe to update the UI.
Figure out how you want to terminate the QThread and clean up associated resources. Typically you would fire a custom event wired up to the destroyLater() slot to get rid of your worker object. You may want to cache/reuse the QThread for subsequent button clicks or you may want to tear it down as well once your worker object has been cleaned up.

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.

Calling a class method in Python/PySide

I spent longer than I'd care to admit think of a suitable 'question' heading for this topic, as my issue is somewhat hard to articulate.
Here is a quick summary of the situation:
I'm writing a basic GUI with Python 3.4 and PySide
I'm using QFileSystemWatcher to monitor a particular file
When the file is changed, QFileSystemWatcher calls a method, which in turn calls a method within a PySide Class
All of the above seems to be working perfectly, except the GUI-specific actions detailed in the PySide Class method aren't being executed (I'll explain in more detail below).
Example code:
#Establishing the PySide GUI Class
class GUI(QMainWindow, Ui_GUI):
def __init__(self, parent=None)
super(GUI, self).__init__(parent)
self.setupUi(self)
QtCore.QObject.connect(self.Button, QtCore.SIGNAL("clicked()"), self.Run)
def Run(self):
print("1")
self.treeWidget1.clear()
self.treeWidget2.clear()
print("2")
self.label1.setText("Text 1")
self.label2.setText("Text 2")
print("3")
for y in range(0, 5):
self.treeWidget1.resizeColumnsToContents()
print("Finished")
#Establish the file monitoring mechanism, *outside* the PySide class
def FileChanged():
Script = GUI()
Script.Run()
Paths = ['path/to/file']
Watch = QtCore.QFileSystemWatcher(Paths)
Watch.fileChanged.connect(FileChanged)
#Setting up the GUI
if __name__ == '__main__':
app = QApplication(sys.argv)
showGUI = GUI()
showGUI.show()
app.exec_()
As I mentioned above, the above code doesn't return any errors. When I change the file (listed in the path), FileChanged does indeed call the Run() method from the GUI class. However, it won't actually do any of the 'stuff', it will only execute the print commands in between the 'stuff'.
If I then click on the 'Button' in the GUI, it will execute Run() correctly, and properly execute all the 'stuff'.
My question: is there something I'm missing here? If it's calling the method correctly, and is able to execute the various 'print' commands, why is it not executing the actual 'stuff'?
Thanks!
EDIT 1: I've removed the -do stuff- tags and put in some example code. All the 'stuff' code relates to updating various PySide QLabels, QTreeWidgets, etc.
EDIT 2: I forget the () at the end of the treeWidget clear commands.
The Script object created in the FileChanged function has local scope, and will be garbage-collected as soon as the function returns.
If the Run slot gets called when the signal fires, it will carry out all of the changes correctly, but you won't get to see any of those changes, because Script will be deleted before it is ever shown.
In order to for the example script to begin to make any sense, it would need to be re-arranged to something like this:
#Setting up the GUI
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
showGUI = GUI()
#Establish the file monitoring mechanism, *outside* the PySide class
def FileChanged():
showGUI.Run()
Paths = ['path/to/file']
Watch = QtCore.QFileSystemWatcher(Paths)
Watch.fileChanged.connect(FileChanged)
showGUI.show()
app.exec_()
Of course, it's possible that your real code is nothing like the example in your question (which has numerous other errors preventing it from being run), and so this might be no help. But if that is the case, you will have to post a fully working, self-contained example that properly demonstrates the problems you are having.

wxPython: Threading GUI --> Using Custom Event Handler

I am trying to learn how to run a thread off the main GUI app to do my serial port sending/receiving while keeping my GUI alive. My best Googling attempts have landed me at the wxpython wiki on: http://wiki.wxpython.org/LongRunningTasks which provides several examples. I have settled on learning the first example, involving starting a worker thread when the particular button is selected.
I am having trouble understanding the custom-event-definition:
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""
def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
Primarily the
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
I think EVT_RESULT is placed outside the classes so as to make it call-able by both classes (making it global?)
And.. the main GUI app monitors the thread's progress via:
# Set up event handler for any worker thread results
EVT_RESULT(self,self.OnResult)
I also notice that in a lot of examples, when the writer uses
from wx import *
they simply bind things by
EVT_SOME_NEW_EVENT(self, self.handler)
as opposed to
wx.Bind(EVT_SOME_NEW_EVENT, self.handler)
Which doesn't help me understand it any faster.
Thanks,
That's the old style of defining custom events. See the migration guide for more information.
Taken from the migration guide:
If you create your own custom event
types and EVT_* functions, and you
want to be able to use them with the
Bind method above then you should
change your EVT_* to be an instance of wx.PyEventBinder instead of a
function. For example, if you used to
have something like this:
myCustomEventType = wxNewEventType()
def EVT_MY_CUSTOM_EVENT(win, id, func):
win.Connect(id, -1, myCustomEventType, func)
Change it like so:
myCustomEventType = wx.NewEventType()
EVT_MY_CUSTOM_EVENT = wx.PyEventBinder(myCustomEventType, 1)
Here is another post that I made with a couple of example programs that do exactly what you are looking for.
You can define events like this:
from wx.lib.newevent import NewEvent
ResultEvent, EVT_RESULT = NewEvent()
You post the event like this:
wx.PostEvent(handler, ResultEvent(data=data))
Bind it like this:
def OnResult(event):
event.data
handler.Bind(EVT_RESULT, OnResult)
But if you just need to make a call from a non-main thread in the main thread you can use wx.CallAfter, here is an example.
Custom events are useful when you don't want to hard code who is responsible for what (see the observer design pattern). For example, lets say you have a main window and a couple of child windows. Suppose that some of the child windows need to be refreshed when a certain change occurs in the main window. The main window could directly refresh those child windows in such a case but a more elegant approach would be to define a custom event and have the main window post it to itself (and not bother who needs to react to it). Then the children that need to react to that event can do it them selves by binding to it (and if there is more than one it is important that they call event.Skip() so that all of the bound methods get called).
You may want to use Python threads and queues and not custom events. I have a wxPython program (OpenSTV) that loads large files that caused the gui to freeze during the loading. To prevent the freezing, I dispatch a thread to load the file and use a queue to communicate between the gui and the thread (e.g., to communicate an exception to the GUI).
def loadBallots(self):
self.dirtyBallots = Ballots()
self.dirtyBallots.exceptionQueue = Queue(1)
loadThread = Thread(target=self.dirtyBallots.loadUnknown, args=(self.filename,))
loadThread.start()
# Display a progress dialog
dlg = wx.ProgressDialog(\
"Loading ballots",
"Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots),
parent=self.frame, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
)
while loadThread.isAlive():
sleep(0.1)
dlg.Pulse("Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots))
dlg.Destroy()
if not self.dirtyBallots.exceptionQueue.empty():
raise RuntimeError(self.dirtyBallots.exceptionQueue.get())

Categories