PySide, PysideUIC and Multiple Inheritance - python

I have a dialog class that is inheriting a pyside-uic-generated python class but my problem is that it cannot be extended my adding another base class.
import sys
from PySide import QtGui
from mi_ui import Ui_Dialog
class Worker(object):
def __init__(self):
super(Worker, self).__init__()
self.data = 1
class MainDialog(QtGui.QDialog, Ui_Dialog, Worker):
def __init__(self):
super(MainDialog, self).__init__()
self.setupUi(self)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dlg = MainDialog()
print dlg.data
dlg.show()
sys.exit(app.exec_())
When I try to extend MainDialog with Worker, super does not call the Worker's __init__ and the print dlg.data fails because "AttributeError: 'MainDialog' object has no attribute 'data'"
My only work around seems to ignore super and invoke each __init__ manually.
QtGui.QDialog.__init__(self)
Worker.__init__(self)
Is this my only solution?
This is for Python 2.7.

Multiple inheritance is tricky in python. The classes that you are inheriting from cannot have any conflicts if you want it to work perfectly. In most cases multiple inheritance with pyside does cause a conflict because everything inherits QObject giving identical variables and methods. Python doesn't know which one to inherit. The painting is another area for conflict. The other thing to consider is the order of the inheritance. I believe that python tries inheritance and init from left to right. So if you only want the init method for the QtGui.QDialog and the Worker (Ui_Dialog will probably conflict) then you might want to try.
class MainDialog(QtGui.QDialog, Worker, Ui_Dialog):
In python 3 you can call the super method a little differently.
class MainDialog(QtGui.QDialog, Worker, Ui_Dialog):
super().__init__()
I believe that the way you are calling the init for 2.7 is the correct way of doing things. Questions similar to this are all over the place. It is a common problem known as the Diamond problem. Python super method and calling alternatives may explain things a little more.

Make Worker the first base-class:
class MainDialog(Worker, QtGui.QDialog, Ui_Dialog)
This will still result in MainDialog.__init__ being called first, then Worker.__init__ (which can see if you add some print statements). But you will still be able to access the data attribute from inside the MainDialog.__init__.
The Ui_Dialog class doesn't really figure in any of this, because it is just an ordinary python class inheriting from object and doesn't ever call super. So it can go anywhere you like in the base-class order.
Obviously, if you do things this way, you will have to take care not to clobber any methods from the other base-classes in the Worker class - but you already had this "problem" anyway (just in a different order).

Related

why super always need to be called in the __init__

See the screenshot of the code below. In the code block, it can be seen from the class definition part that the ResNet is a subclass of nn.Module, and when we define the __init__ function, we still call the super().__init__(). I am really confused about that, I have already look a stack overflow explanation but the accept answer is too brief so I don't get it (for example, if super().__init__() always need to be called why python don't just integrate it inside the __init__()), I was hoping a more easy to understand explanation with some examples.
The Python super() method lets you access methods from a parent class from within a child class. In other words this will work only if you are working in a child class inherited from another. This helps reduce repetition in your code. super() does not accept any arguments.
class Parent():
def __init__(self, name):
self.hair = 'blonde'
self.full_name = name + ' kurt'
class Child(Parent):
def __init__(self, brand):
super().__init__('franz')
print(self.fullname) # franz kurt
# inherited blonde but the phenotype of the hair in child is different
# so the attribute was overwrited
self.hair = 'dark hair'
One core feature of object-oriented programming languages like Python is inheritance. Inheritance is when a new class uses code from another class to create the new class.
When you’re inheriting classes, you may want to gain access to methods from a parent class. That’s where the super() function comes in.
Reference: https://realpython.com/python-super/

Initializing a class derived from a base type

