How to override PySide2 widget's method generated by Qt Designer?
-
I'm trying to learn PySide2 by creating a mimic executable like powerrename tool in powertoys.
I created all the visible UI part in Qt Designer.
Then generated a class namedUi_PowerRenameDialog
and import the class into the code showed below.
I wanted to accomplish a functionality which can let users drag and drop files from Windows Explorer to theQTreeWidget
instance in the window.I encountered a error when trying to override the
dragEnterEvent
anddropEvent
methods of theQTreeWidget
instance.
The Error message shows here:TypeError: dragEnterEvent() missing 1 required positional argument: 'evt'.
Here is my full code:
import sys from PySide6 import QtWidgets from PySide6 import QtCore from ui_powerRename import Ui_PowerRenameDialog class MyPowerRenameDialog(QtWidgets.QDialog): def __init__(self,parent=None): super().__init__(parent) self.ui = Ui_PowerRenameDialog() self.ui.setupUi(self) self.modify_ui() self.create_connections() def modify_ui(self): self.ui.previewTreeWidget.setAcceptDrops(True) ####################### The Overloading Part ###################################### def dragEnterEvent(self,evt): if evt.mimeData().hasUrls(): evt.accept() else: evt.ignore() def dropEvent(self,evt): for url in evt.mimeData().urls(): newItem = QtWidgets.QTreeWidgetItem(url) self.addTopLevelItem(newItem) self.ui.previewTreeWidget.dragEnterEvent = dragEnterEvent self.ui.previewTreeWidget.dropEvent = dropEvent ######################################################################################## items = QtWidgets.QTreeWidgetItemIterator(self.ui.previewTreeWidget) for item in items: item.value().setFlags(item.value().flags()|QtCore.Qt.ItemIsUserCheckable) item.value().setCheckState(0,QtCore.Qt.Checked) def create_connections(self): self.ui.cancelButton.clicked.connect(self.close) if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) window = MyPowerRenameDialog() window.show() sys.exit(app.exec_())
- I wonder what is the proper way to achieve that functionality?
- And is there a way to override the method of widget instance which is generated by Qt Designer?
-
@johnjml
Hi and welcome.Did you come across this code, or something like it, in a Python/PySide/PyQt example or thread? In which case please post the link so I can read.
Or, is this just your own code for how you think to go about overloading Qt methods? Your code is very "Pythony", one couldn't do anything like it in C++, so need to understand where you are coming from.
-
Hi, @JonB
Thanks for your reply.First,I must say sorry. It's all about 'overriding'. 'overload' is my typo,edited that.Sorry if you feel confused.
It's my own code. As I mentioned,I'm trying to learn PySide,and my knowledge of C++ is very poor,which is limited to basic syntax(sorry for my stupidity).
I came across the basic idea from this PySide documentation example.
I have watched some tutorials about PySide, such as this Zurbrigg PySide tutorial.Sorry that I cannot find systematic instructions of learning PySide and I really think the documentation of PySide is a little bit messy for beginners like me.Sometimes I have to navigate to C++ version of the documents to fetch some information of a class or a method (such as return types).
I know that I can derive a custom QTreeWidget class from the basic QTreeWidget and override the methods there.
But I do what to know is there a way to override methods of widget generated from Qt Designer?
And beginner-friendly resources of learning Pyside will be much appreciated.
Thank you.
-
@johnjml
The example you used has nothing to say about either overloading or overriding. The way you have chosen to try to achieve thatself.ui.previewTreeWidget.dragEnterEvent = dragEnterEvent self.ui.previewTreeWidget.dropEvent = dropEvent
is a totally Pythonic feature of replacing am existing function pointer, or whatever this is referred to in Python ("monkey-patching?"). It would not be be available in C++, and it's not how we go about overriding an existing virtual method. I do not know whether your way can work in Python/PySide, perhaps not because of the error message you receive.
The way I have done it in PySide is the same as I would in C++, and corresponds to Python examples I have seen. Basically we must subclass the widget, and the Python would look something like this:
class PreviewTreeWidget(QTreeWidget): def __init__(self, parent=None): super().__init__(parent) def dragEnterEvent(self, evt): # your code def dropEvent(self, evt): # your code
An example for PyQt5 is shown in e.g. https://zetcode.com/gui/pyqt5/dragdrop/. Should be same for PySide2.
When you need to do this against a widget in Qt Designer, you need to use its Promote feature to effectively declare your
PreviewTreeWidget
class as derived from and usable in place of a standardQTextWidget
. I'm not sure how that works or how easy/difficult it is from PySide, Google for e.g.pyside2 designer promote widget
(orpyqt5
) and have a read.I am mentioning @SGaist here, in the hope he will see this and comment as to whether this is right or wrong for PySide. He is a general expert, but I know he has used PySide too. I will also mention @mrjj, who is a Designer UI expert for things like Promote, but I'm not sure if he knows about the PySide requirements for this.
Finally, when you get past this issue, unless you know better than I this code
item.value().setFlags(item.value().flags()|QtCore.Qt.ItemIsUserCheckable) item.value().setCheckState(0,QtCore.Qt.Checked)
does not look right. I don't see you can call these methods on
value()
. I would have thought you would remove.value()
from both of these? -
Hi,
Monkey patching can work, no problem with that but at some point it becomes really hard to reason about it.
Since you are doing several changes, you should follow @JonB´a suggestion and use the promote feature of Designer.
If you have simple widgets, you might also want to learn to build your UI directly with code.
-
@JonB
Oh,I see.
Seems the correct way of overriding is to declare and define my custom QTreeWidget and derive from the basic widget.
The 'monkey-patching' thing is a python way to change function or method in runtime.I was thinking it might have the same effect like overriding,that why I used the term 'overriding' in the question.I can see I'm totally wrong about that.As for the
.value()
issue you mentioned, everyitem
in that snippet is actually a Iterater object in python,so I have to use the.value()
to get the actualQTreeWidgetItem
to access the methods inQTreeWidgetItem
instance.
If I remove.value()
,I will get anAttributeError: 'PySide6.QtWidgets.QTreeWidgetItemIterator' object has no attribute 'setFlags'
.The official doc is referenced here.
I'm also quite confued by this,hope the experts you mentioned can explain it.
Maybe I should change my code to this so it can be undertood better:itemIter = QtWidgets.QTreeWidgetItemIterator(self.ui.previewTreeWidget) for iter in itemIter: iter.value().setFlags(iter.value().flags()|QtCore.Qt.ItemIsUserCheckable) iter.value().setCheckState(0,QtCore.Qt.Checked)
-
@johnjml
For the overriding, see @SGaist's confirmation of my understand above.The iterator is a small problem, which you will get right. I respect what you say. However you referred me to the PySide2 reference page and that starts with example:
it = QTreeWidgetItemIterator(treeWidget) while it: if (it).text(0) == itemText: (it).setSelected(True) it = it + 1
Here you can see no use of
.value()
. However, looking at it I am concerned it might be some straight copy of the required C++ code, and nonsense for Python? I believe some/most/all of the PySide2 examples code may have been left as simple, automated translation from the C++ examples...? -
@JonB
Right,that example is the confusing point.I noticed the problem too.
See that is the 'messy' part in PySide doc I was talking about.
Some examples and descriptions are not so python user friendly.
Return types are not strictly listed.
Still,very appreciate your help. -
@johnjml
Sadly (depending on your POV!) Python is still very much a second-class citizen in the Qt world.PyQt5 never had its own documentation, you just had to go look up the normal C++ documentation and figure for yourself.
PySide2 does have those documentation pages you reference. However, they seem to have been generated automatically by some translation tool from the literal C++ examples in their doc pages. Newer PySide6 docs do not seem to me to have changed in this respect. As we are both saying, I believe that while some of them work some will not. As witness the current case. I found the PySide2 docs useless, and always use the C++ ones, regardless of which language I am programming in. At least be aware that even if C++ is not your favored language it can be worth looking at the standard C++ docs as well as the PySide stuff.
-
@SGaist
I see,monkey patching is really confusing me.Maybe it will work in some way.
I will dig in the promote feature right now.
Thanks for your help.Could you please provide some PySide beginner-friendly learning resources,master SGaist?
It would help alot.
Again,huge thanks. -
@johnjml
Then don't use your monkey-patching code approach! If you do the Promote route there will be no monkey patching, and it will work by overriding the method in a subclass, like one would have to do in C++. The only issue then is how you get Designer Promote to work right for PySide2, there must be Googlable answers out there..... -
@JonB
Yeah,I abandoned the monkey patching thing. It's not a good way to edit widgets.
I'll follow your promote solution.I'll try google it.I didn't even know there is a promote feature before.
I'm not a native english speaker,hope my codes and typos didn't make you angry.Really appreciate the help of yours.Thanks alot.