Solved Mixins - Inherits from two QLineEdits using UiLoader
-
I have two custom
QLineEdit
:- ValidatedQLineEdit
- DelayedQLineEdit
If I want to have a
QLineEdit
with the functionality of both, how do I do it?I am doing this:
class DelayedValidatedLineEdit(DelayedLineEdit, ValidatedLineEdit): pass
But when I run it Qt creates an empty
QLineEdit
for me and then:
This happens because my two customQLineEdit
inherits fromQLineEdit
. If one of them did not inherit from anyone, it does work.If I create the
DelayedValidatedLineEdit
manually it works but I'm loading the ui file with the promotedQLineEdit
and then this happens.from PySide2 import QtCore, QtWidgets from utils import Color class DelayedLineEdit(QtWidgets.QLineEdit): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.delay = 250 self.timer = QtCore.QTimer() self.timer.setSingleShot(True) self.textEdited.connect(self._restart_timer) def _restart_timer(self): self.timer.start(self.delay) class ValidatedLineEdit(QtWidgets.QLineEdit): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.color = Color.DEFAULT def color_default(self): if self.color != Color.DEFAULT: self.color = Color.DEFAULT self.setStyleSheet(None) def color_red(self): if self.color != Color.RED: self.color = Color.RED self.setStyleSheet('background-color: rgb(255, 200, 200)') def validate(self, extra_condition=True, validate_empty=True): if (not validate_empty or self.text()) and extra_condition: self.color_default() else: self.color_red() class DelayedValidatedLineEdit(DelayedLineEdit, ValidatedLineEdit): pass
-
And I fixed it, I have copied the code as is from the third link I put in one message above. Now it works.
https://gist.github.com/fredrikaverpil/0a89b174c9d29bc585f11d250adf2a7c
Now I can go in peace but against your recommendations xD
-
Okay my first thought when looking at this is you are making it more complicated than it ought to be but then perhaps this is just an example of what you are actually trying to do so I did a quick google search and found THIS which might help you with your stated issue
However I personally would not go this route at all as you are just itching to have issues crop up on you due to the seemingly unnecessary extra complexity. Along with using
super( )
which I do believe still has bugs in it within python (check the bug reports for this) due to the dynamic difference between the Python language and the C++/Java language from which it was adopted from. Along with just the extra complexity that just havingsuper( )
creates due to the 4 issues you must handle properly in order not to break the code. So let me ask what would be wrong with perhaps K.I.S.S.ing it by doing something more like this:from PySide2.QtCore import * from PySide2.QtWidgets import * from utils import Color class Delayer(QTimer): def __init__(self): QTimer.__init__(self) self.delay = 250 self.setSingleShot(True) def Restart(self): self.timer.start(self.delay) class DelayedLineEdit(QLineEdit): def __init__(self): QLineEdit.__init__(self) self.Delyr = Delayer() self.textEdited.connect(self.Delyr.Restart) class Validator(QObject): def __init__(self, LinEditr): QObject.__init__(self) self.LneEdt = LinEditr self.color = Color.DEFAULT def SetColorDefault(self): if self.color != Color.DEFAULT: self.color = Color.DEFAULT self.LneEdt.setStyleSheet(None) def SetColorRed(self): if self.color != Color.RED: self.color = Color.RED self.LneEdt.setStyleSheet('background-color: rgb(255, 200, 200)') def Valid(self, validate_empty=True, extra_condition=True): if (not validate_empty or self.LneEdt.text()) and extra_condition: self.SetColorDefault() else: self.SetColorRed() class ValidatedLineEdit(QLineEdit): def __init__(self): QLineEdit.__init__(self) self.Vldatr = Validator(self) # Nothing within your code snippet did more than this # which does nothing but I assume your finished piece # makes that connection class DelayedValidatedLineEdit(QLineEdit): def __init__(self): QLineEdit.__init__(self) # Here is the semi-universal Validator self.Vldatr = Validator(self) # Here is the semi-universal Delayer self.Delyr = Delayer() self.textEdited.connect(self.Delyr.Restart)
So with the above your Delayer and your Validator can actually become separate files for easier maintainability as well as modularization so that they can be swapped in and out of other entities to quickly change various functionality within the final classes that use them. Further these could also be made even more generic by allowing them to handle different types of objects rather than just QLineEdits but that is beyond the scope of what this was all about.
-
Thank you very much for your work! I like your code, it shows that you have experience.
That resource you found on google was one that I looked to try to fix this.
I understand that
super()
is a problem because of the differences between Python and C++, I will try not to use it.I have had to make some modifications to your code to make it work with my
UiLoader
class. Unfortunately, in the end, it returns the same error as it had before.The modifications I have made to your code are the following:
And I've also had to add*args
and**kwargs
so UiLoader won't throw me an error.from PySide2.QtCore import * from PySide2.QtWidgets import * from utils import Color class Delayer(QTimer): def __init__(self, *args, **kwargs): QTimer.__init__(self, *args, **kwargs) self.delay = 250 self.setSingleShot(True) def Restart(self): self.timer.start(self.delay) class DelayedLineEdit(QLineEdit): def __init__(self, *args, **kwargs): QLineEdit.__init__(self, *args, **kwargs) self.Delyr = Delayer() self.textEdited.connect(self.Delyr.Restart) class Validator(QObject): def __init__(self, LinEditr, *args, **kwargs): QObject.__init__(self, *args, **kwargs) self.LneEdt = LinEditr self.color = Color.DEFAULT def SetColorDefault(self): if self.color != Color.DEFAULT: self.color = Color.DEFAULT self.LneEdt.setStyleSheet(None) def SetColorRed(self): if self.color != Color.RED: self.color = Color.RED self.LneEdt.setStyleSheet('background-color: rgb(255, 200, 200)') def Valid(self, validate_empty=True, extra_condition=True): if (not validate_empty or self.LneEdt.text()) and extra_condition: self.SetColorDefault() else: self.SetColorRed() class ValidatedLineEdit(QLineEdit): def __init__(self, *args, **kwargs): QLineEdit.__init__(self, *args, **kwargs) self.Vldatr = Validator(self) # Nothing within your code snippet did more than this # which does nothing but I assume your finished piece # makes that connection class DelayedValidatedLineEdit(QLineEdit): def __init__(self, *args, **kwargs): QLineEdit.__init__(self, *args, **kwargs) # Here is the semi-universal Validator self.Vldatr = Validator(self) # Here is the semi-universal Delayer self.Delyr = Delayer() self.textEdited.connect(self.Delyr.Restart)
I got my UiLoader class from these sources:
- https://robonobodojo.wordpress.com/2017/10/03/loading-a-pyside-ui-via-a-class/
- https://robonobodojo.wordpress.com/2018/10/29/pyside2-widget-class-from-a-qt-designer-ui/
- https://gist.github.com/fredrikaverpil/0a89b174c9d29bc585f11d250adf2a7c
My UiLoader class:
from PySide2.QtUiTools import QUiLoader def load_ui(ui_file, base_instance=None, custom_classes=()): loader = UiLoader(base_instance) for custom_class in custom_classes: loader.registerCustomWidget(custom_class) loader.load(ui_file) class UiLoader(QUiLoader): def __init__(self, base_instance): super().__init__() self.base_instance = base_instance def createWidget(self, class_name, parent=None, name=''): if parent is None and self.base_instance: return self.base_instance else: return super().createWidget(class_name, parent, name)
-
Oops dang typos ;-) I have adjusted the original for that little over sight
Oh so your using UI files rather than regular PY files for your GUI feel for you a lot of unnecessary overhead that gains nothing when it comes down to the bottom line.
This is to say whatever your the GUI is that your UI file represents could have been much cleaner and concisely done using straight python-qt code but yeah I know not a lot of quality examples out there for folks to see how that is done but I keep trying to enlighten folk to how it ought to be used versus using those not so friendly excessively complex UI files that most folks seem to unnecessarily use.
If you would like I can show you how to fix that UI file loader issue by not using it ;-) and instead create those UI files as normal PY files
-
Okay proof of concept here is a MUC that shows rendering each of those Line Edits without any errors due to some other code that I am guessing has issues ;) Note I had to replace your Color class since I did not have it but you could just easily add it back in with no effect to the execution. Granted they probably do not do what you want them to do because as I stated that had not been outlined in the code snippet you provided
from PySide2.QtCore import * from PySide2.QtGui import * from PySide2.QtWidgets import * #from utils import Color class Delayer(QTimer): def __init__(self): QTimer.__init__(self) self.delay = 250 self.setSingleShot(True) def Restart(self): self.timer.start(self.delay) class Validator(QObject): def __init__(self, LinEditr): QObject.__init__(self) self.LneEdt = LinEditr self.color = 'black' def SetColorDefault(self): if self.color != 'black': self.color = 'black' self.LneEdt.setStyleSheet(None) def SetColorRed(self): if self.color != 'red': self.color = 'red' self.LneEdt.setStyleSheet('background-color: rgb(255, 200, 200)') def Valid(self, validate_empty=True, extra_condition=True): if (not validate_empty or self.LneEdt.text()) and extra_condition: self.SetColorDefault() else: self.SetColorRed() class DelayedLineEdit(QLineEdit): def __init__(self): QLineEdit.__init__(self) self.Delyr = Delayer() self.textEdited.connect(self.Delyr.Restart) class ValidatedLineEdit(QLineEdit): def __init__(self): QLineEdit.__init__(self) self.Vldatr = Validator(self) # Nothing within your code snippet did more than this # which does nothing but I assume your finished piece # makes that connection class DelayedValidatedLineEdit(QLineEdit): def __init__(self): QLineEdit.__init__(self) # Here is the semi-universal Validator self.Vldatr = Validator(self) # Here is the semi-universal Delayer self.Delyr = Delayer() self.textEdited.connect(self.Delyr.Restart) class MainWindow(QWidget): def __init__(self): QWidget.__init__(self) Top=300; Left=700; Width=300; Hight=100 self.setGeometry(Left, Top, Width, Hight) self.setWindowTitle('Test It Out') self.lneDelyd = DelayedLineEdit() self.lneVldtd = ValidatedLineEdit() self.lneDlyVld = DelayedValidatedLineEdit() VBox = QVBoxLayout() VBox.addWidget(self.lneDelyd) VBox.addWidget(self.lneVldtd) VBox.addWidget(self.lneDlyVld) VBox.addStretch(1) self.setLayout(VBox) if __name__ == '__main__': MainThred = QApplication([]) MainGui = MainWindow() MainGui.show() MainThred.exec()
-
Yeah it works but the UiLoader is so useful... I can edit the gui, save and run. Otherwise you would have to generate the .py code, format the code ... (before doing it like this)
I will search on the internet again to see if I find how to fix the UiLoader.
-
Well I am not sure how to convey to you that -- doing it the way I am saying is actually just as easy as using that Designer and what you end up with is a lot less headaches from using something that just is not designed to play nice
note I say this not from my own personal experience but from the numerous students I have taught how to code using python-qt without the Designer who used to use the Designer and have since left it behind and moved on to much more productive coding
-
And I fixed it, I have copied the code as is from the third link I put in one message above. Now it works.
https://gist.github.com/fredrikaverpil/0a89b174c9d29bc585f11d250adf2a7c
Now I can go in peace but against your recommendations xD
-
To each their own -- some folks like to work easy and efficient and some folks like to do things the hard way ;-)