I am working on a project involving PyQt5, and I am struggling with managing inheritance between widgets.
I have one QWidget screen that inherits off QtWidgets.QWidget and another class which is generated by QtDesigner. It reads something like this:
class a(QtWidgets.QWidget, Ui_a):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.setupUi(self)
<some attributes>
<some functions
Here, I inherit off Ui_a, a separate class stored in a generated file, and I can call setupUi (a method of Ui_a) fine.
I now want to create another class b, which also is a QWidget that needs to be displayed. This class b requires the use of some of the functions and attributes from class a. I can easily just copy paste the required stuff in but that is bad practice so I am looking for a more neat solution. If I do the code:
class b(QtWidgets.QWidget, Ui_b, a):
def __init__(self, parent=None):
QtWidgets.QWidget.__init(self, parent)
self.setupUi(self)
This then crashes with an error message saying that it cannot create a consistent method resolution order.
My first question is - I know I need to call the init method of class a since a's attributes are created there, but I don't know how.
My second question is - How do I fix this MRO error and succeed in creating the new class b which can use a's attributes and functions?
You are trying to mix in the parent classes before the derived class. There is no need to, just inherit directly from a and the new Ui_b and nothing else:
class b(a, Ui_b):
# *no* __init__ needed either
a already pulls in QtWidgets.QWidget.
Now, Ui_a and Ui_b may not play well together. You may have to invoke both Ui_a.setupUi() and Ui_b.setupUi(). You can do so explicitly:
class b(a, Ui_b):
def __init__(self, parent=None):
super().__init__(parent)
# Ui_b.setupUi would have shadowed Ui_a.setupUi, so
# call the latter explicitly
Ui_a.setupUi(self, self) # unbound
It may be that Ui_a and Ui_b can't be mixed at all. In that case you should just pull out all the methods you want to re-use into a separate base class and have both a and b inherit from that:
class SharedStuff:
# ...
class a(QtWidgets.QWidget, SharedStuff, Ui_a):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.setupUi(self)
class b(QtWidgets.QWidget, SharedStuff, Ui_b):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.setupUi(self)
Your question is slightly unclear, but I am going to assume that Ui_a and Ui_b aren't both classes generated from Qt Designer files - because there is no sane way of inheriting from two such classes.
I'm guessing your current classes are equivalent to this:
class A(QtWidgets.QWidget, Ui_FromQtDesigner):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.setupUi(self)
def methodA(self):
print('A:', self.windowTitle())
class B(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
def methodB(self):
print('B:', self.windowTitle())
and you want a class C which will be a QWidget subclass that gets both the Qt Designer UI elements and the custom methods from classes A and B.
Now, Qt uses single inheritance almost exclusively. Inheriting from two QWidget classes is usually an error, unless the classes share a common set of base-classes (and even then, there are a number of limitations that still make it a questionable design choice - for more details, see this blog post).
In your case, it would be better to use a simple mixin class. It is only really necessary for class A to inherit from QWidget, so the set of classes can be defined like this:
class WidgetA(QtWidgets.QWidget, Ui_FromQtDesigner):
def __init__(self, parent=None):
super(WidgetA, self).__init__(parent)
self.setupUi(self)
def methodA(self):
print('A:', self.windowTitle())
class WidgetMixinB(object):
def methodB(self):
print('B:', self.windowTitle())
class WidgetC(WidgetA, WidgetMixinB):
pass
And if you also want to use WidgetMixinB as a widget in its own right, you can create another class for it like this:
class WidgetB(QtWidgets.QWidget, WidgetMixinB):
pass
Related
Using python 2.7 with new class style, if my class inherits from Objectclass, what is the behavior of super(ClassName, self).__init__() ? What I mean is, what happens behind the scenes ? What is the difference if I omitted it ?
An example above:
class ClassName(object):
"""docstring for ClassName"""
def __init__(self, arg):
super(ClassName, self).__init__() ## The question above is about this super
self.arg = arg
class OtherClass(ClassName):
"""docstring for OtherClass"""
def __init__(self, arg, otherArg):
super(OtherClass, self).__init__(arg) ## The question below is about this super
self.otherArg = otherArg
If I omit the super, what is the happens behind the scenes ?
Thanks.
In the case of single inheritance, absolutely nothing. object.__init__() does squat.
In the case of multiple inheritance, one entire chain of initialization is not called. What this does to your application varies, but it's generally not a good thing to happen.
Im making a QTableWidget in Pyqt and ran into a bit of an annoying hiccup.
I need to use widgets in my table for its functionality, so im using setCellWidget to add them to the table. However, widgets dont have the same methods available as QTableWidgetItem's do (especially regarding selection in the table).
Im wondering if its possible to do something subclassing both items, so i can have the methods of both, and how i woulda dd that to the table.
Something like:
class TableItem(QtGui.QTableWidgetItem, QtGui.QWidget):
def __init__(self, parent=None):
super(TableItem, self).__init__(parent)
self.check = QtGui.QCheckBox()
self.label = QtGui.QLabel('Some Text')
self.h_box = QtGui.QHBoxLayout()
self.h_box.addWidget(self.check)
self.h_box.addWidget(self.label)
and then somehow add that to my table as a TableWidgetItem so it displays widgets and also has selection methods available.
Any ideas here?
For reference:
setCellWidget: http://pyqt.sourceforge.net/Docs/PyQt4/qtablewidget.html#setCellWidget
QWidget: (easy to find, i cant post more than 2 links)
-Which doesnt have the nice methods for a table
QTableWidgetItem: http://pyqt.sourceforge.net/Docs/PyQt4/qtablewidgetitem.html#type
with isSelected and setSelected (Methods not avialble from a widget used in setCellWidget.
To return the widget in a cell you can use table.cellWidget(row, column) and then use your widgets methods on that. But beacuse setSelected and isSelected arent methods of a widget, you cant check for selection. I was hoping to subclass the two together to allow for both
--Basically I need to know how to get my class to 'return' the proper type when i call it to add to the table with setItem
I am not sure what you want to do but you could "inject" a method like:
class TableWidgetItem(QtGui.QTableWidgetItem):
def __init__(self, parent=None):
QtGui.QTableWidgetItem.__init__(self)
def doSomething(self):
print "doing something in TableWidgetItem"
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self)
tableWidgetItem = TableWidgetItem()
widget = Widget()
def widgetFunction(self):
tableWidgetItem.doSomething()
# as an instance method
settatr(widget, "widgetFunction", MethodType(widgetFunction, widget, type(widget)))
# or as a class method
settatr(widget, "widgetFunction", widgetFunction)
Then you can:
>>>widget.widgetFunction()
doing something in TableWidgetItem
(not tested)
First off this question is based around PySide, but it is a general question of inheritance for class attributes.
So I have an inheritance problem. I basically want to inherit from 2 PySide GUI classes. Multiple inheritance had major conflicts and gave errors. Basically, I made a custom widget and want to make that same widget into a dock widget (floating window).
One way that I found easy to implement was to override the getattr method to redirect attribute calls, like below.
class DockWidget(QtGui.QDockWidget):
def __init__(self):
super().__init__()
self.test = Test()
# Container is a custom widget containing the Test widget and a toolbar.
self.setWidget(self.test.getCustomContainer())
def __getattr__(self, name):
"""Call a property's method when the given attribute name is not found.
Note: Gives full access to the test attribute.
Args:
name (str): Method or property name.
"""
if hasattr(self.test, name):
return self.test.__getattribute__(name)
# end __getattr
# end class DockWidget
class Test(QtGui.QWidget):
def doSomething(self, msg):
print(msg)
# end doSomething
# end Test
widg = DockWidget()
widg.doSomething("Test")
I would like to know if this is considered really bad, and if there is a better way.
Since DockWidget and Test both inherit QWidget, you can use a mixin. This would allow you to do things like reimplement virtual methods, which would not be possible using __getattr__.
class WidgetMixin(object):
def doSomething(self, msg):
print(msg)
def closeEvent(self, event):
print(self.__class__.__name__)
class Test(WidgetMixin, QtGui.QWidget):
def __init__(self):
super().__init__()
class DockWidget(WidgetMixin, QtGui.QDockWidget):
def __init__(self):
super().__init__()
I am just wondering if there is a shortcut to setting default attributes.
I generally would do
class class1(object):
def __init__(self, att1='X'):
self.att1 = att1
Is there a shortcut in the lines off
class class1(object):
def __init__(self, self.att1='X'):
I assume at the moment of the call, the object does not exist, so seem logical that this way does not work, but maybe there is sort of less verbose way to deal with this when there is a lot more attributes to be set.
Any ideas?
Try something like:
class class1(object):
att1='X'
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
I have a wxPython application with some code like below. I want to set a value of an attribute of the class MyFrame, but I can't reference to it.
How can I make this code work?
class MyFrame1(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.gauge_1 = wx.Gauge(self, -1)
self.notebook_1=myNotebook(self, -1)
class myNotebook(wx.Notebook):
def __init__(self, *args, **kwds):
wx.Notebook.__init__(self, *args, **kwds)
self.other_class_1=other_class()
self.other_class_1.do_sth()
class other_class(object):
def do_sth(self):
gauge_1.SetValue(value) #doesn't work of course, how do I do this?
I think its somewhat poor designer for a child UI element to have specific knowledge about its parent. Its a backwards design. Children should usually have some way of signaling or raising an event and letting the appropriate listener react. But, if this is really want you want to do, then you probably want to get the parent item and act on it directly...
Note: Don't do this approach. I'm illustrating why the design has issues...
First off, you can't even do it with the way the code is structured, because other_class has no reference to a parent. Its a generic instance. So you would have to do something like...
class other_class(object):
def __init__(self, parent):
self.parent = parent
And in your Notebook class...
class myNotebook(wx.Notebook):
def __init__(self, *args, **kwds):
wx.Notebook.__init__(self, *args, **kwds)
# note here we pass a reference to the myNotebook instance
self.other_class_1 = other_class(self)
self.other_class_1.do_sth()
Then once your other_class now knows about its parent, you have to get the parent of the parent to have the MyFrame1 instance...
class other_class(object):
def __init__(self, parent):
self.parent = parent
def do_sth(self, value):
self.parent.GetParent().gauge_1.SetValue(value)
Do you see now why its a bad design? Multiple levels of objects have to assume knowledge of the parent structure.
I'm not up on my wxPython so I can't give you specifics, but here are some possible general approaches to consider:
Determine what the role of other_class really is. If its really meant to operate on children of your MyFrame1, then that functionality belongs under MyFrame1 so it can have knowledge of those members.
If other_class were a wx object, it could emit a wx.Event when the do_sth() method is called. You could bind that event at the MyFrame1 or Notebook level and do whatever work is needed in the handler.
Try something like this:
class other_class(object):
def __init__(self):
self.g1=MyFrame1()
def do_sth(self):
self.g1.gauge_1.SetValue(value)