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 1.9k 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.
  • G Offline
    G Offline
    Gazi
    wrote on 24 Jan 2024, 18:25 last edited by
    #1

    Hello,

    I am trying to show a table in pyqt, where for the first column i have a boolean state. I would like the cells of the first column to show a checkbox only. I want to use custom images for the true/false states of the checkbox.

    I have been trying for some time, but unfortunately i did not have success. I have been playing with item delegates, but no luck.

    Any help is greatly appreciated.

    Bests,
    Gazi

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 24 Jan 2024, 19:27 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 24 Jan 2024, 20:39
      0
      • S SGaist
        24 Jan 2024, 19:27

        Hi,

        How did you implement your delegate ?

        G Offline
        G Offline
        Gazi
        wrote on 24 Jan 2024, 20:39 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 24 Jan 2024, 20:49
        0
        • G Gazi
          24 Jan 2024, 20:39

          @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 24 Jan 2024, 20:49 last edited by
          #4

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

          S 1 Reply Last reply 27 Jan 2024, 20:53
          0
          • G Gazi
            24 Jan 2024, 20:49

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

            S Offline
            S Offline
            SGaist
            Lifetime Qt Champion
            wrote on 27 Jan 2024, 20:53 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 28 Jan 2024, 20:38
            1
            • S SGaist
              27 Jan 2024, 20:53

              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 28 Jan 2024, 20:38 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

              J 1 Reply Last reply 28 Jan 2024, 20:58
              0
              • G Gazi
                28 Jan 2024, 20:38

                @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

                J Offline
                J Offline
                JonB
                wrote on 28 Jan 2024, 20:58 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 29 Jan 2024, 08:11
                0
                • J JonB
                  28 Jan 2024, 20:58

                  @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 29 Jan 2024, 08:11 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.

                  J 1 Reply Last reply 29 Jan 2024, 11:00
                  0
                  • G Gazi
                    29 Jan 2024, 08:11

                    @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.

                    J Offline
                    J Offline
                    JonB
                    wrote on 29 Jan 2024, 11:00 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 29 Jan 2024, 12:32
                    0
                    • J JonB
                      29 Jan 2024, 11:00

                      @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 29 Jan 2024, 12:32 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.

                      J 1 Reply Last reply 29 Jan 2024, 13:05
                      0
                      • G Gazi
                        29 Jan 2024, 12:32

                        @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.

                        J Offline
                        J Offline
                        JonB
                        wrote on 29 Jan 2024, 13:05 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 29 Jan 2024, 13:35
                        0
                        • J JonB
                          29 Jan 2024, 13:05

                          @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 29 Jan 2024, 13:35 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.

                          S J 2 Replies Last reply 29 Jan 2024, 13:41
                          0
                          • G Gazi
                            29 Jan 2024, 13:35

                            @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.

                            S Offline
                            S Offline
                            SGaist
                            Lifetime Qt Champion
                            wrote on 29 Jan 2024, 13:41 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
                              29 Jan 2024, 13:35

                              @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.

                              J Offline
                              J Offline
                              JonB
                              wrote on 29 Jan 2024, 13:42 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

                              5/14

                              27 Jan 2024, 20:53

                              topic:navigator.unread, 9
                              • Login

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