I am trying to create a PyQt5.QtWidgets.QWidget derived widget, whilst at the same time inherit from another base class, Base.
Inheriting from Base causes an error when calling the QWidget constructor, saying I never provided the arguments which Base requires.
This is how I attempt to call the QWidget and Base constructors:
class Deriv(QtWidgets.QWidget, Base):
def __init__(self):
QtWidgets.QWidget.__init__(self)
Base.__init__(self, "hello world")
The error I'm getting says:
QtWidgets.QWidget.__init__(self)
TypeError: __init__() missing 1 required positional argument: 'id'
Is it possible to inherit from both QWidget and Base, and if so, how do I call their respective constructors correctly?
Here is an exemplar app which reproduces the error I am experiencing:
#!/usr/bin/env python3
import sys
from PyQt5 import QtWidgets
class Base(object):
def __init__(self, id):
self.id = id
class Deriv(QtWidgets.QWidget, Base):
def __init__(self):
QtWidgets.QWidget.__init__(self)
Base.__init__(self, "hello world")
self.show()
def main():
app = QtWidgets.QApplication(sys.argv)
d = Deriv()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Here is the traceback:
Traceback (most recent call last):
File "./test.py", line 22, in <module>
main()
File "./test.py", line 18, in main
d = Deriv()
File "./test.py", line 11, in __init__
QtWidgets.QWidget.__init__(self)
TypeError: __init__() missing 1 required positional argument: 'id'
Note that if I only inherit from QWidget (so just remove everything referring to Base), then the code works
class Deriv(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.show()
I'm assuming QtWidgets.QWidget.__init__ itself uses super to call __init__. With your single inheritance case, the MRO consists of three classes: [Deriv, QtWidgets.QWidget, object]. The call super(QtWidgets.QWidget, self).__init__ would call object.__init__, which doesn't expect any additional arguments.
In your multiple inheritance example, the MRO looks like
[Deriv, QtWidgets.QWidget, Base, object]
which means now the call super(QtWidgets.QWidget, self).__init__ refers not to object.__init__, but Base.__init__, which does require an additional argument, leading to the error you see.
Related
edit : Problem solved, I was indeed a idiot and it was indeed a very silly mistake. The solution was that I forgot to give an instance of UI when creating a Joueur instance... Sorry and thanks to all who tried to help me.
I've been trying to code a chess game. I am now in the process of testing it and I got a "weird" error (from my experience I probably just messed up, but my situation is not exactly what people on every other post on this error seem to be and I have been searching my code and documentation for clues for some hours with no success : so here I am).
So to summarize the code (I won't put code I have successfully run before and will only show what should be relevant):
I have a board class which is basically my model and my controller. In the constructor I ask for arguments two players, a UI class, and a hash class. Before I implemented the last two, and added it in the constructor, the code ran just fine.
class Plateau:
def __init__(self, j1, j2, UI, Hash):
self.UI = UI
self.UI.ajoutePlateau(self)
self.Hash = Hash
self.Hash.hashPlateau(self)
# some code to deal with the rest
...
#
self.UI.initAffichage()
I then have a UI interface and a ConsolUI (UITerminal in my code) which inherit from it. It is as expected what show to the user the board and also what ask the human player (if there is one) what he wants to play.
class UI:
def __init__(self):
self.Plateau = None
def ajoutePlateau(self, Plateau):
self.Plateau = Plateau
# a bunch of method that throw an exception and are overrided by the child class
...
#
class UITerminal(UI):
def __init__(self):
#super(UITerminal, self).__init__(self)
#super().__init__(self)
#UI.__init__(self)
#super(UITerminal, self).__init__()
super().__init__()
#UI.__init__()
# the method that had to be overridden
...
#
I have also tried few version of the UITerminal constructor (which are above and put on comment).
or even just nothing since it should not even be needed (I think...).
Then there is the hash, which is build the same way as the UI : an interface, a child.
class Hash:
def __init__(self):
pass
# a bunch of method that throw an exception and are overridden by the child class
...
#
class ZombristHash(Hash):
def __init__(self):
#super(ZombristHash, self).__init__(self)
#super().__init__(self)
#Hash.__init__(self)
#super(ZombristHash, self).__init__()
super().__init__()
#Hash.__init__()
# a bunch of code to init the zombristhash
...
#
same as with the UI, I tried multiple way to call the interface constructor.
Then I have my main, which is only 1 line and is what throw the error :
p = Plateau(Humain("j1"), Humain("j2"), UITerminal(), ZombristHash())
and the error is :
Traceback (most recent call last): File "plateau.py", line 298, in <module> p = Plateau(Humain("j1"), Humain("j2"), UITerminal(), ZombristHash())
TypeError: __init__() missing 1 required positional argument: 'UI'.
From what I understand, he tells me I haven't given the board constructor a UI as argument, but I did so I do not understand what is happening.
I tried on quamran's suggestion this :
p = Plateau(None, None, None, None)
And it seems to think it was ok now...
Traceback (most recent call last):
File "plateau.py", line 298, in <module>
p = Plateau(None, None, None, None)
File "plateau.py", line 13, in __init__
self.UI.ajoutePlateau(self)
AttributeError: 'NoneType' object has no attribute 'ajoutePlateau'
As #Jacques Gaudin noticed, you need to change this :
class ZombristHash(Hash):
def __init__(self):
Hash.__init__()
to this :
class ZombristHash(Hash):
def __init__(self):
super().__init__()
and same for UI.
Finally, you get something like this :
class Plateau:
def __init__(self, j1, j2, UI, Hash):
self.UI = UI
self.UI.ajoutePlateau(self)
self.Hash = Hash
class Hash:
def __init__(self):
pass
class ZombristHash(Hash):
def __init__(self):
super().__init__()
class UI:
def __init__(self):
self.Plateau = None
def ajoutePlateau(self, Plateau):
self.Plateau = Plateau
class UITerminal(UI):
def __init__(self):
# super(UITerminal, self).__init__()
super().__init__()
class Humain():
def __init__(self, j):
pass
p = Plateau(Humain("j1"), Humain("j2"), UITerminal(), ZombristHash())
The following minimal example crashes in pyqt 5.7.1 on windows (copy-paste this in a .py file and run):
from PyQt5.QtWidgets import QListWidgetItem, QListWidget, QApplication
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal
class MyListItem(QListWidgetItem):
def __init__(self, obj):
QListWidgetItem.__init__(self, 'example')
obj.sig_name_changed.connect(self.__on_list_item_name_changed)
def __on_list_item_name_changed(self, new_name: str):
self.setText(new_name)
class MyListItem2(QListWidgetItem, QObject):
def __init__(self, obj):
QListWidgetItem.__init__(self, 'example')
QObject.__init__(self)
obj.sig_name_changed.connect(self.pyqt_slot)
#pyqtSlot(str)
def __on_list_item_name_changed(self, new_name: str):
self.setText(new_name)
class Data(QObject):
sig_name_changed = pyqtSignal(str)
class SearchPanel(QListWidget):
def __init__(self, parent=None):
QListWidget.__init__(self, parent)
obj = Data()
hit_item = MyListItem(obj) # OK
hit_item = MyListItem2(obj) # crashes
self.addItem(hit_item)
obj.sig_name_changed.emit('new_example')
app = QApplication([])
search = SearchPanel()
search.show()
app.exec()
Now just comment out the line that says "crashes", and it works fine. Moreover, the list widget shows 'new_example', showing that the signal went through.
Is there a way to make it work with MyListItem2? i.e. I want to be able to decorate the slot with pyqtSlot, which in turn requires (in PyQt 5.7) that I derive item from QObject.
The intent here is that each item in the list has several characteristics that can change (icon, font, text color) based on signals from associated Data instance (each instance actually "lives", in the Qt sense of the term, in a second thread of our application).
This has got nothing to do with pyqtSlot.
The actual problem is that you are trying to inherit from two Qt classes, and that is not generally supported. The only exceptions to this are Qt classes that implement interfaces, and Qt classes that share a common base-class (e.g. QListWidget and QWidget). However, only the former is offically supported, and there are several provisos regarding the latter (none of which are relevant here).
So a Python class that inherits from both QListWidgetItem and QObject just will not work. The main problem occurs when PyQt tries to access attributes that are not defined by the top-level base-class (even when the attribute does not exist). In earlier PyQt versions, this would simply raise an error:
>>> class MyListItem2(QListWidgetItem, QObject): pass
...
>>> x = MyListItem2()
>>> x.objectName()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: could not convert 'MyListItem2' to 'QObject'
>>> x.foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: could not convert 'MyListItem2' to 'QObject'
which makes it clear that (in C++ terms) a MyListItem2(QListWidgetItem) cannot be cast to a QObject. Unfortunately, it seems that more recent versions of PyQt5 no longer raise this error, and instead just immediately dump core (which presumably is a bug).
If you really need to use pyqtSlot, one suggestion would be to use composition rather than subclassing. So perhaps something like this:
class ListItemProxy(QObject):
def __init__(self, item, obj):
QObject.__init__(self)
self._item = item
obj.sig_name_changed.connect(self.__on_list_item_name_changed)
#pyqtSlot(str)
def __on_list_item_name_changed(self, new_name: str):
self._item.setText(new_name)
class MyListItem2(QListWidgetItem):
def __init__(self, obj):
QListWidgetItem.__init__(self, 'example')
self._proxy = ListItemProxy(self, obj)
This question already has answers here:
Deriving a class from TestCase throws two errors
(2 answers)
Closed 6 years ago.
I have this plain vanilla unit test, which works as expected, as long I leave out the constructor.
import sys
import unittest
class Instance_test(unittest.TestCase):
def __init__(self):
super(Instance_test, self).__init__()
self.attribute = "new"
def test_something(self):
pass
def test_other(self):
self.assertTrue(True)
pass
def setUp(self):
pass
def tearDown(self):
pass
def suite():
return unittest.makeSuite(Instance_test, "test")
def main():
runner = unittest.TextTestRunner(sys.stdout)
runner.run(suite())
if __name__ == "__main__":
main()
With the constructor in place in get this backtrace:
Traceback (most recent call last):
File "f:\gt\check.py", line 31, in main()
File "f:\gt\check.py", line 28, in main
runner.run(suite())
File "f:\gt\check.py", line 24, in suite
return unittest.makeSuite(Instance_test, "test")
File "C:\Python34\lib\unittest\loader.py", line 374, in makeSuite
testCaseClass)
File "C:\Python34\lib\unittest\loader.py", line 70, in loadTestsFromTestCase
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
File "C:\Python34\lib\unittest\suite.py", line 24, in __init__
self.addTests(tests)
File "C:\Python34\lib\unittest\suite.py", line 60, in addTests
for test in tests:
TypeError: __init__() takes 1 positional argument but 2 were given
What's wrong and how else could I have a central attribute to be shared by different test_xxx methods?
I would use unittest.TestCase's setUp() and tearDown() methods instead of init. Just do the same thing you're doing except use the setUp method.
It is happening because __init__ takes more than one arguments, you forgot to provide args and kwargs as arguments. It should have been like this -
class Instance_test(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(Instance_test, self).__init__(*args, **kwargs)
self.attribute = "new"
Besides why are you overriding __init__ anyway (as 2achary suggested) when setUp method is exactly meant for this purpose.
class Instance_test(unittest.TestCase):
def setUp(self):
self.attribute = 'new'
Just add an argument named methodName to your own __init__ method and you should be good to go:
class Instance_test(unittest.TestCase):
def __init__(self, methodName='runTest'):
super(Instance_test, self).__init__(methodName=methodName)
self.attribute = "new"
...
The base TestCase.__init__ signature is as follows:
class TestCase(object):
def __init__(self, methodName='runTest'):
...
As you can see, it takes an additional methodName argument, and there is no constructor without it, hence the failure you encountered.
I'm wondering about this issue I'm getting with Python, I'm sure this is well known. I've been dabbling in Python now for a while and am getting used to it's flavor but I'm running into this issue outlined below. If you run this code below:
import pprint
pp = pprint.PrettyPrinter(indent=4).pprint
class Foo(object):
def __init__(self):
self.foo_var = 1
class Bar(Foo):
def __init__(self):
self.bar_var = 2
super(Foo, self).__init__()
def deep_access(self):
pp(self.bar_var)
class FooBar(Bar):
def __init__(self):
self.foobar_var = 3
super(Bar, self).__init__()
def access(self):
# call Bar
self.deep_access()
fb = FooBar()
fb.access()
You will receive the following error:
Traceback (most recent call last):
File "inheritance.py", line 29, in <module>
fb.access()
File "inheritance.py", line 26, in access
self.deep_access()
File "inheritance.py", line 16, in deep_access
pp(self.bar_var)
AttributeError: 'FooBar' object has no attribute 'bar_var'
From the error I gather it's looking for bar_var in FooBar rather than Bar, but if I call the parent class, why not use the variable declared in the parent?? How do I get a parent class to access it's own variables? It just seems weird to me coming from a different approach to OOP.
Instead of self.deep_access it tried Bar.deep_access(self) and super(FooBar, self).deep_access and super(Bar, self).deep_access and it doesn't work.
You are not calling super() correctly. The first argument should always be the class that's calling super, not any of its base classes..
change
class Bar(Foo):
def __init__(self):
self.bar_var = 2
super(Foo, self).__init__()
# ^^^
to
class Bar(Foo):
def __init__(self):
self.bar_var = 2
super(Bar, self).__init__()
# ^^^
and so on...
There's a nice article explaining most of the ins and outs of super() called "Super considered Super!"
I am trying to subclass ConfigParser. When trying to access _sections it says
'acc' object has no attribute '_sections'
Example Code(python 2.7):
import ConfigParser
class acc(object, ConfigParser.RawConfigParser):
def __init__(self, acc_file):
super(acc,self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)
The problem has been described much better here.
This is Python's Method Resolution Order.
So, what actually happens is that the MRO "chaining" does not behave well when encoutering an old style class. Or to be specific - classes that do not call super
In your code, configParser.RawConfigParser__init__(..) is never called. To fix this you could call it manually (simply add ConfigParser.RawConfigParser.__init__(self ...) in acc's init), although I'm not sure this is recommended or even valid.
Your other option is to make all classes conform to the new style, and invoke super, or old style, and initialize explicitly.
The only thing that seems to be working is if all classic-style classes are after all new-style classes in the output of Class.mro(), and specifically after object. This will prevent super from calling them.
The other answer isn't very safe because of this scenario:
class TheClassicClass:
def __init__(self):
print "instantiating clasic class!"
self._msg = "the classic class!"
def m(self):
print self._msg
class acc(ConfigParser.RawConfigParser, TheClassicClass, object):
def __init__(self, acc_file):
super(acc,self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)
a.m()
Fix to other answer: adding these lines to acc's __init__ should inform to explicitly instantiate the classes:
ConfigParser.RawConfigParser.__init__(self)
TheClassicClass.__init__(self)
To confirm the problem in your code, let's try to reproduce this problem in a simple setup...
We'll make old (classic) classes:
class AClassic:
def __init__(self):
print "init aclassic"
class BClassic:
def __init__(self):
print "init bclassic"
self.name = "bclassic"
def m(self):
print "print from " + self.name
And new style classes that invoke super:
class ANew(object):
def __init__(self):
print "init anew"
super(ANew, self).__init__()
class BNew(object):
def __init__(self):
print "init bnew"
super(BNew, self).__init__()
def m(self):
print "print from bnew"
In the simplest case, we're manually calling __init__ to make sure classes are instantiated. This would look something like:
class TestOldStyle(AClassic, BClassic):
def __init__(self):
AClassic.__init__(self)
BClassic.__init__(self)
self.m()
print "old style"
TestOldStyle()
In this case, depth-first, left-to-right, is the order of the MRO.
Output:
old style
init aclassic
init bclassic
print from bclassic
Now let's try new style, which looks like this with super:
# will init ANew and BNew
class TestNewSuper(ANew, BNew, object):
def __init__(self):
super(TestNewSuper, self).__init__()
TestNewSuper()
Since both classes call super, they are both instantiated and the output is:
init anew
init bnew
Now, we try using some "hybrids" of a classic-style classes (ConfigParser.RawConfigParser is a classic-style-class, and cannot call super:
# will init ANew , but not BClassic
class TestOldStyleSuper(ANew, BClassic):
def __init__(self):
super(TestOldStyleSuper, self).__init__()
self.m()
Output:
init anew
And immediately an exception:
Traceback (most recent call last):
File "./inhert.py", line 35, in <module>
TestOldStyleSuper()
File "./inhert.py", line 33, in __init__
self.m()
File "./inhert.py", line 15, in m
print "print from " + self.name
AttributeError: 'TestOldStyleSuper' object has no attribute 'name'
This happens since BClassic is not instantiated.
More examples of unexpected behaviour:
# init ANew, BClassic
class TestHybrid(ANew, BClassic, BNew):
def __init__(self):
super(TestHybrid, self).__init__()
self.m()
TestHybrid()
will initialize ANew and BClassic, but not BNew:
init anew
init bclassic
print from bclassic
And creating an incosistent MRO:
# no consistent MRO exception
class TestHybrid(ANew, object, BNew):
def __init__(self):
super(TestHybrid, self).__init__()
self.m()
TestHybrid()
Exception:
Traceback (most recent call last):
File "./inhert.py", line 33, in <module>
class TestHybrid(ANew, object, BNew):
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases BNew, object
Reverse the order of subclassing as follows:
import ConfigParser
class acc(ConfigParser.RawConfigParser, object):
def __init__(self, acc_file):
super(acc, self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)