Passing Generic type to inner class - python

In this code, I want to pass the type T to the inner class Emitter.
T = TypeVar('T')
class MySignal(Generic[T]):
class Emitter(QtCore.QObject,Generic[T]):
signal = Signal(T)
def __init__(self):
super(MySignal.Emitter, self).__init__()
def __init__(self):
self.emitter = MySignal.Emitter[T]()
def emit(self,*args,**kw):
self.emitter.signal.emit(*args,**kw)
It doesn't behave as expected.
If I do
minMaxChanged=MySignal[tuple]()
Then minMaxChanged.emitter.__orig_class__.__args__[0] looks like T~ instead of tuple. The minMaxChanged class itself is as expected.
Generally, specifying Signal(typ) results in typ being the argument type expected to be passed to emit function. Consequently, in this case, Signal is called as if it were called without args, and calling emit with 1 argument fails since it expects no args.
I also expected the code
T = TypeVar('T')
class MySignal(Generic[T]):
class Emitter(QtCore.QObject):
signal = Signal(T)
def __init__(self):
super(MySignal.Emitter, self).__init__()
def __init__(self):
self.emitter = MySignal.Emitter()
def emit(self,*args,**kw):
self.emitter.signal.emit(*args,**kw)
to work. However, the exact same issue remains.
Update
Notice that this is not a redundant complication. This class (partially brought here) is meant to wrap a Qt Signal which can only be omitted from class that is a Qt class. It meant to provide a generic signal wrapper. See how to emit signal from a non PyQt class?
Trying double inheritance MySignal(QtCore.QObject, Generic[T]) resulted in a c++ style crash.

#ekhumu suggestion indeed works. I hoped for something prettier.
class MySignal:
def __init__(self,typ):
Emitter = type('Emitter', (QtCore.QObject,), {'signal': Signal(typ)})
self.emitter = Emitter()
def emit(self,*args,**kw):
self.emitter.signal.emit(*args,**kw)
def connect(self, slot):
self.emitter.signal.connect(slot)

Related

How does Python support this common problem related to run time polymorphism?

I am trying to exectute the below code but I get errors.
class base:
def callme(data):
print(data)
class A(base):
def callstream(self):
B.stream(self)
def callme(data):
print("child ", data)
class B:
def stream(data):
# below statement doesn't work but I want this to run to achieve run time
# polymorphism where method call is not hardcoded to a certain class reference.
(base)data.callme("streaming data")
# below statement works but it won't call child class overridden method. I
# can use A.callme() to call child class method but then it has to be
# hardcoded to A. which kills the purpose. Any class A or B or XYZ which
# inherits base call should be able to read stream data from stream class.
# How to achive this in Python? SO any class should read the stream data as
# long as it inherits from the base class. This will give my stream class a
# generic ability to be used by any client class as long as they inherit
# base class.
#base.callme("streaming data")
def main():
ob = A()
ob.callstream()
if __name__=="__main__":
main()
I got the output you say you're looking for (in a comment rather than the question -- tsk, tsk) with the following code, based on the code in your question:
class base:
def callme(self, data):
print(data)
class A(base):
def callstream(self):
B.stream(self)
def callme(self, data):
print("child", data)
class B:
#classmethod
def stream(cls, data):
data.callme("streaming data")
def main():
ob = A()
ob.callstream()
if __name__=="__main__":
main()
Basically, I just made sure the instance methods had self parameters, and since you seem to be using B.stream() as a class method, I declared it as such.

Pythonic way to encapsulate method arguments of a class

