Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. QDataWidgetMapper how to clear mapped widgets when model is empty
Forum Updated to NodeBB v4.3 + New Features

QDataWidgetMapper how to clear mapped widgets when model is empty

Scheduled Pinned Locked Moved Unsolved Qt for Python
20 Posts 3 Posters 1.6k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • JonBJ JonB

    @StarterKit
    If the model emits a signal like dataChanged() for whatever your "NULL" case is, then if you say QDataWidgetMapper does not act on the signal maybe you can subclass and handle it? If it does not emit a signal then it does not sound like a QDataWidgetMapper issue. I suggest you test this.

    S Offline
    S Offline
    StarterKit
    wrote on last edited by StarterKit
    #11

    @JonB, but data haven't been changed. Why would a model emit anything?
    I mean for the case of topic starter it might be a solution. But my case is slightly different. I have a table of data and user may move a cursor by one way or another (I mean data cursor, that tracks active row/record in the database table). As result different data should be shown in views and widgets. I.e. no change of data, only change of view should happen. So model has no reason to emit anything - it is precisely why QDataWidgetMapper was created - to make widgets aware of cursor's position change and supply a new piece of data to them.

    1 Reply Last reply
    0
    • S StarterKit

      @JonB , I would be happy if you will show a small example of how to handle my case with help of delegate.
      Because either I miss something crucial here or it isn't possible. My opinion is backed with the fact that I have a custom widget with integer property that handles value from a database. And everything works fine until it comes to a row with NULL value - the problem is that widget is never notified by QDataWidgetMapper in this case and it simply have no idea that it should display something new.
      So I belive it isn't possible to handle this case via delegate. I would be happy to be wrong and learn something new from you.

      I remember out discussion happened by link you posted. But there we talked about opposite direction - when widget has NULL value and should put this value into database table. This indeed may be overridden (and I implemented it) because I know the moment when data are being saved to the database.
      But here I'm talking about displaying the data from database (and actually more complex actions that might be hanled by custom widget) - and widget isn't notified about data change at all. And this is much bigger problem - you have no idea when row was changed in database. Yes, you may make some solution that will track user activity in some place and notify widgets - but it makes QDataWidgetMapper a useless thing because it is big part of its destiny.

      JonBJ Online
      JonBJ Online
      JonB
      wrote on last edited by
      #12

      @StarterKit said in QDataWidgetMapper how to clear mapped widgets when model is empty:

      until it comes to a row with NULL value - the problem is that widget is never notified by QDataWidgetMapper in this case and it simply have no idea that it should display something new.

      Then why don't you implement this?

      S 1 Reply Last reply
      0
      • JonBJ JonB

        @StarterKit said in QDataWidgetMapper how to clear mapped widgets when model is empty:

        until it comes to a row with NULL value - the problem is that widget is never notified by QDataWidgetMapper in this case and it simply have no idea that it should display something new.

        Then why don't you implement this?

        S Offline
        S Offline
        StarterKit
        wrote on last edited by
        #13

        @JonB, I feel I lost the point.
        Lets me explain it again and then you may feel the gap if I have it somewhere.
        For simplicity, let's have only 1 table with data.
        This table is displayed in a couple of different table views.
        There is a panel that has several widgets linked to currently selected row in a table and displays something to the user based on data present in current row.
        I.e. user or application may "randomly" select a row in the table. And after this selection something is displayed to the user.

        I have widgets from the panel linked to the table via QDataWidgetMapper - so if row is changed then widgets are updated by means of QDataWidgetMapper and perform their logic (And this happens well until we hit a NULL integer value).
        I might be wrong but AFAIK QSqlTableModel or QDataWidgetMapper doesn't have a signal that would be fired after row change.
        So currently I have quite a simple link - widgets-mapper-table that works on its own. What you propose me is to monitor status of views linked to the table and then update widgets? is it right? Then it will create a lot of interconnections between unrelated modules that I really would like to avoid....

        B 2 Replies Last reply
        0
        • S StarterKit

          @JonB, I feel I lost the point.
          Lets me explain it again and then you may feel the gap if I have it somewhere.
          For simplicity, let's have only 1 table with data.
          This table is displayed in a couple of different table views.
          There is a panel that has several widgets linked to currently selected row in a table and displays something to the user based on data present in current row.
          I.e. user or application may "randomly" select a row in the table. And after this selection something is displayed to the user.

          I have widgets from the panel linked to the table via QDataWidgetMapper - so if row is changed then widgets are updated by means of QDataWidgetMapper and perform their logic (And this happens well until we hit a NULL integer value).
          I might be wrong but AFAIK QSqlTableModel or QDataWidgetMapper doesn't have a signal that would be fired after row change.
          So currently I have quite a simple link - widgets-mapper-table that works on its own. What you propose me is to monitor status of views linked to the table and then update widgets? is it right? Then it will create a lot of interconnections between unrelated modules that I really would like to avoid....

          B Offline
          B Offline
          BamboozledBaboon
          wrote on last edited by BamboozledBaboon
          #14

          @StarterKit QDataWidgetMapper emits the currentIndexChanged signal. I don't believe a model would have a reason to track "current index". I think what Jon is suggesting is to subclass QDataWidgetMapper to get it to properly update when it comes across nulls?

          JonBJ 1 Reply Last reply
          1
          • S StarterKit

            @JonB, I feel I lost the point.
            Lets me explain it again and then you may feel the gap if I have it somewhere.
            For simplicity, let's have only 1 table with data.
            This table is displayed in a couple of different table views.
            There is a panel that has several widgets linked to currently selected row in a table and displays something to the user based on data present in current row.
            I.e. user or application may "randomly" select a row in the table. And after this selection something is displayed to the user.

            I have widgets from the panel linked to the table via QDataWidgetMapper - so if row is changed then widgets are updated by means of QDataWidgetMapper and perform their logic (And this happens well until we hit a NULL integer value).
            I might be wrong but AFAIK QSqlTableModel or QDataWidgetMapper doesn't have a signal that would be fired after row change.
            So currently I have quite a simple link - widgets-mapper-table that works on its own. What you propose me is to monitor status of views linked to the table and then update widgets? is it right? Then it will create a lot of interconnections between unrelated modules that I really would like to avoid....

            B Offline
            B Offline
            BamboozledBaboon
            wrote on last edited by BamboozledBaboon
            #15

            @StarterKit You've peaked my curiosity... because I realized your specific spinbox problem was going to be my problem as well very soon. I've come up with a work around that seems to work at the moment. I subclassed QSpinBox and created a custome property, value2, that accepts a string instead of an integer. If the mapper attempts to call the setter function with an empty string then the function will intercept this and convert it to zero.

            from PySide6.QtCore import QByteArray, Property
            from PySide6.QtWidgets import QSpinBox
            
            
            class MySpin(QSpinBox):
                def readValue2(self):
                    return self.value()
            
                def setValue2(self, val):
                    if not val:
                        self.setValue(0)
                    else:
                        self.setValue(int(val))
            
                value2 = Property(str, readValue2, setValue2)
            

            Use the overloaded addMapping function to map to this custom property like this:

            mapper.addMapping(my_custom_spin, 3, QByteArray("value2"))
            

            The root of the issue seems to be is that the datamapper by default maps to the 'user' property, which is 'value' for a spinbox. The value property is strictly an integer type. Because mapper sees an integer property and it doesn't know how to convert null to integer it doesn't bother and skips updating the value property. But we've observed mapper will convert nulls to an empty string for text boxes... So trick mappper to sending a string by mapping to a string type property instead.

            Edit: realizing Jon suggested the approach should be to create a custom delegate Id really like to see how a pro would handle this! I still haven't fully wrapped my head around how/when to use delegates.

            1 Reply Last reply
            1
            • JonBJ JonB

              @StarterKit
              I looked at your bug report. It is rather different from the issue we were discussing, viz. "unbind" a QDataWidgetMapper in the sense of return it to its original state.

              You are now asking for all Qt data-mappable widgets to support the display/entry of backend database NULL values. Possibly regrettably that has never been the case, and I would guess is unlikely to be introduced as a result of your bug report. You are expected to write this yourself via an item delegate and suitable code in setEditorData() & setModelData().

              See also https://forum.qt.io/topic/131088/pyside6-qdatawidgetmapper-how-to-store-null-from-mapped-widget-into-db and https://www.qtcentre.org/threads/35832-Custom-QLineEdit-to-store-NULL-with-QDataWidgetMapper.

              B Offline
              B Offline
              BamboozledBaboon
              wrote on last edited by
              #16

              @JonB said in QDataWidgetMapper how to clear mapped widgets when model is empty:

              ...You are expected to write this yourself via an item delegate and suitable code in setEditorData() & setModelData().

              Could it be this simple?:

              class MyDelegate(QStyledItemDelegate):
                  def setEditorData(self, editor, index):
                      if isinstance(editor, QSpinBox) and not index.data():
                          editor.setValue(0)
                          return
                      super().setEditorData(editor, index)
              

              I suppose I should read through the links you posted.

              1 Reply Last reply
              1
              • B BamboozledBaboon

                @StarterKit QDataWidgetMapper emits the currentIndexChanged signal. I don't believe a model would have a reason to track "current index". I think what Jon is suggesting is to subclass QDataWidgetMapper to get it to properly update when it comes across nulls?

                JonBJ Online
                JonBJ Online
                JonB
                wrote on last edited by JonB
                #17

                @BamboozledBaboon said in QDataWidgetMapper how to clear mapped widgets when model is empty:

                I think what Jon is suggesting is to subclass QDataWidgetMapper to get it to properly update when it comes across nulls?

                If the mapper attempts to call the setter function with an empty string then the function will intercept this and convert it to zero.

                Because mapper sees an integer property and it doesn't know how to convert null to integer it doesn't bother and skips updating the value property.

                Yes to all of these!

                Could it be this simple?:

                Does it work? If so, yes!

                The only thing I would say: You are actually setting the editor to 0 when data is "NULL". This means the user cannot see the difference between a genuine 0 versus a "NULL" shown as a 0. And particularly when the user is in edit mode and submitting the record code (e.g. via setModelData()) won't know when it sees a 0 whether to submit that or "NULL" to the database.

                In the case of a QSpinBox I believe you could empty out the text of the number to show as "blank", and accept "blank" input as meaning submit "NULL" to the database. It would mean that you must not use any standard integer validator on it.

                I think you may have to use findChild<QLineEdit *>(spinBox) to access the text. Another possibility might be to employ an unused value, e.g. -1 if appropriate, and combine with specialVlaueText(). There is also valueFromText() and textFromValue().

                Sometimes this does not work for the widget type. I recall having a QDateEdit for dates. But these could be left "unfilled" on the form, stored as "NULL" in the database. QDateEdit does not allow you to "blank it out" in any way. Then you need the delegate to add something to allow for "NULL". For example, a checkbox for "NULL"/"empty"/"no date". And maybe disable/enable the date edit according as that box is checked/unchecked.

                S 1 Reply Last reply
                0
                • JonBJ JonB

                  @BamboozledBaboon said in QDataWidgetMapper how to clear mapped widgets when model is empty:

                  I think what Jon is suggesting is to subclass QDataWidgetMapper to get it to properly update when it comes across nulls?

                  If the mapper attempts to call the setter function with an empty string then the function will intercept this and convert it to zero.

                  Because mapper sees an integer property and it doesn't know how to convert null to integer it doesn't bother and skips updating the value property.

                  Yes to all of these!

                  Could it be this simple?:

                  Does it work? If so, yes!

                  The only thing I would say: You are actually setting the editor to 0 when data is "NULL". This means the user cannot see the difference between a genuine 0 versus a "NULL" shown as a 0. And particularly when the user is in edit mode and submitting the record code (e.g. via setModelData()) won't know when it sees a 0 whether to submit that or "NULL" to the database.

                  In the case of a QSpinBox I believe you could empty out the text of the number to show as "blank", and accept "blank" input as meaning submit "NULL" to the database. It would mean that you must not use any standard integer validator on it.

                  I think you may have to use findChild<QLineEdit *>(spinBox) to access the text. Another possibility might be to employ an unused value, e.g. -1 if appropriate, and combine with specialVlaueText(). There is also valueFromText() and textFromValue().

                  Sometimes this does not work for the widget type. I recall having a QDateEdit for dates. But these could be left "unfilled" on the form, stored as "NULL" in the database. QDateEdit does not allow you to "blank it out" in any way. Then you need the delegate to add something to allow for "NULL". For example, a checkbox for "NULL"/"empty"/"no date". And maybe disable/enable the date edit according as that box is checked/unchecked.

                  S Offline
                  S Offline
                  StarterKit
                  wrote on last edited by StarterKit
                  #18

                  Hi guys,
                  @JonB, @BamboozledBaboon thank you for discussion. Things your mentioned are clear for me and I did it previously one way or another so these things should work well.

                  Just to add some comments from my side.

                  @JonB, you are right - we should distinguish "Zero" and "NULL" values at a widget level. But it isn't a big issue - if your design requires DB to have both, "Zero" and "NULL" values, then your widget should be capable to distinguish and handle it. It is up to you how to implement it - for example, I did a separate button (that is part of custom widget) that sets the widget to NULL value implicitly.

                  @BamboozledBaboon, you proposal with a string property is clear and it should work for sure (just I would prefer to use fieldIndex() method to get an index of a field for addMapping() as names are a bit more consistent then order of fields).

                  But... I dislike an idea of having second string property very much. While it works it makes code really dirty with unnecessary duplication.
                  My strong opinion - it is a bug, because it is QDataWidgetMapper who does the job and it should do it for any value that may be present in database. And as NULL is a valid value - it should handle it one way or another, not ignore it.

                  Anyway, thanks both of you for this discussion and ideas how to overcome it because looking at number of unresolved bugs in Qt Widgets I feel it might take a looong time to have it corrected.

                  JonBJ 1 Reply Last reply
                  0
                  • S StarterKit

                    Hi guys,
                    @JonB, @BamboozledBaboon thank you for discussion. Things your mentioned are clear for me and I did it previously one way or another so these things should work well.

                    Just to add some comments from my side.

                    @JonB, you are right - we should distinguish "Zero" and "NULL" values at a widget level. But it isn't a big issue - if your design requires DB to have both, "Zero" and "NULL" values, then your widget should be capable to distinguish and handle it. It is up to you how to implement it - for example, I did a separate button (that is part of custom widget) that sets the widget to NULL value implicitly.

                    @BamboozledBaboon, you proposal with a string property is clear and it should work for sure (just I would prefer to use fieldIndex() method to get an index of a field for addMapping() as names are a bit more consistent then order of fields).

                    But... I dislike an idea of having second string property very much. While it works it makes code really dirty with unnecessary duplication.
                    My strong opinion - it is a bug, because it is QDataWidgetMapper who does the job and it should do it for any value that may be present in database. And as NULL is a valid value - it should handle it one way or another, not ignore it.

                    Anyway, thanks both of you for this discussion and ideas how to overcome it because looking at number of unresolved bugs in Qt Widgets I feel it might take a looong time to have it corrected.

                    JonBJ Online
                    JonBJ Online
                    JonB
                    wrote on last edited by
                    #19

                    @StarterKit said in QDataWidgetMapper how to clear mapped widgets when model is empty:

                    if your design requires DB to have both, "Zero" and "NULL" values, then your widget should be capable to distinguish and handle it.

                    Just to wrap up. This is the nub of the problem. There seem to be just 3 possibilities:

                    1. The widget itself should allow for a "NULL" value, somehow.
                    2. QDataWidgetMapper supplied should "modify" all widgets it uses to add a facility for showing/entering "NULL", somehow.
                    3. You should modify QDataWidgetMapper (e.g. in an item delegate) to deal with "NULL" yourself.

                    Now, QDataWidgetMapper simply is not supplied with #2. In the Qt approach of "KISS" QDWM just deals with values which can be mapped to the widget, as supplied. Since the widget does not handle "NULL" the straightforward implementation of QDWM does not either. I do not see that as a failing of QDWM.

                    Of course the "best" would be if all the base widgets allowed for "NULL"/empty. But they don't. Like I said, you may be able to do it for a QSpinBox by leaving it "empty", but take the case of QDateEdit and it's impossible. And if it did allow for this you could not simply have a widget value type of int or QDate, you would either have to change that to e.g. a QVariant or have some isEmpty() method. So it would be a not inconsiderable change to all widgets.

                    S 1 Reply Last reply
                    1
                    • JonBJ JonB

                      @StarterKit said in QDataWidgetMapper how to clear mapped widgets when model is empty:

                      if your design requires DB to have both, "Zero" and "NULL" values, then your widget should be capable to distinguish and handle it.

                      Just to wrap up. This is the nub of the problem. There seem to be just 3 possibilities:

                      1. The widget itself should allow for a "NULL" value, somehow.
                      2. QDataWidgetMapper supplied should "modify" all widgets it uses to add a facility for showing/entering "NULL", somehow.
                      3. You should modify QDataWidgetMapper (e.g. in an item delegate) to deal with "NULL" yourself.

                      Now, QDataWidgetMapper simply is not supplied with #2. In the Qt approach of "KISS" QDWM just deals with values which can be mapped to the widget, as supplied. Since the widget does not handle "NULL" the straightforward implementation of QDWM does not either. I do not see that as a failing of QDWM.

                      Of course the "best" would be if all the base widgets allowed for "NULL"/empty. But they don't. Like I said, you may be able to do it for a QSpinBox by leaving it "empty", but take the case of QDateEdit and it's impossible. And if it did allow for this you could not simply have a widget value type of int or QDate, you would either have to change that to e.g. a QVariant or have some isEmpty() method. So it would be a not inconsiderable change to all widgets.

                      S Offline
                      S Offline
                      StarterKit
                      wrote on last edited by StarterKit
                      #20

                      @JonB I respect your summary and I think it is a correct one.
                      But let me disagree with you about QDataWidgetMapper behavior. I have nothing against "KISS" but the behavior should be also consistent and clear.
                      As I see it is:
                      A) inconsistent - as for some datatypes we have a mapping for NULL and for others have not. There might be different views but down to underlying bits - empty string and NULL are not the same values, so here we definitely have an adaptation and translation. But we don't have it for integer.
                      B) unclear - the documentation says nothing about someting won't be updated. There are only this text "Every time the current index changes, each widget is updated with data from the model via the property specified when its mapping was made." So it is always a surprise that calls for some kind of ad-hoc workarounds (instead of a proper design).

                      Summarizing - I think the best what should be done:

                      1. all datatypes should be treated equally (either with some translation or ingnoring NULL for all of them).
                      2. documentation should have a clear statement about how it actually works.

                      I think I should update my bug reports with this kind of proposal.

                      1 Reply Last reply
                      1

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved