I am learning PyQt5 and I tried to create a extended widgets.
I am also learning OOP so I lack experience for this project.
My final goal is to have master widgets that can disable/enable some slave widgets.
So far I need check and radio buttons.
So I tried to create an abstract class that contains the extended behavior of the widgets (the management of the state of the slaves widgets):
class QDisablingWidget():
__metaclass__ = ABCMeta
def __init__(self):
self.slaveWidgets = []
self.slaveStateWhenMasterIsEnabled = {}
def addSlaveWidget(self, slaveWidget, isEnabledWhenMasterIsEnabled=True):
[...]
def updateSlaveStatus(self):
[...]
Then I create my extended widget classes:
class QDisablingCheckBox(QtWidgets.QCheckBox, QDisablingWidget):
def __init__(self, text=None, parent=None, isInMutexGroup=False):
super(QtWidgets.QCheckBox, self).__init__()
super(QDisablingWidget, self).__init__()
if text:
self.setText(text)
if parent:
self.setParent(parent)
self.isInMutexGroup = isInMutexGroup
# Click signal handling
self.stateChanged.connect(self.updateSlaveStatus)
class QDisablingRadioButton(QtWidgets.QRadioButton, QDisablingWidget):
def __init__(self, text=None, parent=None, isInMutexGroup=False):
super(QtWidgets.QRadioButton, self).__init__()
super(QDisablingWidget, self).__init__()
if text:
self.setText(text)
if parent:
self.setParent(parent)
self.isInMutexGroup = isInMutexGroup
# Click signal handling
self.toggled.connect(self.updateSlaveStatus)
You can already see the problem:
I need to connect my self.updateSlaveStatus to the correct signals (stateChanged and toggled) so I added it in the constructor of the derived classes.
Recently I also added the isInMutexGroup argument for some implementation reasons and I realize that I am duplicating the code in both derived classes...
It is the first time I try to use OOP "for real" (first attempt of multiple inheritance and abstract class), so even if I know I am breaking the beauty of the OOP concept, I don't know what to do to get a nice class hierarchy...
So basically, I am looking for a solution on this example. But I am also looking for guidelines, general advice, tutorials, etc. Actually anything that could help me!
Thank you for your help.
Even if I got a downvote for that question, I think some beginners could be interested by the solution I found:
I have my extension abstract class like that:
class QDisablingWidget(QtCore.QObject):
__metaclass__ = ABCMeta
def __init__(self, isInMutexGroup=False, **kwds):
[...]
Then I can derive class like that:
class QDisablingCheckBox(QtWidgets.QCheckBox, QDisablingWidget):
def __init__(self, **kwds):
super().__init__(**kwds)
# On click signal handling
self.stateChanged.connect(self.updateSlaveStatus)
and
class QDisablingRadioButton(QtWidgets.QRadioButton, QDisablingWidget):
def __init__(self, **kwds):
super().__init__(**kwds)
# On click signal handling
self.toggled.connect(self.updateSlaveStatus)
Finally, when I use the classes I need to create object like that:
disablingRadioBut = QWidgets.QDisablingRadioButton(text="My button",
parent=self,
isInMutexGroup=True)
I.e I must use keywords explicitly so that each constructors will eat the kewords the use/know.
Thanks to this approach I have maximum reusability of my extension class.
I got this solution here:
http://pyqt.sourceforge.net/Docs/PyQt5/multiinheritance.html
And more details here:
http://rhettinger.wordpress.com/2011/05/26/super-considered-super/
A very nice article!
Related
I'm in scenario where I want to refactor several classes which have identical and/or similar methods. The number of class are around ~20 and the number of similar methods are around ~15. All sorts of combinations exist within this space, which is why I'm a bit reluctant to using inheritance to solve this issue (rightfully?).
The code is part of a wrapper around another application that is controlled by a com api. The wrapper in turn is part of a package that is distributed internally at the company where I work. Therefore the interfaces of the classes have to remain the same (for backwards compatibility).
This example illustrates some very simplified versions of the classes:
class FirstCollectionLike:
def __init__(self):
self._collection = list()
def add(self, arg):
self._collection.append(arg)
def remove(self, index):
del self._collection[index]
class SecondCollectionLike:
def __init__(self):
self._collection = list()
self._resource = some_module.get_resource()
def start(self):
some_module.start(self.resource)
def add(self, arg):
self._collection.append(arg)
def remove(self, value):
self._collection.remove(value)
class SomeOtherClass:
def __init__(self):
self._some_attribute = 0
self._resource = some_module.get_resource()
def add(self, value):
self._some_attribute += value
def start(self):
some_module.start(self._resource)
Are there any design patterns I could look into that would help me solve this issue?
My initial thought was to create method classes like Add, RemoveByIndex and RemoveByName that implements __call__ like so:
class Add:
def __init__(self, owner):
self.owner = owner
def __call__(self, item):
self._collection.append(item)
class AddAndInstantiate:
def __init__(self, owner, type_to_instantiate):
self.owner = owner
self.type_to_instantiate = type_to_instantiate
def __call__(self, name):
self._collection.append(type_to_instantiate(name))
and then assign instances of those classes as instance attributes to their respective owner objects:
class RefactoredClassOne:
def __init__(self):
self.add = Add(self)
self.remove = RemoveByIndex(self)
class RefactoredClassTwo:
def __init__(self):
self.add = AddAndInstantiate(self, SomeClass)
self.remove = RemoveByName(self)
This way I could quite easily add any method I want to a class and provide some arguments to the method class if needed (like the type of the class to instantiate in the example above). The downside is that it is a bit harder to follow what is happening, and the automatic documentation generation we use (sphinx) does not work if the methods are implemented in this way.
Does this seem like a bad approach? What are the alternatives?
First, if your classes are as simple as you example suggest, I'm not sure OOP is the right tool. What your classes are doing is just renaming a couple of basic calls. This is useless abstraction and IMO a bad practice (why force me to look to into the SecondClassCollectionLike.py file to discover that .add() is 1) in fact a wrongly named append and 2) that my collection is in fact a listwith a fancy name?)
In that case I'd say that a functional approach might be better, and a workflow such as:
a = SecondClassCollectionLike()
a.add("x")
a.add("y")
a.remove(0)
a.start()
would be a lot clearer if it looked like
a = list()
a.append("x")
a.append(y)
del a[0]
somemodule.start()
If your classes are in fact more complex and you really want to keep the OOP approach, I think that this solution is probably close to your solution and what you're looking for.
The idea is to have modules which hold the logic. For example a _collection_behaviour.py module, which holds the add(), remove(), increment() or whatever. And a _runtime.py module, which holds that start(), stop(), etc. logic.
This way you could have classes which exibit behaviour from these modules:
calss MyClass():
def __init__(self):
self._collection = list()
from ._collection_behaviour import add
from ._collection_behaviour import remove
from ._runtime import start
But I do not see the point in making these functions classes which implement __call__ if that's all they do.
I have a problem about analyzing these lines of code, if you want to view the whole code I can post it.
Can you guys please explain whats going on here?
I'm planning to create a database program with tkinter and sqlite, I have a windows PC.
I didnt write the code I just want to study it.
class meh(Tkinter.Frame):
def __init__(self, parent):
Tkinter.Frame.__init__(self, parent)
self.parent=parent
self.initialize_user_interface()
Whats going on here?
def __init__(self, parent):
Tkinter.Frame.__init__(self, parent)
Python supports object-oriented programming. One of the core concepts in OOP is inheritance, which means that a class can "inherit" methods from another class. For instance
class Animal:
def __init__(self, name): # The __init__ function is called when an object is
self.name = name # instantiated. That's called a "constructor"
print('Created ' + name)
def make_sound(self):
print("...")
def die(self):
print(self.name + ' died.')
class Cat(Animal): # The class in the parenthesis is the superclass.
def make_sound(self): # A cat makes a different sound from any other animal,
print("Meow") # so we give it its own make_sound method. But it dies
# the same as any other animal, so it will inherit
# the die method from the superclass.
Now when we try this code:
>>> fluffy = Animal('Fluffy')
Created Fluffy
>>> fluffy.make_sound()
...
>>> fluffy.die()
Fluffy died.
>>> kitty = Cat("Kitty")
Created Kitty
>>> kitty.make_sound()
Meow
>>> kitty.die()
Kitty died.
So now, let's look at the code you gave:
class meh(Tkinter.Frame):
This line creates a class that inherits from Tkinter.Frame. That means that it should be able to do anything that a Frame can do, which will probably be things like containing UI elements.
def __init__(self, parent):
This is a constructor that gets called when you create a new meh. It takes parent as a parameter. I'd check the documentation, but in UI development, that often means interface element that this one should appear inside. For instance, the parent might be a window or another frame.
Tkinter.Frame.__init__(self, parent)
This is calling the __init__ method of the class Tkinter.Frame. This basically means that instead of defining something to do instead of what Tkinter.Frame does in its constructor, they're defining things to do in addition to Tkinter.Frame.__init__.
In Functions class, I would like to access the variable of the Frame class.
Please tell me if there is any way.
class Functions():
def changeText():
...
...
I want to change the 'text' in the Frame class
ex )Frame.text.SetFont('change text')
GUI element
class Frame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, ....)
....
....
self.text = wx.StaticText(panel, .....)
You can do this by sending an instance of the class to the function:
class myClass(object):
def __init__(self, text):
self.text = text
def changeText(input):
input.text = "world"
example = myClass("hello")
changeText(example)
You will have to tell your objects what to work on. Out of thin air your Functions instance will not know (how should it?) what Frame should be. You could make Frame a global, but I do not think that is a good idea (it will break if you want to work with more than one frame instance). So you would write:
class Functors:
...
def set_text(txt_frame, the_text):
"""txt_frame has to be a :class:`my_txt_frm` instance with ``self.text`` being a ``StaticText`` instance."""
txt_frame.text.SetLabel(the_text)
class my_txt_frm(wx.Frame): # do not name the derived class Frame to make more clear it is derived!
def __init__(# ...
...
self.text = wx.StaticText(#...
So now comes the interesting part: how to tie the parts together? You have to have something like that somewhere in your code:
funct = Functors() # the class which know how to do things on our GUI elements
frm = my_txt_frm(#...
Some lines later...
funct.set_text(frm, 'thenewtext')
So for your app which has the bigger picture it is necessary to keep references to the building blocks to be able to tie them together later.
An orderly way to tie things together is called MVC (see a great example in the wxPython wiki). Even if you do not want to model your app after this paradigm, you can learn from it how to reason about separation of concerns.
I've watch Raymond Hettingers Pycon presentation and read his Python’s super() considered super! as well as many questions here on Stackoverflow, but I've encounter a problem with inheritance that I can't seem to find an answer for. I'm going to use my problem as an example, but I'm interested in knowing why it doesn't work rather than how to fix it.
I have 3 classes I want to inherit from, both so that I can use their methods but also so that I can pass an instance of my own class in other functions or methods.
None of the inherited classes calls super().__init__(**kwargs) in their initializer, so I tried making an adapter class for each class, and a Root class to catch the remaining keyword arguments:
class Root(object):
def __init__(**kwargs):
pass
class Rect(Root):
def __init__(self, pos=(0, 0), size=(16, 16), **kwargs):
self.rect = pygame.Rect(pos, size)
super().__init__(**kwargs)
class Sprite(Root):
def __init__(self, **kwargs):
self.sprite = pygame.sprite.Sprite()
super().__init__(**kwargs)
class Surface(Root):
def __init__(self, size, **kwargs):
self.image = pygame.Surface(size).convert()
super().__init__(**kwargs)
class Gameobject(Rect, Sprite, Surface):
def __init__(self, **kwargs):
super().__init__(**kwargs)
This doesn't work because the methods of each class are not inherited to the GameObject class, rather it inherit just instances of each class.
My other attempt was to inherit both Root and their respective class from pygame for each adapter class, for example:
class Rect(Root, pygame.Rect):
def __init__(self, **kwargs):
super().__init__(**kwargs)
This leads to an error: "TypeError: multiple bases have instance lay-out conflict", which I've understood to be a conflict between the built-in implementation or something similar (not quite sure if I understood correctly).
How will I go on from here? Where am I doing wrong? Thanks in advance!
I'm trying to implement Qt's Color Editor Factory Example (http://doc-snapshot.qt-project.org/4.8/itemviews-coloreditorfactory.html) in PySide.
The problem I'm facing is that QStandardItemEditorCreator class is not in PySide, or I haven't been able to find it after searching the docs for a long time. The only reference to it in the PySide documentation can be found in the following page (http://www.pyside.org/docs/pyside/PySide/QtGui/QItemEditorCreatorBase.html), the relevant part being the following:
QStandardItemEditorCreator is a convenience template class that can be used to register widgets without the need to subclass PySide.QtGui.QItemEditorCreatorBase .
Without any link to QStandardItemEditorCreator.
In short, how may I get QStandardItemEditorCreator's functionality in PySide?
Thanks.
I ended up implementing QStandardItemEditorCreator in python. Here's my implementation:
class QStandardItemEditorCreator(QItemEditorCreatorBase):
def __init__(self, cls):
super(QStandardItemEditorCreator, self).__init__()
self.propertyName = cls.staticMetaObject.userProperty().name()
self.cls = cls
def createWidget(self, parent):
return self.cls(parent)
def valuePropertyName(self):
return self.propertyName
If anyone has a better answer I'll gladly choose yours over mine.