Objects of my class A are similar to network connections, i.e. characterized by a handle per connection opened. That is, one calls different methods with a handle (a particular connection) as argument. My class A (python 2.7) looks like:
class A(object):
def __init__(self, *args):
... some init
def my_open(self, *args)
handle = ... some open
return handle
def do_this(self, handle, *args):
foo_this(handle, args)
def do_that(self, handle, *args):
foo_that(handle, args)
A typical usage is
a = A(args)
handle = a.my_open(args2)
a.do_this(handle, args3)
Now, in a particular situation, there is only one connection to take care of, i.e. one handle in play. So, it is reasonable to hide this handle but keep class A for the more general situation. Thus, my first thoughts on a class B
which "is a" kind of class A (usage stays the same but hides handle) are:
class B(A):
def __init__(self, *args):
super(A, self).__init__(args)
self.handle = None
def my_open(self, *args):
self.handle = super(A, self).__init__(args)
def do_this(self, *args):
super(A, self).do_this(self.handle, args)
def do_that(self, *args):
super(A, self).do_that(self.handle, args)
Unfortunately, in my opinion, it seems very convoluted. Any better ideas?
Objects of my class A are similar to network connections, i.e. characterized by a handle per connection opened. That is, one calls different methods with a handle (a particular connection) as argument.
You have inverted the responsibility. The handle object holds the state the methods operate on, so those methods should live on the handle, not the factory.
Move your methods to the handle object, so the API becomes:
a = A(args)
handle = a.my_open(args2)
handle.do_this(args3)
The class implementing the handle() could retain a reference to a if so required; that's an implementation detail that the users of the API don't need to worry about.
You then return new handles, or a singleton handle, as needed.
By moving responsibility to the handle object, you can also make your factory produce handles of entirely different types, depending on the arguments. A(args).my_open(args2) could also produce the singleton handle that you now have class B for, for example.
How about a class for the handle itself?:
class Handle(object):
def __init__(self, *args):
# init ...
self._handle = low_level_handle
def do_this(self, *args):
# do_this ...
pass
def do_that(self, *args):
# do_that
pass
class A(object):
def __init__(self, *args):
# init ...
def my_open(self, *args):
handle = Handle(args)
# handle post-processing (if any)
return handle
e.g.:
a = A(args)
handle = a.my_open(args2)
handle.do_this(args3)

Emit existing signal with custom type

I would like to "increase" an existing signal (currentIndexChanged in example above) to make it able to return another type:
from PySide import QtCore, QtGui
class MyClass(object):
pass
class MyClassComboBox(QtGui.QComboBox):
#QtCore.Signal(int, result=MyClass)
def currentIndexChanged(self, *args):
my_class_instance = self._id_to_my_class(args[0])
return my_class_instance
class MyClassWidget(QtGui.QWidget):
def __init__(self, *args, **kwargs):
super(MyClassWidget, self).__init__(*args, **kwargs)
cb = MyClassComboBox(self)
cb.currentIndexChanged[MyClass].connect(self.do_it)
def do_it(self, *args):
assert isinstance(args[0], MyClass)
print args
As you guess, this code doesn't work because of #QtCore.Signal(int, result=MyClass). As you guess, the idea is to register an existing signal (currentIndexChanged) to return a given, custom type (MyClass).
Any idea on how I can do this?
Messing with existing Qt signals doesn't sound like a good idea. If you need to pass additional or modified arguments to a slot when a signal is emitted, you can do so with a lambda function.
While it's not entirely clear what you are trying to achieve from your example, you probably want something like this
cb.currentIndexChanged.connect(lambda i: self.do_it(MyClass(i)))

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

PyQt signal with arguments of arbitrary type / PyQt_PyObject equivalent for new-style signals

I have an object that should signal that a value has changed by emitting a signal with the new value as an argument. The type of the value can change, and so I'm unsure of how to write the signal type. I know that I can acconmplish this using old-style signals like this:
self.emit(SIGNAL("valueChanged(PyQt_PyObject)"), newvalue)
but how would I write this using new-style signals?
I am aware of a previous question related to this but no "real" answer was given there.
First, the object you're emitting from needs the signal defined as an attribute of its class:
class SomeClass(QObject):
valueChanged = pyqtSignal(object)
Notice the signal has one argument of type object, which should allow anything to pass through. Then, you should be able to emit the signal from within the class using an argument of any data type:
self.valueChanged.emit(anyObject)
I'm a beginner and this is the first question I'm attempting to answer, so apologies if I have misunderstood the question...
The following code emits a signal that sends a custom Python object, and the slot uses that
class to print "Hello World".
import sys
from PyQt4.QtCore import pyqtSignal, QObject
class NativePythonObject(object):
def __init__(self, message):
self.message = message
def printMessage(self):
print(self.message)
sys.exit()
class SignalEmitter(QObject):
theSignal = pyqtSignal(NativePythonObject)
def __init__(self, toBeSent, parent=None):
super(SignalEmitter, self).__init__(parent)
self.toBeSent = toBeSent
def emitSignal(self):
self.theSignal.emit(toBeSent)
class ClassWithSlot(object):
def __init__(self, signalEmitter):
self.signalEmitter = signalEmitter
self.signalEmitter.theSignal.connect(self.theSlot)
def theSlot(self, ourNativePythonType):
ourNativePythonType.printMessage()
if __name__ == "__main__":
toBeSent = NativePythonObject("Hello World")
signalEmitter = SignalEmitter(toBeSent)
classWithSlot = ClassWithSlot(signalEmitter)
signalEmitter.emitSignal()

Categories