Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QListWidget item editing
QtWS25 Last Chance

QListWidget item editing

Scheduled Pinned Locked Moved Solved General and Desktop
17 Posts 5 Posters 7.7k Views
  • 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.
  • J Offline
    J Offline
    JonB
    wrote on 14 Dec 2018, 10:49 last edited by
    #1

    Qt 5.7. I am using a QListWidget, where its QListWidgetItems have the ItemIsEditable flag. At the moment, at least, the items are the standard strings, with the editor being the built-in QLineEdit, i.e. I am not providing my own editor.

    I need to know when the user starts editing an item and when he has finished editing an item (e.g. while editing, buttons I have attached to the list widget for adding/deleting items should be disabled).

    Call me dumb, but I cannot see signals (or overridable) functions for these?

    • The only signal I can see for starting out is QListWidget::itemDoubleClicked. Is that the only way to start editing, and that's what I'm supposed to use?

    • Worse for finished editing. There is QListWidget::itemChanged, which seems great if the user makes a change and clicks elsewhere or whatever --- I receive that one.

    • But what if the user does not change the item? He might do anything, e.g. click some other widget, tab away, I have no idea. How do I know what the QLineEdit seems to know when it takes the user out of editing mode and no change has been made?

    1 Reply Last reply
    0
    • V VRonin
      14 Dec 2018, 17:01

      It's actually super easy.
      In the delegate:

      • add an argument of type QtCore.QModelIndex to the signals.
      • remove self.closeEditor.connect(self.editFinished)
      • change self.editStarted.emit() to self.editStarted.emit(index)
      • add
      def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
              self.editFinished.emit(index)
              return super().destroyEditor(editor, index)
      

      Now in JEditableListWidget you can connect a slot that accepts a QtCore.QModelIndex. To find the corresponding QListWidgetItem you can just call item(index.row())

      J Offline
      J Offline
      JonB
      wrote on 20 Dec 2018, 14:21 last edited by JonB
      #13

      @VRonin
      Well, I followed your principle of having the QStyledItemDelegate emit signals with the QModelIndex as a parameter.

      I diverged from yours slightly. Given that I'm now overriding destroyEditor() so as to access the index, it seems to me that I might as well be consistent and override createEditor() for beginning editing. So the following two overrides:

      class JEditableListStyledItemDelegate(QtWidgets.QStyledItemDelegate):
          # class variable for "editStarted" signal, with QModelIndex parameter
          editStarted = QtCore.pyqtSignal(QtCore.QModelIndex, name='editStarted')
          # class variable for "editFinished" signal, with QModelIndex parameter
          editFinished = QtCore.pyqtSignal(QtCore.QModelIndex, name='editFinished')
      
          def createEditor(self, parent: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex):
              editor = super().createEditor(parent, option, index)
              if editor is not None:
                  self.editStarted.emit(index)
              return editor
      
          def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
              self.editFinished.emit(index)
              return super().destroyEditor(editor, index)
      

      The above seems to give me fairly reliable signals for what I want, e.g. disable other buttons for doing things to QListWidget (add row, delete row) while the user is editing a row.

      Seem reasonable to you? Thanks for all your input.

      V 1 Reply Last reply 20 Dec 2018, 14:25
      2
      • V Offline
        V Offline
        VRonin
        wrote on 14 Dec 2018, 11:03 last edited by VRonin
        #2

        That's because it's handled by the delegate.
        Subclass QStyledItemDelegate

        class SignalItemDelegate : public QStyledItemDelegate{
        Q_OBJECT
        Q_DISABLE_COPY(SignalItemDelegate)
        public:
        explicit SignalItemDelegate(QObject* parent = Q_NULLPTR):QStyledItemDelegate(parent){
        QObject::connect(this,&SignalItemDelegate::closeEditor,this,&SignalItemDelegate::editFinished);
        }
        void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE{
        editStarted();
        return QStyledItemDelegate::setEditorData(editor,index);
        }
        Q_SIGNALS:
        void editStarted();
        void editFinished();
        };
        

        And then call something like:

        SignalItemDelegate* delegate = new SignalItemDelegate(listWidget);
        QObject::connect(delegate,&SignalItemDelegate::editStarted,[](){qDebug("edit started")});
        QObject::connect(delegate,&SignalItemDelegate::editFinished,[](){qDebug("edit finished")});
        listWidget->setItemDelegate(delegate);
        

        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
        ~Napoleon Bonaparte

        On a crusade to banish setIndexWidget() from the holy land of Qt

        J 2 Replies Last reply 14 Dec 2018, 11:10
        5
        • V VRonin
          14 Dec 2018, 11:03

          That's because it's handled by the delegate.
          Subclass QStyledItemDelegate

          class SignalItemDelegate : public QStyledItemDelegate{
          Q_OBJECT
          Q_DISABLE_COPY(SignalItemDelegate)
          public:
          explicit SignalItemDelegate(QObject* parent = Q_NULLPTR):QStyledItemDelegate(parent){
          QObject::connect(this,&SignalItemDelegate::closeEditor,this,&SignalItemDelegate::editFinished);
          }
          void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE{
          editStarted();
          return QStyledItemDelegate::setEditorData(editor,index);
          }
          Q_SIGNALS:
          void editStarted();
          void editFinished();
          };
          

          And then call something like:

          SignalItemDelegate* delegate = new SignalItemDelegate(listWidget);
          QObject::connect(delegate,&SignalItemDelegate::editStarted,[](){qDebug("edit started")});
          QObject::connect(delegate,&SignalItemDelegate::editFinished,[](){qDebug("edit finished")});
          listWidget->setItemDelegate(delegate);
          
          J Offline
          J Offline
          JonB
          wrote on 14 Dec 2018, 11:10 last edited by
          #3

          @VRonin
          Ah ha! Your famous QStyledItemDelegate, which I have steadfastly not got into so far! OK, let me go set this up and see where I get. I may have further questions for what I'm trying to actually achieve fully (the above is just one part), I'll come back to your expertise on that if I may....

          1 Reply Last reply
          0
          • D Offline
            D Offline
            dheerendra
            Qt Champions 2022
            wrote on 14 Dec 2018, 11:13 last edited by
            #4

            Did you get a chance to look other set of signals like.

            currentItemChanged(..)
            clicked(..)
            entered(..)
            pressed(..)
            

            Dheerendra
            @Community Service
            Certified Qt Specialist
            http://www.pthinks.com

            J 1 Reply Last reply 14 Dec 2018, 11:14
            1
            • D dheerendra
              14 Dec 2018, 11:13

              Did you get a chance to look other set of signals like.

              currentItemChanged(..)
              clicked(..)
              entered(..)
              pressed(..)
              
              J Offline
              J Offline
              JonB
              wrote on 14 Dec 2018, 11:14 last edited by
              #5

              @dheerendra
              Yep, I looked through all those. None of them pick up, e.g., terminating editing, at least, so no good....

              1 Reply Last reply
              0
              • D Offline
                D Offline
                dheerendra
                Qt Champions 2022
                wrote on 14 Dec 2018, 11:17 last edited by
                #6

                terminatingEditing has to be custom. You have no choice other the your own delegate :)

                Dheerendra
                @Community Service
                Certified Qt Specialist
                http://www.pthinks.com

                J 1 Reply Last reply 14 Dec 2018, 11:19
                0
                • D dheerendra
                  14 Dec 2018, 11:17

                  terminatingEditing has to be custom. You have no choice other the your own delegate :)

                  J Offline
                  J Offline
                  JonB
                  wrote on 14 Dec 2018, 11:19 last edited by
                  #7

                  @dheerendra
                  Yep, that's fine, as I wrote to @VRonin above, I am presently having a go at his code (converted to Python/PyQt!) to get me going....

                  1 Reply Last reply
                  0
                  • D Offline
                    D Offline
                    DyraSan
                    wrote on 14 Dec 2018, 11:48 last edited by DyraSan
                    #8

                    Hey, I am newbie

                    and I want to know how to edit a QListWidget item without removing it to edit and adding back?

                    thanks,

                    Kissanime

                    1 Reply Last reply
                    0
                    • V Offline
                      V Offline
                      VRonin
                      wrote on 14 Dec 2018, 12:03 last edited by
                      #9

                      listWidget->item(i)->setData(role,data);

                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                      ~Napoleon Bonaparte

                      On a crusade to banish setIndexWidget() from the holy land of Qt

                      1 Reply Last reply
                      3
                      • V VRonin
                        14 Dec 2018, 11:03

                        That's because it's handled by the delegate.
                        Subclass QStyledItemDelegate

                        class SignalItemDelegate : public QStyledItemDelegate{
                        Q_OBJECT
                        Q_DISABLE_COPY(SignalItemDelegate)
                        public:
                        explicit SignalItemDelegate(QObject* parent = Q_NULLPTR):QStyledItemDelegate(parent){
                        QObject::connect(this,&SignalItemDelegate::closeEditor,this,&SignalItemDelegate::editFinished);
                        }
                        void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE{
                        editStarted();
                        return QStyledItemDelegate::setEditorData(editor,index);
                        }
                        Q_SIGNALS:
                        void editStarted();
                        void editFinished();
                        };
                        

                        And then call something like:

                        SignalItemDelegate* delegate = new SignalItemDelegate(listWidget);
                        QObject::connect(delegate,&SignalItemDelegate::editStarted,[](){qDebug("edit started")});
                        QObject::connect(delegate,&SignalItemDelegate::editFinished,[](){qDebug("edit finished")});
                        listWidget->setItemDelegate(delegate);
                        
                        J Offline
                        J Offline
                        JonB
                        wrote on 14 Dec 2018, 15:36 last edited by JonB
                        #10

                        @VRonin
                        OK, I have come up with the basics which I believe emulates/corresponds to your C++ as best I can. I now have a further question about something which you don't do in your example which I would like.

                        First, I'll paste the guts of my Python/PyQt code. You'll see it's rather different in signals/slots/emits. This may help or hinder, I don't know, feel free to ignore if that's best.

                        class JEditableListStyledItemDelegate(QtWidgets.QStyledItemDelegate):
                            # class variable for "editStarted" signal
                            editStarted = QtCore.pyqtSignal(name='editStarted')
                            # class variable for "editFinished" signal
                            editFinished = QtCore.pyqtSignal(name='editFinished')
                        
                            def __init__(self, parent: QtCore.QObject=None):
                                super().__init__(parent)
                        
                                self.closeEditor.connect(self.editFinished)
                        
                            def setEditorData(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
                                self.editStarted.emit()
                                return super().setEditorData(editor, index)
                        
                        
                        class JEditableListWidget(QtWidgets.QListWidget):
                            # class variable for "editStarted" signal
                            editStarted = QtCore.pyqtSignal(name='editStarted')
                            # class variable for "editFinished" signal
                            editFinished = QtCore.pyqtSignal(name='editFinished')
                        
                            def __init__(self, parent: QtWidgets.QWidget=None):
                                super().__init__(parent)
                        
                                styledItemDelegate = JEditableListStyledItemDelegate(self)
                                styledItemDelegate.editStarted.connect(self.editStarted)
                                styledItemDelegate.editFinished.connect(self.editFinished)
                                self.setItemDelegate(styledItemDelegate)
                        
                        

                        The above "works". The JEditableListStyledItemDelegate emits its own editStarted/Finished signals. The JEditableListWidget "redirects" those signals to its own editStarted/Finished signals, for the outside world to slot onto. So far so good?

                        Now, if you look at a QListWidget signal for an item like http://doc.qt.io/qt-5/qlistwidget.html#itemChanged, its signature is QListWidget::itemChanged(QListWidgetItem *item). Hence it sends to the outside world a parameter of the QListWidgetItem which has been changed. My editStarted/Finished signals should do the same.

                        Now I get stuck as to how I'm supposed to do that.

                        I start by changing my JEditableListWidget signals to:

                        class JEditableListWidget(QtWidgets.QListWidget):
                            # class variable for "editStarted" signal, including item
                            editStarted = QtCore.pyqtSignal(QtWidgets.QListWidgetItem, name='editStarted')
                            # class variable for "editFinished" signal, including item
                            editFinished = QtCore.pyqtSignal(QtWidgets.QListWidgetItem, name='editFinished')
                        

                        The extra first parameter of QtWidgets.QListWidgetItem declares the signal as forwarding the item as a parameter.

                        Now how do I pass that? I believe that (a) I will need a lambda for the signal and (b) I need to know what item is being edited. I think where I had:

                        styledItemDelegate.editStarted.connect(self.editStarted)
                        

                        I now need something like:

                        styledItemDelegate.editStarted.connect(lambda: self.editStarted(someItemWidget))
                        

                        [Actually I suspect it might be more like:

                        styledItemDelegate.editStarted.connect(lambda: self.editStarted.emit(someItemWidget))
                        

                        ]
                        Could you tell me:

                        • Is this how you would do it in C++ (e.g. with a lambda)? (If not, I may be doing it wrong.) Could you show me your C++ for how you would do this and I will attempt to figure it to PyQt? (Also, I believe your code omitted an emit because I know that's a NO-OP in C++; but could you include it wherever it should be, because in PyQt you can see we have to use emit as a function.)

                        • How do I calculate what the someItemWidget will be, from inside my JEditableListWidget?

                        1 Reply Last reply
                        0
                        • V Offline
                          V Offline
                          VRonin
                          wrote on 14 Dec 2018, 17:01 last edited by
                          #11

                          It's actually super easy.
                          In the delegate:

                          • add an argument of type QtCore.QModelIndex to the signals.
                          • remove self.closeEditor.connect(self.editFinished)
                          • change self.editStarted.emit() to self.editStarted.emit(index)
                          • add
                          def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
                                  self.editFinished.emit(index)
                                  return super().destroyEditor(editor, index)
                          

                          Now in JEditableListWidget you can connect a slot that accepts a QtCore.QModelIndex. To find the corresponding QListWidgetItem you can just call item(index.row())

                          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                          ~Napoleon Bonaparte

                          On a crusade to banish setIndexWidget() from the holy land of Qt

                          J 2 Replies Last reply 14 Dec 2018, 19:52
                          4
                          • V VRonin
                            14 Dec 2018, 17:01

                            It's actually super easy.
                            In the delegate:

                            • add an argument of type QtCore.QModelIndex to the signals.
                            • remove self.closeEditor.connect(self.editFinished)
                            • change self.editStarted.emit() to self.editStarted.emit(index)
                            • add
                            def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
                                    self.editFinished.emit(index)
                                    return super().destroyEditor(editor, index)
                            

                            Now in JEditableListWidget you can connect a slot that accepts a QtCore.QModelIndex. To find the corresponding QListWidgetItem you can just call item(index.row())

                            J Offline
                            J Offline
                            JonB
                            wrote on 14 Dec 2018, 19:52 last edited by
                            #12

                            @VRonin
                            TVM. I'll implement next week. You're passing the QtCore.QModelIndex available in the JEditableListStyledItemDelegate class up the signal chain to indicate which QListWidgetItem was acted upon. I thought the JEditableListWidget class would know which item in the list was being edited, but seemingly not?

                            1 Reply Last reply
                            0
                            • V VRonin
                              14 Dec 2018, 17:01

                              It's actually super easy.
                              In the delegate:

                              • add an argument of type QtCore.QModelIndex to the signals.
                              • remove self.closeEditor.connect(self.editFinished)
                              • change self.editStarted.emit() to self.editStarted.emit(index)
                              • add
                              def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
                                      self.editFinished.emit(index)
                                      return super().destroyEditor(editor, index)
                              

                              Now in JEditableListWidget you can connect a slot that accepts a QtCore.QModelIndex. To find the corresponding QListWidgetItem you can just call item(index.row())

                              J Offline
                              J Offline
                              JonB
                              wrote on 20 Dec 2018, 14:21 last edited by JonB
                              #13

                              @VRonin
                              Well, I followed your principle of having the QStyledItemDelegate emit signals with the QModelIndex as a parameter.

                              I diverged from yours slightly. Given that I'm now overriding destroyEditor() so as to access the index, it seems to me that I might as well be consistent and override createEditor() for beginning editing. So the following two overrides:

                              class JEditableListStyledItemDelegate(QtWidgets.QStyledItemDelegate):
                                  # class variable for "editStarted" signal, with QModelIndex parameter
                                  editStarted = QtCore.pyqtSignal(QtCore.QModelIndex, name='editStarted')
                                  # class variable for "editFinished" signal, with QModelIndex parameter
                                  editFinished = QtCore.pyqtSignal(QtCore.QModelIndex, name='editFinished')
                              
                                  def createEditor(self, parent: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex):
                                      editor = super().createEditor(parent, option, index)
                                      if editor is not None:
                                          self.editStarted.emit(index)
                                      return editor
                              
                                  def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
                                      self.editFinished.emit(index)
                                      return super().destroyEditor(editor, index)
                              

                              The above seems to give me fairly reliable signals for what I want, e.g. disable other buttons for doing things to QListWidget (add row, delete row) while the user is editing a row.

                              Seem reasonable to you? Thanks for all your input.

                              V 1 Reply Last reply 20 Dec 2018, 14:25
                              2
                              • J JonB
                                20 Dec 2018, 14:21

                                @VRonin
                                Well, I followed your principle of having the QStyledItemDelegate emit signals with the QModelIndex as a parameter.

                                I diverged from yours slightly. Given that I'm now overriding destroyEditor() so as to access the index, it seems to me that I might as well be consistent and override createEditor() for beginning editing. So the following two overrides:

                                class JEditableListStyledItemDelegate(QtWidgets.QStyledItemDelegate):
                                    # class variable for "editStarted" signal, with QModelIndex parameter
                                    editStarted = QtCore.pyqtSignal(QtCore.QModelIndex, name='editStarted')
                                    # class variable for "editFinished" signal, with QModelIndex parameter
                                    editFinished = QtCore.pyqtSignal(QtCore.QModelIndex, name='editFinished')
                                
                                    def createEditor(self, parent: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex):
                                        editor = super().createEditor(parent, option, index)
                                        if editor is not None:
                                            self.editStarted.emit(index)
                                        return editor
                                
                                    def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
                                        self.editFinished.emit(index)
                                        return super().destroyEditor(editor, index)
                                

                                The above seems to give me fairly reliable signals for what I want, e.g. disable other buttons for doing things to QListWidget (add row, delete row) while the user is editing a row.

                                Seem reasonable to you? Thanks for all your input.

                                V Offline
                                V Offline
                                VRonin
                                wrote on 20 Dec 2018, 14:25 last edited by
                                #14

                                @JonB said in QListWidget item editing:

                                Given that I'm now overriding destroyEditor() so as to access the index, it seems to me that I might as well be consistent and override createEditor() for beginning editing.

                                Only doubt I have is if the delegate recycles the editor. Try going from editing 1 cell to editing another one directly and see if both create and destroy are called or it just calls setEditorData

                                "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                                ~Napoleon Bonaparte

                                On a crusade to banish setIndexWidget() from the holy land of Qt

                                J 1 Reply Last reply 20 Dec 2018, 14:45
                                1
                                • V VRonin
                                  20 Dec 2018, 14:25

                                  @JonB said in QListWidget item editing:

                                  Given that I'm now overriding destroyEditor() so as to access the index, it seems to me that I might as well be consistent and override createEditor() for beginning editing.

                                  Only doubt I have is if the delegate recycles the editor. Try going from editing 1 cell to editing another one directly and see if both create and destroy are called or it just calls setEditorData

                                  J Offline
                                  J Offline
                                  JonB
                                  wrote on 20 Dec 2018, 14:45 last edited by JonB
                                  #15

                                  @VRonin
                                  OK, thanks! You should have said that concern was why you had overridden different/"inconsistent" methods, I had no idea why! Tried with direct click to edit another item, all is well, does call destroy and then create.

                                  So I shall mark what I did as the solution to my original question. TVM.

                                  P.S.
                                  Your Xmas hat looks good. But a lot of others are also wearing them...! :)

                                  mrjjM V 2 Replies Last reply 20 Dec 2018, 14:47
                                  1
                                  • J JonB
                                    20 Dec 2018, 14:45

                                    @VRonin
                                    OK, thanks! You should have said that concern was why you had overridden different/"inconsistent" methods, I had no idea why! Tried with direct click to edit another item, all is well, does call destroy and then create.

                                    So I shall mark what I did as the solution to my original question. TVM.

                                    P.S.
                                    Your Xmas hat looks good. But a lot of others are also wearing them...! :)

                                    mrjjM Offline
                                    mrjjM Offline
                                    mrjj
                                    Lifetime Qt Champion
                                    wrote on 20 Dec 2018, 14:47 last edited by
                                    #16

                                    @JonB said in QListWidget item editing:

                                    Your Xmas hat looks good. But a lot of others are also wearing them...! :)

                                    Well that is is on purpose :)

                                    1 Reply Last reply
                                    1
                                    • J JonB
                                      20 Dec 2018, 14:45

                                      @VRonin
                                      OK, thanks! You should have said that concern was why you had overridden different/"inconsistent" methods, I had no idea why! Tried with direct click to edit another item, all is well, does call destroy and then create.

                                      So I shall mark what I did as the solution to my original question. TVM.

                                      P.S.
                                      Your Xmas hat looks good. But a lot of others are also wearing them...! :)

                                      V Offline
                                      V Offline
                                      VRonin
                                      wrote on 20 Dec 2018, 15:36 last edited by
                                      #17

                                      @JonB said in QListWidget item editing:

                                      Your Xmas hat looks good

                                      All credit goes to @mrjj

                                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                                      ~Napoleon Bonaparte

                                      On a crusade to banish setIndexWidget() from the holy land of Qt

                                      1 Reply Last reply
                                      1

                                      10/17

                                      14 Dec 2018, 15:36

                                      • Login

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