PySide2 signal not catched between threads with objects - python

I'm working on a GUI application, developed in Python and its UI library : PySide2 (Qt wrapper for Python)
I have a heavy computation function I want to put on another thread in order to not freeze my UI. The Ui should show "Loading" and when the function is over, receive from it it's results and update the UI with it.
I've tried a lot of different codes, a lot of examples are working for others but not me, is it PySide2 fault ? (For example this is almost what I want to do : Updating GUI elements in MultiThreaded PyQT)
My code is :
class OtherThread(QThread):
def __init__(self):
QThread.__init__(self)
def run(self):
print 'Running......'
self.emit(SIGNAL("over(object)"), [(1,2,3), (2,3,4)])
#Slot(object)
def printHey( obj):
print 'Hey, I\'ve got an object ',
print obj
thr = OtherThread()
self.connect(thr,SIGNAL("over(object)"),printHey)
thr.start()
My code is working if I use primitives such as bool or int but not with object. I see 'Running....' but never the rest.
Hope someone can enlighten me

You can't define signals dynamically on a class instance. They have to be defined as class attributes. You should be using the new-style signals and slot syntax.
class OtherThread(QThread):
over = QtCore.Signal(object)
def run(self):
...
self.over.emit([(1,2,3), (2,3,4)])
class MyApp(QtCore.QObject)
def __init__(self):
super(MyApp, self).__init__()
self.thread = OtherThread(self)
self.thread.over.connect(self.on_over)
self.thread.start()
#QtCore.Slot(object)
def on_over(self, value):
print 'Thread Value', value

Related

Modifying __class__ attributes and PyQt signals for building a state machine

I am building a simple state machine, which manipulates the __class__ attribute to identify which state we are in. This follows recipe 8.19 in "Python Cookbook, D. Beazley". The problem is when I use this recipe with PyQt signals, the state machine is not able to track in which __class__ it belongs.
Here is a simplification of the state machine Interface I am writing:
from PyQt5 import QtCore, QtWidgets
class Interface:
def __init__(self):
self.new_state(DisabledState)
def enable(self):
raise NotImplementedError
def disable(self):
raise NotImplementedError
def new_state(self, newstate):
self.__class__ = newstate
class EnabledState(Interface):
def enable(self):
print('enable/enabled state')
def disable(self):
print('disable/enabled state')
self.new_state(DisabledState)
class DisabledState(Interface):
def enable(self):
print('enable/disabled state')
self.new_state(EnabledState)
def disable(self):
print('disable/disabled state')
When I enable then disable this state machine as so:
class Thread(QtCore.QObject):
enable = QtCore.pyqtSignal()
disable = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
self.interface = Interface()
self.enable.connect(self.interface.enable)
self.disable.connect(self.interface.disable)
myThread = Thread()
app = QtWidgets.QApplication([])
myThread.enable.emit()
myThread.disable.emit()
app.exec_()
In this code above, I first enable the disabled interface and move to enabled state. Then from enabled state I disable the interface, so the expected output is:
# enable/disabled state
# disable/enabled state
However, I get the following where it looks like interface never went to enabled state.
# enable/disabled state
# disable/disabled state
Can anyone explain to me what is happening here?
P.S.
The book describes another recipe which does not require modification of the __class__ attribute and no inheritance. That recipe does indeed give the correct output...
When you pass self.interface.enable to .connect(), it references DisabledState.enable, so it will always behave like it is disabled. To fix this, you could use lambda: self.interface.enable(), and similar for disable.

QTimer not executed when called inside Singleton/Borg

