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. Custom checkbox in a table column
Forum Updated to NodeBB v4.3 + New Features

Custom checkbox in a table column

Scheduled Pinned Locked Moved Unsolved Qt for Python
14 Posts 3 Posters 2.2k 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.
  • SGaistS Offline
    SGaistS Offline
    SGaist
    Lifetime Qt Champion
    wrote on last edited by
    #2

    Hi,

    How did you implement your delegate ?

    Interested in AI ? www.idiap.ch
    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

    G 1 Reply Last reply
    0
    • SGaistS SGaist

      Hi,

      How did you implement your delegate ?

      G Offline
      G Offline
      Gazi
      wrote on last edited by
      #3

      @SGaist Here is the minimum example i could make. I know it looks long, but it is just the minimum number of required functions implemented.

      import sys
      from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QTableView, QCheckBox, QStyledItemDelegate
      from PySide6.QtCore import Qt, QModelIndex, QAbstractTableModel
      from PySide6.QtGui import QColor
      import numpy as np
      import pandas as pd
      
      class item_delegate(QStyledItemDelegate):
          
          def __init__(self, parent):
              
              super().__init__(parent)
              
              # Define checkbox icon dimension
              self.checkbox_width = 16
              self.checkbox_height = 16
              self.alternate_row_background_color = QColor(0, 0, 0, 25)
              
          
          def createEditor(self, parent, option, index):
              if index.column() == 0:
                  editor = QCheckBox()
                  editor.setChecked(index.data(Qt.EditRole))
                  return editor
              return super().createEditor(parent, option, index)
          
      
          def setEditorData(self, editor, index):
              if index.column() == 0:
                  editor.setChecked(index.data(Qt.EditRole))
              else:
                  super().setEditorData(editor, index)
      
          
          def setModelData(self, editor, model, index):
              if index.column() == 0:
                  model.setData(index, editor.isChecked(), Qt.EditRole)
              else:
                  super().setModelData(editor, model, index)
      
          
          def paint(self, painter, option, index):
              rect = option.rect    
             
              super().paint(painter, option, index)            
              
              # Set background color
              if index.row() % 2 == 1:
                  painter.fillRect(rect, self.alternate_row_background_color)
                  
      
      class custom_table_model(QAbstractTableModel):
          def __init__(self, data = None):
              super().__init__()
              
              if data is None:
                  data = pd.DataFrame()
                  
              self._data = data
                      
          def flags(self, index):
              flags = super().flags(index)
              flags |= Qt.ItemIsEditable
              return flags
              
          
          def rowCount(self, parent = QModelIndex()):
              return len(self._data)
      
      
          def columnCount(self, parent = QModelIndex()):
              return len(self._data.columns)
      
      
          def data(self, index, role = Qt.DisplayRole):
              if role == Qt.DisplayRole or role == Qt.EditRole:
                  result = self._data.iloc[index.row(), index.column()]
                  if not isinstance(result, (bool, np.bool_)):
                      result = str(result)
                  else:
                      result = bool(result)
                  return result
              return None
      
          
          def setData(self, index, value, role = Qt.DisplayRole):
              if role == Qt.DisplayRole or role == Qt.EditRole:
                  self._data.iloc[index.row(), index.column()] = value
                  return True
              return super().setData(index, value, role)
      
      
          def headerData(self, section, orientation, role = Qt.DisplayRole):
              if role == Qt.DisplayRole:
                  if orientation == Qt.Horizontal:
                      return str(self._data.columns[section])
                  if orientation == Qt.Vertical:
                      return str(self._data.index[section])
              return None
          
      
      class custom_table(QTableView):
          def __init__(self, data = None):       
              super().__init__()
              
              # Init data
              self.initialize_data()
              
              # Set model
              self.setModel(custom_table_model(self.data))
      
              # Set item delegate
              self.setItemDelegate(item_delegate(parent = self))
              
              
          def initialize_data(self):
              self.data = pd.DataFrame({
                              'include': [True, True, True, True],
                              'x': [1, 12, 50, 65],
                              'animal': ['dog', 'cat', 'cow', 'horse']
                              })    
                      
      
      class MyWindow(QMainWindow):
          def __init__(self):
              super().__init__()
      
              self.init_ui()
      
          def init_ui(self):
              self.setWindowTitle("Minimal PySide6 Example")
      
              # Create a central widget
              central_widget = QWidget()
              self.setCentralWidget(central_widget)
      
              # Create a vertical layout for the central widget
              layout = QVBoxLayout(central_widget)
      
              # Create the drawer widget and add it to the layout
              table = custom_table()
              layout.addWidget(table)
              
      
      def main():
          if not QApplication.instance():
              app = QApplication(sys.argv)
          else:
              app = QApplication.instance()    
          
          window = MyWindow()
          window.show()
          sys.exit(app.exec())
      
      main()
      
      

      I am looking forward to your answer.

      Thanks

      G 1 Reply Last reply
      0
      • G Gazi

        @SGaist Here is the minimum example i could make. I know it looks long, but it is just the minimum number of required functions implemented.

        import sys
        from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QTableView, QCheckBox, QStyledItemDelegate
        from PySide6.QtCore import Qt, QModelIndex, QAbstractTableModel
        from PySide6.QtGui import QColor
        import numpy as np
        import pandas as pd
        
        class item_delegate(QStyledItemDelegate):
            
            def __init__(self, parent):
                
                super().__init__(parent)
                
                # Define checkbox icon dimension
                self.checkbox_width = 16
                self.checkbox_height = 16
                self.alternate_row_background_color = QColor(0, 0, 0, 25)
                
            
            def createEditor(self, parent, option, index):
                if index.column() == 0:
                    editor = QCheckBox()
                    editor.setChecked(index.data(Qt.EditRole))
                    return editor
                return super().createEditor(parent, option, index)
            
        
            def setEditorData(self, editor, index):
                if index.column() == 0:
                    editor.setChecked(index.data(Qt.EditRole))
                else:
                    super().setEditorData(editor, index)
        
            
            def setModelData(self, editor, model, index):
                if index.column() == 0:
                    model.setData(index, editor.isChecked(), Qt.EditRole)
                else:
                    super().setModelData(editor, model, index)
        
            
            def paint(self, painter, option, index):
                rect = option.rect    
               
                super().paint(painter, option, index)            
                
                # Set background color
                if index.row() % 2 == 1:
                    painter.fillRect(rect, self.alternate_row_background_color)
                    
        
        class custom_table_model(QAbstractTableModel):
            def __init__(self, data = None):
                super().__init__()
                
                if data is None:
                    data = pd.DataFrame()
                    
                self._data = data
                        
            def flags(self, index):
                flags = super().flags(index)
                flags |= Qt.ItemIsEditable
                return flags
                
            
            def rowCount(self, parent = QModelIndex()):
                return len(self._data)
        
        
            def columnCount(self, parent = QModelIndex()):
                return len(self._data.columns)
        
        
            def data(self, index, role = Qt.DisplayRole):
                if role == Qt.DisplayRole or role == Qt.EditRole:
                    result = self._data.iloc[index.row(), index.column()]
                    if not isinstance(result, (bool, np.bool_)):
                        result = str(result)
                    else:
                        result = bool(result)
                    return result
                return None
        
            
            def setData(self, index, value, role = Qt.DisplayRole):
                if role == Qt.DisplayRole or role == Qt.EditRole:
                    self._data.iloc[index.row(), index.column()] = value
                    return True
                return super().setData(index, value, role)
        
        
            def headerData(self, section, orientation, role = Qt.DisplayRole):
                if role == Qt.DisplayRole:
                    if orientation == Qt.Horizontal:
                        return str(self._data.columns[section])
                    if orientation == Qt.Vertical:
                        return str(self._data.index[section])
                return None
            
        
        class custom_table(QTableView):
            def __init__(self, data = None):       
                super().__init__()
                
                # Init data
                self.initialize_data()
                
                # Set model
                self.setModel(custom_table_model(self.data))
        
                # Set item delegate
                self.setItemDelegate(item_delegate(parent = self))
                
                
            def initialize_data(self):
                self.data = pd.DataFrame({
                                'include': [True, True, True, True],
                                'x': [1, 12, 50, 65],
                                'animal': ['dog', 'cat', 'cow', 'horse']
                                })    
                        
        
        class MyWindow(QMainWindow):
            def __init__(self):
                super().__init__()
        
                self.init_ui()
        
            def init_ui(self):
                self.setWindowTitle("Minimal PySide6 Example")
        
                # Create a central widget
                central_widget = QWidget()
                self.setCentralWidget(central_widget)
        
                # Create a vertical layout for the central widget
                layout = QVBoxLayout(central_widget)
        
                # Create the drawer widget and add it to the layout
                table = custom_table()
                layout.addWidget(table)
                
        
        def main():
            if not QApplication.instance():
                app = QApplication(sys.argv)
            else:
                app = QApplication.instance()    
            
            window = MyWindow()
            window.show()
            sys.exit(app.exec())
        
        main()
        
        

        I am looking forward to your answer.

        Thanks

        G Offline
        G Offline
        Gazi
        wrote on last edited by
        #4

        @SGaist You can simply copy the code into a file and it should run as it is.

        SGaistS 1 Reply Last reply
        0
        • G Gazi

          @SGaist You can simply copy the code into a file and it should run as it is.

          SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #5

          You have to handle the Qt.CheckStateRole in your model.

          Then in your delegate, paint whatever you want in place of the checkbox. I would recommend that you remove the delegate while adding the support for Qt.CheckStateRole and once you have that working you can enable the delegate again.

          On a side note, you forget to set the parent of the editor, it will thus be rendered outside the view as an independent widget, I don't think you want that.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          G 1 Reply Last reply
          1
          • SGaistS SGaist

            You have to handle the Qt.CheckStateRole in your model.

            Then in your delegate, paint whatever you want in place of the checkbox. I would recommend that you remove the delegate while adding the support for Qt.CheckStateRole and once you have that working you can enable the delegate again.

            On a side note, you forget to set the parent of the editor, it will thus be rendered outside the view as an independent widget, I don't think you want that.

            G Offline
            G Offline
            Gazi
            wrote on last edited by
            #6

            @SGaist

            Well, first, the parent helped to have the checkbox not as a separate window.

            Second, handling the Qt.CheckStateRole is not the solution, i believe. Let me explain why:
            In my use case, i need to accomplish smth more complex than just a regular checkbox. I want to have full control over the checkbox.

            • For starters, as it is implemented now, the checkbox is shown only when the cell is double clicked, i.e. in the edit mode. Normally it shows the string, true or false.
            • Second, i want the checkbox to be shown in the middle of the cell and I have 2 custom images for the checkbox checked and unchecked states. I tried but could not manage to use the paint event of my custom checkbox inside the paint event of the delegate.
            • Third, in my custom checkbox, I handle the mouse events explicitly, so that when i hover the checkbox, i can give the user an indication that he can click the checkbox, i.e. i change the paint of the checkbox depending on whether the user is hovering the checkbox or not.

            Considering these points, i do not believe that the CheckStateRole is the solution, but somehow i need to be able to use a custom component in the cells of a table. I cannot use the QTableWidgetItem, because this will make my code slow, since in my use case the table can have 10000 lines.

            Any help is appreciated on how to achieve these goals.

            Thank you.

            Bests,
            Gazi

            JonBJ 1 Reply Last reply
            0
            • G Gazi

              @SGaist

              Well, first, the parent helped to have the checkbox not as a separate window.

              Second, handling the Qt.CheckStateRole is not the solution, i believe. Let me explain why:
              In my use case, i need to accomplish smth more complex than just a regular checkbox. I want to have full control over the checkbox.

              • For starters, as it is implemented now, the checkbox is shown only when the cell is double clicked, i.e. in the edit mode. Normally it shows the string, true or false.
              • Second, i want the checkbox to be shown in the middle of the cell and I have 2 custom images for the checkbox checked and unchecked states. I tried but could not manage to use the paint event of my custom checkbox inside the paint event of the delegate.
              • Third, in my custom checkbox, I handle the mouse events explicitly, so that when i hover the checkbox, i can give the user an indication that he can click the checkbox, i.e. i change the paint of the checkbox depending on whether the user is hovering the checkbox or not.

              Considering these points, i do not believe that the CheckStateRole is the solution, but somehow i need to be able to use a custom component in the cells of a table. I cannot use the QTableWidgetItem, because this will make my code slow, since in my use case the table can have 10000 lines.

              Any help is appreciated on how to achieve these goals.

              Thank you.

              Bests,
              Gazi

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

              @Gazi said in Custom checkbox in a table column:

              Second, handling the Qt.CheckStateRole is not the solution,

              I think you are mistaken. @SGaist will correct me if not or flesh it out. Qt.CheckStateRole is just the model's representation of whether the checkbox is unchecked/checked (or tristate) communicated back & forth with the view. All 3 of your issues don't have anything to do with that. They should be handled in the delegate which handles the input/display. I believe all of them can be achieved there.

              G 1 Reply Last reply
              0
              • JonBJ JonB

                @Gazi said in Custom checkbox in a table column:

                Second, handling the Qt.CheckStateRole is not the solution,

                I think you are mistaken. @SGaist will correct me if not or flesh it out. Qt.CheckStateRole is just the model's representation of whether the checkbox is unchecked/checked (or tristate) communicated back & forth with the view. All 3 of your issues don't have anything to do with that. They should be handled in the delegate which handles the input/display. I believe all of them can be achieved there.

                G Offline
                G Offline
                Gazi
                wrote on last edited by
                #8

                @JonB said in Custom checkbox in a table column:

                Qt.CheckStateRole

                Exactly, the Qt.CheckStateRole does not solve the issues I have with how the checkbox is displayed.

                JonBJ 1 Reply Last reply
                0
                • G Gazi

                  @JonB said in Custom checkbox in a table column:

                  Qt.CheckStateRole

                  Exactly, the Qt.CheckStateRole does not solve the issues I have with how the checkbox is displayed.

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

                  @Gazi
                  Which is why @SGaist wrote:

                  @SGaist said in Custom checkbox in a table column:

                  You have to handle the Qt.CheckStateRole in your model.
                  Then in your delegate, paint whatever you want in place of the checkbox

                  G 1 Reply Last reply
                  0
                  • JonBJ JonB

                    @Gazi
                    Which is why @SGaist wrote:

                    @SGaist said in Custom checkbox in a table column:

                    You have to handle the Qt.CheckStateRole in your model.
                    Then in your delegate, paint whatever you want in place of the checkbox

                    G Offline
                    G Offline
                    Gazi
                    wrote on last edited by
                    #10

                    @JonB Right. I managed to somehow use the paint function of the custom checkbox I created (no clue why it is not trival to just use a new component in the cells you want). However, I have now the issue that, even though i am using the paint function of my custom checkbox component, i can only interact with the checkbox after double clicking the cell, i.e. entering the edit mode.

                    How can I make the cell in edit mode all the time, or even better, once I hover on a cell, it should become automatically editable?

                    Thanks.

                    JonBJ 1 Reply Last reply
                    0
                    • G Gazi

                      @JonB Right. I managed to somehow use the paint function of the custom checkbox I created (no clue why it is not trival to just use a new component in the cells you want). However, I have now the issue that, even though i am using the paint function of my custom checkbox component, i can only interact with the checkbox after double clicking the cell, i.e. entering the edit mode.

                      How can I make the cell in edit mode all the time, or even better, once I hover on a cell, it should become automatically editable?

                      Thanks.

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

                      @Gazi
                      Here is my recollection:

                      • If you are not in edit mode but want a checkbox to be editable (i.e. clickable) I think you would have to do that by recognising the click on the box area and toggling the state from code.

                      • You can enter edit mode programmatically any time you want (e.g. on hover) by calling void QAbstractItemView::edit(const QModelIndex &index) on the QTableView yourself at whatever point. You can also affect what causes the table view to enter edit mode itself via enum QAbstractItemView::EditTrigger and https://doc.qt.io/qt-6/qabstractitemview.html#editTriggers-prop.

                      Section https://doc.qt.io/qt-6/qstyleditemdelegate.html#subclassing-qstyleditemdelegate also has a bit to say about checkboxes.

                      Finally, I think you should have a look at the Star Delegate Example. That has things like always painting with StarRating::EditMode::Editable and

                      It is possible to open editors programmatically by calling QAbstractItemView::edit(), instead of relying on edit triggers. This could be used to support other edit triggers than those offered by the QAbstractItemView::EditTrigger enum. For example, in the Star Delegate example, hovering over an item with the mouse might make sense as a way to pop up an editor.

                      G 1 Reply Last reply
                      0
                      • JonBJ JonB

                        @Gazi
                        Here is my recollection:

                        • If you are not in edit mode but want a checkbox to be editable (i.e. clickable) I think you would have to do that by recognising the click on the box area and toggling the state from code.

                        • You can enter edit mode programmatically any time you want (e.g. on hover) by calling void QAbstractItemView::edit(const QModelIndex &index) on the QTableView yourself at whatever point. You can also affect what causes the table view to enter edit mode itself via enum QAbstractItemView::EditTrigger and https://doc.qt.io/qt-6/qabstractitemview.html#editTriggers-prop.

                        Section https://doc.qt.io/qt-6/qstyleditemdelegate.html#subclassing-qstyleditemdelegate also has a bit to say about checkboxes.

                        Finally, I think you should have a look at the Star Delegate Example. That has things like always painting with StarRating::EditMode::Editable and

                        It is possible to open editors programmatically by calling QAbstractItemView::edit(), instead of relying on edit triggers. This could be used to support other edit triggers than those offered by the QAbstractItemView::EditTrigger enum. For example, in the Star Delegate example, hovering over an item with the mouse might make sense as a way to pop up an editor.

                        G Offline
                        G Offline
                        Gazi
                        wrote on last edited by
                        #12

                        @JonB How to close then the editor when going out of the cell? One could use the closeEditor() function, but this needs the editor as input, which is not returned by the edit() function.

                        SGaistS JonBJ 2 Replies Last reply
                        0
                        • G Gazi

                          @JonB How to close then the editor when going out of the cell? One could use the closeEditor() function, but this needs the editor as input, which is not returned by the edit() function.

                          SGaistS Offline
                          SGaistS Offline
                          SGaist
                          Lifetime Qt Champion
                          wrote on last edited by
                          #13

                          @Gazi Check editorEvent

                          Interested in AI ? www.idiap.ch
                          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                          1 Reply Last reply
                          0
                          • G Gazi

                            @JonB How to close then the editor when going out of the cell? One could use the closeEditor() function, but this needs the editor as input, which is not returned by the edit() function.

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

                            @Gazi
                            No, but createEditor() is called, which you could always save. There is editorEvent() too. I suggested you look at the Star Delegate code, which even has a void StarDelegate::commitAndCloseEditor() method. Did you do so?

                            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