I am creating a class called an Environment which subclasses a dictionary. It looks something like this:
class Env(dict):
"An environment dict, containing the parent Env (or None) where created."
def __init__(self, parent=None):
self.parent = parent
# super().__init__() <-- not included
Pylint complains that:
super-init-not-called: __init__ method from base class 'dict' is not called.
What does doing super() on a dict type do? Is this something that is required to be done, and if so, why is it necessary?
After playing around with this a bit, I'm not so sure it does anything (or maybe it automatically does the super behind-the-scenes anyways). Here's an example:
class Env1(dict):
def __init__(self, parent=None):
self.parent = parent
super().__init__()
class Env2(dict):
def __init__(self, parent=None):
self.parent = parent
dir(Env1()) == dir(Env2()), len(dir(Env1))
(True, 48)
Pylint doesn't know what dict.__init__ does. It can't be sure if there's some important setup logic in that method or not. That's why it's warning you, so that you can either decide to call super().__init__ to be safe, or to silence the warning if you're confident you don't need the call.
I'm pretty sure you don't need to call dict.__init__ when you want to initialize your instances as empty dictionaries. But that may be dependent on the implementation details of the dict class you're inheriting from (which does all of its setup in the C-API equivalent __new__). Another Python implementation might do more of the setup work for its dictionaries in __init__ and then your code wouldn't work correctly.
To be safe, it's generally a good idea to call your parent class's __init__ method. This is such broad advice that it's baked into Pylint. You can ignore those warnings, and even add comments to your code that will suppress the ones that don't apply to certain parts of your code (so they don't distract you from real issues). But most of the warnings are generally good to obey, even if they don't reflect a serious bug in your current code.
Calling super() is not required, but makes sense if you want to follow OOP, specifically, the Liskov substitution principle.
From Wikipedia, the Liskov substitution principle says:
If S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of the program.
In plain words, let S is a subclass of T. If T has a method or attribute, then S also has it. Moreover if T.some_method(arg1, arg2,...,argn) is a proper syntax, then S.some_method(arg1, arg2, ..., argn) is also a proper syntax and the output is identical. (There is more to it, but I skip it for simplicity)
What does this theory mean for our case? If dict has any attributes (except parent) declared during the init, they are lost, and the Liskov substitution principle is violated. Please check the following example.
class T:
def __init__(self):
self.t = 1
class S(T):
def __init__(self, parent=None):
self.parent = parent
s = S()
s.t
raises the error because class S does not have access to the attribute t.
Why no error is in our case? Because there are no attributes created inside __init__ in the parent class dict. Therefore, the extension works well and does not violate OOP.
To fix PyLint issue, change the code as follows:
class Env(dict):
def __init__(self, parent=None):
super().__init__() # get all parent's __init__ setup
self.parent = parent # add your attributes
It does just what the documentation teaches us: it calls the __init__ method of the parent class. This does all of the initialization behind the attributes you supposedly want to inherit from the parent.
In general, if you do not call super().__init__(), then your object has only the added parent field, plus access to methods and class attributes of the parent. This will work just fine (except for the warning) for any class that does not use initialization arguments -- or, in particular, one that does not initialize any fields on the fly.
Python built-in types do what you expect (or want), so your given use is okay.
In contrast, consider the case of extending your Env class to one called Context:
class Context(Env):
def __init__(upper, lower):
self.upper = upper
self.lower = lower
ctx = Context(7, 0)
print(ctx.upper)
print(ctx.parent)
At this last statement, you'll get a run-time fault: ctx has no attribute parent, since I never called super().__init__() in Context.__init__

How to Use QTimer With a QWidget (if possible)

I need some help figuring out how to create a timer under a widget class. I've got the following class:
class TimerClass(QtGui.QWidget):
def __init__(self, parent = None):
super(TimerClass, self).__init__()
and I'm trying to implement a timer as follows:
def start_timer(self):
timer = QtCore.QTimer(self)
timer.timeout.connect(self.__Time)
timer.start(1000)
and it calls the following:
def __Time(self):
print("Timer End")
This QWidget is called from my MainWindow and I have another timer that works without a problem as shown above with MainWindow but I can't figure out how to get it to work with QWidget. I assume the use of QWidget is the problem because I get the following error when I try and run it:
AttributeError: 'MainWindow' object has no attribute '_TimerClass__Time'
Can anyone tell me what I'm doing wrong or what the proper way of doing this would be?
The only way to get that error from the code you've posted, is if the MainWindow class inherits the TimerClass, and then an instance of MainWindow tries to call self.__Time().
That cannot work, because double-underscored attributes are only directly accessible to the class that defines them. If you rename the method to have only one underscore, the error will go away.
However, using multiple inheritance with QObject subclasses (such as QWidget) is generally a bad idea, and should be avoided. Use delegation instead.

multi inherit from QObject and QRunnable error

I'm using pyqt4.
I have a class multi inherited from QObject and QRunnable like this:
class DownloadTask(QObject, QRunnable):
def __init__(self):
QObject.__init__(self)
QRunnable.__init__(self)
self.setAutoDelete(False)
When an instance of DownloadTask is initializing, the last line throws exception:
TypeError: could not convert 'DownloadTask' to 'QRunnable'
But I think it is correct in grammer, QRunnable has the method setAutoDelete. Why can't it convert to QRunnable?
Update:
I intend to use QThreadPool to manage multi threads downloading resources from Internet, and emit a signal after finished. How can I do that?
PyQt reference guide > Things to be Aware Of > Multiple inheritance:
It is not possible to define a new Python class that sub-classes from more than one Qt class.

QObject::connect: Cannot queue arguments of type 'object' in PySide

I am using PySide (Python Qt binding).
I have a worker thread of class QThread that updates the main GUI thread (updates the QTableWidget) through signal/slot mechanism.
In my worker thread, I have the following:
self.emit(SIGNAL("alterTable(object"), params)
In my GUI thread I have this:
self.connect(self.worker, SIGNAL("alterTable(object)"), self.updateMainTable, Qt.AutoConnection)
Since there are several similar worker threads running all connecting to the same slot (the self.updateMainTable), I should use the AutoConnection (and consequently the QueuedConnection). Using the Qt.DirectConnection works, but it is not safe (or so I have been told).
But when I try to use the AutoConnection, I get the following error:
QObject::connect: Cannot queue arguments of type 'object'
(Make sure 'object' is registered using qRegisterMetaType().)
I have Googled for eons trying to figure out a way how to use the qRegisterMetaType() in PySide, to no avail. All the resources I found online point to a C++ syntax/documentation.
If it makes any difference, the object in question is a dict most of the time.
I guess I have found an answer myself, well not exactly an answer, but a workable solution.
I switched all the Signals to the new-style syntax. In case anyone is wondering, I did that by defining a custom signal in my worker class. So my code looks something like this
class Worker(QThread):
alterTable = Signal(dict)
def __init__(self, parent=None):
....
self.alterTable.emit(parameters)
class GUI(QMainWindow):
def __init__(self, parent=None):
WorkerModule.Worker().alterTable.connect(self.myMethod)
For some reason the Signal has to be inside the QThread class; otherwise, Qt complains about "Signal has no attribute connect" error, which is very weird.
Incredibly late for the answer here, my apologies. Not enough reputation to add a comment to your accepted answer. I hope this might help new PySide/Pyside2 users who stumble upon your issue.
Issue: QObject::connect: Cannot queue arguments of type 'object'
Solution: self.connect(self.worker, SIGNAL("alterTable(PyObject)"), self.updateMainTable, Qt.AutoConnection)
Issue: Qt complains about "Signal has no attribute connect" error
Solution: The connect attribute is implemented in QObject, so you must first call the parent's init method through either QMainWindow.__init__(self) or super(GUI, self).__init__() (Py2) or also super().__init__() (Py3).
Cheers.

Categories

Resources