I've implemented a timer using QTimer inside a Singleton. The Singleton is implemented using the Borg pattern. If I start a QTimer with single shot inside a function of the Singleton it won't be executed. The same call in a function outside the Singleton works well.
This is the code:
#!/usr/bin/env python
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication
class Borg():
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
class Timers(Borg):
def __init__(self):
Borg.__init__(self)
def update_not_working(self):
QTimer().singleShot(2000, Timers().update_not_working)
print('update not working')
def update_working():
QTimer().singleShot(2000, update_working)
print('update working')
if __name__ == '__main__':
app = QApplication(sys.argv)
print('start timer')
Timers().update_not_working()
update_working()
sys.exit(app.exec_())
The output is (no error, no exception):
start timer
update not working
update working
update working
....
Why is one call working and the other not? Is there something wrong with my implementation of the Borg or with the usage of QTimer?
print self in update_not_working and print Timers() in update working show that the Timers object before the event loop started is different from the one within:
update not working
<__main__.Timers instance at 0xb52162cc>
update working
<__main__.Timers instance at 0xb52162cc>
update working
<__main__.Timers instance at 0xb521650c>
update working
<__main__.Timers instance at 0xb521650c>
update working
<__main__.Timers instance at 0xb521650c>
update working
<__main__.Timers instance at 0xb521650c>
#classmethod should help here because it allows to call the method on an instance OR on the class as you do in the single shot statement.
Compare: When should I use #classmethod and when def method(self)?
This is actually just a matter of normal garbage-collection.
If you add some debugging code to your example like this:
class Timers(Borg):
def __init__(self):
Borg.__init__(self)
print('init:', self)
def update_not_working(self):
QTimer().singleShot(1, Timers().update_not_working)
print('update not working')
def __del__(self):
print('deleted:', self)
it will produce output like this:
start timer
init: <__main__.Timers object at 0x7f194bf53eb8>
init: <__main__.Timers object at 0x7f1940cfdb00>
deleted: <__main__.Timers object at 0x7f1940cfdb00>
update not working
deleted: <__main__.Timers object at 0x7f194bf53eb8>
update working
update working
As you can see, both of the Timers instances get deleted long before the single-shot timer sends its timeout() signal. And when they deleted, their instance-methods get deleted as well, which will automatically disconnect them from the signal. This shows that the Borg pattern does not produce a true singleton: it just mimics some of the behaviour of one.
If you use a real singleton class, like this:
class Timers2(object):
_instance = None
def __new__(cls):
if Timers2._instance is None:
Timers2._instance = object.__new__(cls)
return Timers2._instance
def update_not_working(self):
QTimer().singleShot(2000, Timers2().update_not_working)
print('update not working')
your example will work as expected. This is because there is only ever one instance, and it is kept alive by being cached as a class attribute.
Finally, the reason why the update_working() succeeds, is because it is a globally defined function. As with the class attribute, this ensures that it won't get garbage-collected until the script completes.
class Borg():
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
class Timers(Borg):
def __init__(self):
Borg.__init__(self)
#classmethod
def update_not_working(cls):
QTimer().singleShot(2000, Timers().update_not_working)
print('update not working')
def update_working():
QTimer().singleShot(2000, update_working)
print('update working')
if __name__ == '__main__':
app = QApplication(sys.argv)
print('start timer')
Timers().update_not_working()
update_working()
sys.exit(app.exec_())

PyQt Code Splitting - Design Vs Functionality

Am struggling to comprehend how to split code in (Py)Qt. The aim is to have the design & navigation tabs in QMainWindow, each tab triggering code in other files. So far it only launches with the ActionClass in the same document / putting in an external document causes 'app not defined' when clicking the tab. The below works without errors, but is clunky.
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.u = Ui_MainWindow()
self.u.setupUi(self)
self.u.tabs.currentChanged.connect(self.TabsChanged)
def TabsChanged(self, i):
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass.__init__
class ActionClass(Main):
def __init__(self):
app.u.lineEdit.setText("test")
app = Main()
app.show()
sys.exit(app.exec_())
The examples I keep seeing have all code in one document. Is there another way to do this e.g. where the ActionClass is in another file/writing u.lineEdit.setText instead of app.u.lineEdit.setText. It seems inheritance & an instance of Main can't be accessed from the ActionClasses doc, so I can't see how they would communicate back to the Main?
Much appreciated
As suggest #M4rtini you can separate your code into python modules. And then import them (use them) in your main module.
For instance the code you posted can be separated in to files:
# actions_class.py
class ActionClass(Main):
def __init__(self):
app.u.lineEdit.setText("test")
and
# main.py
from action_class import ActionClass # This line no need much explanation ;)
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.u = Ui_MainWindow()
self.u.setupUi(self)
self.u.tabs.currentChanged.connect(self.TabsChanged)
def TabsChanged(self, i):
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass.__init__
app = Main()
app.show()
sys.exit(app.exec_())
In order to understand how import works see the link I left you above.
More explanation
Lest's see:
The correct way of executin code inside a __init__ method is creating an instance. See the example below.
class A:
def __init__(self):
print("Executing A.__init__")
print("Doing things wrong")
A.__init__ # This don't print enything
print("Doing things well")
A() # This works as expected.
So, you line reads:
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass.__init__
and should reads:
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass()
On the other hand, is a bad practice put code that's not for initialize the instance inside the __init__ methods.
If you don't need the instance but yet you want to store the functions inside a class (something like a c++ namespace) you creating
use #staticmethod decorator.
class A:
#staticmethod
def foo():
print("Oh, wow, a static method in Python!")
A.foo()
So, your ActionClass could be rewritten as:
class ActionClass(Main):
#staticmethod
def do_action:
app.u.lineEdit.setText("test")
ans then you can use it like this:
if i == self.u.tabs.indexOf(self.u.tabFirst): ActionClass.do_action()

How do I tell a class method to wait until a signal from a QDialog class method is caught?

