Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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:
    da045f4a-2e85-4c4d-90e3-f7edf9fa58d3-image.png
    This happens because my two custom QLineEdit inherits from QLineEdit. 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 promoted QLineEdit 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


  • Banned

    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 having super( ) 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.



  • @Denni-0

    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:
    724bae5e-ea0f-470d-ba9e-e09b5a78492a-image.png
    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:

    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)
    
    

  • Banned

    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


  • Banned

    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.


  • Banned

    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


  • Banned

    To each their own -- some folks like to work easy and efficient and some folks like to do things the hard way ;-)


Log in to reply