I have the following code:
class Functions(QObject):
mysig = Signal(filename)
def __init__(self, parent=None):
super(Functions, self).__init__(parent)
self.result = None
def showDialog(self, filename):
self.mysig.emit(filename)
def grabResult(self):
while not self.result:
time.sleep(5)
return result #this is the question
def setResult(self, result):
self.result = result
The other part of the code has this:
class Dialog(QDialog):
anotherSig = Signal(str)
fun = Functions()
def __init__(self, parent=None, filename=filename):
self.filename = filename
#Here it displays a picture based on the filename parameter
def okButtonClicked(self):
text = self.lineedit.text()
fun.setResult(text)
#Tried also this:
self.anotherSig.emit(text)
The Functions() class is called from a worker QThread (not shown here).
I guess my question is this: how do I tell my Functions class that the user has entered the the text and clicked the OK button? I tried connecting that anotherSig Signal, but when I try to do so, Qt complains about QPixmaps not being safe to be set from a different thread, and it doesn't work.
The method that I am using here "works", but I feel it's not very reliable. Plus, it only works when all of the relevant methods in the Functions class are #classmethod - this way, for some reason, it doesn't work. The setResult is called (I added a print statement to make sure), but the grabResult still shows self.result as None.
This code is not working because the call to showDialog is happening on the instantiation of a Functions object that is an attribute of what ever object is off on the other thread. Your fun in Dialog, which you set the result on, is a different instantiation.
To move the results back to the original Functions object I think you need to connect anotherSig of the Dialog object to the setResult function on the Functions object you want to get the results back.
Does something like this work (hard to test this with out a good bit of boiler plate).
class Functions(QObject):
mysig = Signal(filename,Functions)
def __init__(self, parent=None):
super(Functions, self).__init__(parent)
self.result = None
def showDialog(self, filename):
self.mysig.emit(filename,self)
def grabResult(self):
while not self.result:
time.sleep(5)
return result #this is the question
#QtCore.Slot(str)
def setResult(self, result):
self.result = result
def connection_fun(filename,fun):
d = Dialog(filename)
# what ever else you do in here
d.anotherSig.connect(fun.setResult))
Using time.sleep causes your application to freeze. One method for making your class wait is using QEventLoop like this:
loop = QEventLoop()
myDialog.mySignal.connect(loop.quit)
loop.exec_()

Is there a generic update notification/pub sub system for GTK?

I'm building a PyGTK application with several widgets that when changed, need to notify other widgets about the change. I would like to avoid code like this:
def on_entry_color_updated(self, widget):
self.paint_tools_panel.current_color_pane.update_color()
self.main_window.status_bar.update_color()
self.current_tool.get_brush().update_color()
And do something like this instead:
def on_entry_color_updated(self, widget):
self.update_notify('color-changed')
The status bar, current color pane and current tool would subscribe to that notification event and act accordingly. From what I can tell, the GObject signaling mechanism only allows me to register a callback on a particular widget, so each object that wants to receive a notification has to be aware of that widget.
Does GTK provide such a system or should I build it myself? Developers of Shotwell, a photo organization application for GNOME, had to build their own signaling mechanism, if I understand their design doc correctly. Searching here on SO didn't turn out any definitive answers.
Edit:
Clarification why I think GObject signaling is not what I need (or just a part of what I need). With GObject, I need to explicitly connect an object to another object, like so:
emitter.connect('custom-event', receiver.event_handler)
So in my application, I would have to do this:
class ColorPane(gtk.Something):
def __init__(self, application):
# init stuff goes here...
application.color_pallette.connect('color-changed', self.update_color)
def update_color(self, widget):
"""Show the new color."""
pass
class StatusBar(gtk.Something):
def __init__(self, application):
# init stuff goes here...
application.color_pallette.connect('color-changed', self.update_color)
def update_color(self, widget):
"""Show the new color name."""
pass
class Brush(gtk.Something):
def __init__(self, application):
# init stuff goes here...
application.color_pallette.connect('color-changed', self.update_color)
def update_color(self, widget):
"""Draw with new color."""
pass
In other words, I have to pass the application object or some other object that knows about the color_pallete to other objects in my application so that they connect to color_pallette signals. This is the kind of coupling that I want to avoid.
For one, you could create a custom subclass of GObject, which provides some custom signals. The following example is a slightly adapted version of the one given in the linked article:
import pygtk
pygtk.require('2.0')
import gobject
class Car(gobject.GObject):
__gsignals__ = {
'engine-started': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
'engine-stopped': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
}
def __init__(self):
gobject.GObject.__init__(self)
self._state = 0
def start(self):
if not self._state:
self._state = 1
self.emit('engine-started')
def stop(self):
if self._state:
self._state = 0
self.emit('engine-stopped')
gobject.type_register(Car)
def kill_switch(c):
def callback(*unused, **ignored):
c.stop()
return callback
def on_start(*unused, **ignored):
print "Started..."
def on_stop(*unused, **ignored):
print "Stopped..."
some_car = Car()
some_car.connect('engine-started', on_start)
some_car.connect('engine-started', kill_switch(some_car))
some_car.connect('engine-stopped', on_stop)
some_car.start()
Another approach would be to take advantage of one of the many event/signalling packages already on PyPI, for example:
Zope Event
Louie
PyDispatcher
Darts Events
Trellis
GObjects don't have to be widgets. For example, your application class can be a GObject which defines signals that other widgets connect to.
Also, I don't think you understood the Shotwell design document correctly. It looks to me like their signalling system is 100% GObject signalling system, just with particular guarantees about the order in which signals are handled. As they say in their design document, such things are possible in plain GObject, but Vala makes it easier to code it their way.

Categories