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. Creating a column of editable Checkbox in QTableview using QItemDelegate
Forum Updated to NodeBB v4.3 + New Features

Creating a column of editable Checkbox in QTableview using QItemDelegate

Scheduled Pinned Locked Moved Unsolved Qt for Python
7 Posts 2 Posters 6.1k 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.
  • H Offline
    H Offline
    hachbani
    wrote on last edited by
    #1

    I'm using Pyside2, Python 3.8. I have a QTableView, the value of the first column is a bool, I want to implement a delegate to paint Editable CheckBoxs in the first column.
    I get a centered CheckBox in my first column, I can't edit it, but, when I double click it, It creates a second Checkbox in the same cell at the left, which is Editable and also sets the new CheckState to my Model. Let's say my centered CheckBox is set to True, I double click, a second Checked CheckBox is created to its left, I Uncheck it, I click somewhere else, the second checkBox disappears, the centered checkBox is set to Unchecked, and my ModelData is updated. (see picture below for what happens after double clicking the centered CheckBox)

    d35113ca-f854-4095-b5f4-f461ba1af59c-image.png

    Here's my Table Model class:

    class TableModel(QtCore.QAbstractTableModel):
    
    	def __init__(self, mlist=None):
    		super(TableModel, self).__init__()
    		self._items = [] if mlist == None else mlist
    		self._header = []
    
    	def rowCount(self, parent = QtCore.QModelIndex):
    		return len(self._items)
    
    	def columnCount(self, parent = QtCore.QModelIndex):
    		return len(self._header)
    
    	def flags(self, index):
    		if index.column() == 0:
    			return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
    		return QtCore.Qt.ItemIsEnabled
    
    	def data(self, index, role = QtCore.Qt.DisplayRole):
    		if not index.isValid():
    		   return None
    		elif role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
    			return self._items[index.row()][index.column()]
    		elif role == QtCore.Qt.CheckStateRole:
    			return None
    		else:
    			return None
    	
    	def setData(self, index, value, role = QtCore.Qt.EditRole):
    		if value is not None and role == QtCore.Qt.EditRole:
    			self._items[index.row()][index.column()] = value
    			self.dataChanged.emit(index, index)
    			return True
    		return False
    

    My CheckBox Delegate class:

    class CheckBoxDelegate(QtWidgets.QItemDelegate):
    
    	def __init__(self, parent = None):
    		QtWidgets.QItemDelegate.__init__(self, parent)
    
    	def createEditor(self, parent, option, index):
    		if not (QtCore.Qt.ItemIsEditable & index.flags()): 
    			return None
    		check = QtWidgets.QCheckBox(parent)
    		check.clicked.connect(self.stateChanged)
    
    		return check
    
    	def setEditorData(self, editor, index):
    		editor.blockSignals(True)
    		editor.setChecked(index.data())
    		editor.blockSignals(False)
    
    	def setModelData(self, editor, model, index):
    		print('set model data', editor.isChecked())
    		model.setData(index, editor.isChecked(), QtCore.Qt.EditRole)
    
    	def paint(self, painter, option, index):
    		print('painting', index.data())
    		value = index.data()
    		if value:
    			value = QtCore.Qt.Checked
    		else:
    			value = QtCore.Qt.Unchecked
    
    		self.drawCheck(painter, option, option.rect, value)
    		self.drawFocus(painter, option, option.rect)
    	
    	@QtCore.Slot()
    	def stateChanged(self):
    		print("sender", self.sender())
    		self.commitData.emit(self.sender())
    

    In My MainWindow Class, I added the following line:

    self.MyTableView.setItemDelegateForColumn(0, CheckBoxDelegate(self))
    

    Any thoughts on how to resolve this ?

    Thanks in advance.

    H 1 Reply Last reply
    0
    • H hachbani

      I'm using Pyside2, Python 3.8. I have a QTableView, the value of the first column is a bool, I want to implement a delegate to paint Editable CheckBoxs in the first column.
      I get a centered CheckBox in my first column, I can't edit it, but, when I double click it, It creates a second Checkbox in the same cell at the left, which is Editable and also sets the new CheckState to my Model. Let's say my centered CheckBox is set to True, I double click, a second Checked CheckBox is created to its left, I Uncheck it, I click somewhere else, the second checkBox disappears, the centered checkBox is set to Unchecked, and my ModelData is updated. (see picture below for what happens after double clicking the centered CheckBox)

      d35113ca-f854-4095-b5f4-f461ba1af59c-image.png

      Here's my Table Model class:

      class TableModel(QtCore.QAbstractTableModel):
      
      	def __init__(self, mlist=None):
      		super(TableModel, self).__init__()
      		self._items = [] if mlist == None else mlist
      		self._header = []
      
      	def rowCount(self, parent = QtCore.QModelIndex):
      		return len(self._items)
      
      	def columnCount(self, parent = QtCore.QModelIndex):
      		return len(self._header)
      
      	def flags(self, index):
      		if index.column() == 0:
      			return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
      		return QtCore.Qt.ItemIsEnabled
      
      	def data(self, index, role = QtCore.Qt.DisplayRole):
      		if not index.isValid():
      		   return None
      		elif role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
      			return self._items[index.row()][index.column()]
      		elif role == QtCore.Qt.CheckStateRole:
      			return None
      		else:
      			return None
      	
      	def setData(self, index, value, role = QtCore.Qt.EditRole):
      		if value is not None and role == QtCore.Qt.EditRole:
      			self._items[index.row()][index.column()] = value
      			self.dataChanged.emit(index, index)
      			return True
      		return False
      

      My CheckBox Delegate class:

      class CheckBoxDelegate(QtWidgets.QItemDelegate):
      
      	def __init__(self, parent = None):
      		QtWidgets.QItemDelegate.__init__(self, parent)
      
      	def createEditor(self, parent, option, index):
      		if not (QtCore.Qt.ItemIsEditable & index.flags()): 
      			return None
      		check = QtWidgets.QCheckBox(parent)
      		check.clicked.connect(self.stateChanged)
      
      		return check
      
      	def setEditorData(self, editor, index):
      		editor.blockSignals(True)
      		editor.setChecked(index.data())
      		editor.blockSignals(False)
      
      	def setModelData(self, editor, model, index):
      		print('set model data', editor.isChecked())
      		model.setData(index, editor.isChecked(), QtCore.Qt.EditRole)
      
      	def paint(self, painter, option, index):
      		print('painting', index.data())
      		value = index.data()
      		if value:
      			value = QtCore.Qt.Checked
      		else:
      			value = QtCore.Qt.Unchecked
      
      		self.drawCheck(painter, option, option.rect, value)
      		self.drawFocus(painter, option, option.rect)
      	
      	@QtCore.Slot()
      	def stateChanged(self):
      		print("sender", self.sender())
      		self.commitData.emit(self.sender())
      

      In My MainWindow Class, I added the following line:

      self.MyTableView.setItemDelegateForColumn(0, CheckBoxDelegate(self))
      

      Any thoughts on how to resolve this ?

      Thanks in advance.

      H Offline
      H Offline
      hachbani
      wrote on last edited by
      #2

      I've tried different fixes:

      • Not overriding paint():

      The column contains either True or false, double clicking the cell creates an editable checkbox

      • the createEditor() method returns None
      def createEditor(self, parent, option, index):
         	return None
      

      It creates a single centered checkbox, no labels, but the checkbox isn't editable

      • Not overriding createEditor() method

      Creates a single centered checkbox with no labels, double clicking the cell replace the checkbox with a combobox (True or False), from which I can change the CheckBox state.

      Neither of those fixes gave me what I'm looking for: A signe centered checkbox created that is editable with a single click

      JonBJ 1 Reply Last reply
      0
      • H hachbani

        I've tried different fixes:

        • Not overriding paint():

        The column contains either True or false, double clicking the cell creates an editable checkbox

        • the createEditor() method returns None
        def createEditor(self, parent, option, index):
           	return None
        

        It creates a single centered checkbox, no labels, but the checkbox isn't editable

        • Not overriding createEditor() method

        Creates a single centered checkbox with no labels, double clicking the cell replace the checkbox with a combobox (True or False), from which I can change the CheckBox state.

        Neither of those fixes gave me what I'm looking for: A signe centered checkbox created that is editable with a single click

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #3

        @hachbani
        I don't know how my solution relates to yours, but: I did this --- centered checkbox, editable. I did not do any paint overriding. I did not store the value in EditRole (I left that empty), only in CheckState role. I did use the Alignment role in data() to make the box be centered. And I did use @Christian-Ehrlicher's recent posted fix to correctly allow centered checkbox --- I'm hoping that by typing his name here he will point you to the code for this, as I can't recall which post it was in.

        Don't know if this helps... :)

        H 1 Reply Last reply
        0
        • JonBJ JonB

          @hachbani
          I don't know how my solution relates to yours, but: I did this --- centered checkbox, editable. I did not do any paint overriding. I did not store the value in EditRole (I left that empty), only in CheckState role. I did use the Alignment role in data() to make the box be centered. And I did use @Christian-Ehrlicher's recent posted fix to correctly allow centered checkbox --- I'm hoping that by typing his name here he will point you to the code for this, as I can't recall which post it was in.

          Don't know if this helps... :)

          H Offline
          H Offline
          hachbani
          wrote on last edited by hachbani
          #4

          @JonB Thanks for your reply ! hope Christian can save my life with a code snippet.

          If I'm getting this right, there's no escape from getting the second checkbox to be created, instead a fix is to center it as well (so It overlays with the first one). Am I getting this right ?

          JonBJ 1 Reply Last reply
          0
          • H hachbani

            @JonB Thanks for your reply ! hope Christian can save my life with a code snippet.

            If I'm getting this right, there's no escape from getting the second checkbox to be created, instead a fix is to center it as well (so It overlays with the first one). Am I getting this right ?

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #5

            @hachbani
            Don't know, and finding it hard looking at my code to remember anything any more! :(

            I don't know all, but here is the nub of what I got from Christian. It's C++, but you seem competent, so should be doable from Python/PySide2:

            // ********** CLASS CheckAlignmentProxyStyle ********** //
            
            /*virtual*/ QRect UIUtils::CheckAlignmentProxyStyle::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const /*override*/
            {
                // `QProxyStyle` to center checkboxes (from `Qt::ItemIsuserCheckable`) on item views (in `QTableView` etc.)
                // Code taken from https://forum.qt.io/topic/94049/how-to-center-a-column-with-a-checkbox-in-qtableview/8
                // To apply this style outside world (e.g. `MainWindow`) must go `qApp->setStyle(new UIUtils::CheckAlignmentProxyStyle);`
                // and individual models' `flags()` need to go:
                //     case QtExtra::CheckAlignmentRole:
                //         return Qt::AlignHCenter;
            
                // call base class
                const QRect baseRes = QProxyStyle::subElementRect(element, option, widget);
            
                if (element == SE_ItemViewItemCheckIndicator)
                {
                    // `SE_ItemViewItemCheckIndicator` => checkbox
                    const QStyleOptionViewItem* const itemOpt = qstyleoption_cast<const QStyleOptionViewItem*>(option) ;
                    Q_ASSERT(itemOpt);
                    // read item's `CheckAlignmentRole` role value
                    const QVariant alignData = itemOpt->index.data(QtExtra::CheckAlignmentRole);
                    if (alignData.isNull())
                        return baseRes;
                    const int alignFlag = alignData.toInt();
                    const QRect itemRect = option->rect;
                    Q_ASSERT(itemRect.width()>baseRes.width() && itemRect.height()>baseRes.height());
                    int x = 0;
                    if (alignFlag & Qt::AlignLeft)
                        x = baseRes.x();
                    else if (alignFlag & Qt::AlignRight)
                        x = itemRect.x() + itemRect.width() - (baseRes.x() - itemRect.x()) - baseRes.width();
                    else if (alignFlag & Qt::AlignHCenter)
                        x = itemRect.x() + (itemRect.width() / 2) - (baseRes.width() / 2);
                    return QRect(QPoint(x, baseRes.y()), baseRes.size());
                }
            
                return baseRes;
            }
            

            Go check out https://forum.qt.io/topic/94049/how-to-center-a-column-with-a-checkbox-in-qtableview/8

            I see that is a @VRonin solution. Yet I remember interacting with @Christian-Ehrlicher. I think he did a fix so a future/current Qt version had it built-in so you wouldn't have to write this code. Hopefully he will clarify, this is what happens to one's memory as one gets older... :(

            I see I added

            namespace QtExtra
            {
            // extra `Qt::ItemDataRole` value for aligning checkboxes in tables
            // see `UIUtils::CheckAlignmentProxyStyle`
            enum { CheckAlignmentRole = Qt::UserRole + Qt::CheckStateRole + Qt::TextAlignmentRole };
            }
            

            I cannot recall, does this give you enough to go on?

            P.S. So if I recall/understand this correctly, I'm not using createEditor() etc. at all. Rather, I'm just using flags() to include Qt.ItemIsUserCheckable. User can check box without going into edit mode. So there is only one checkbox, no overlaying, I never create any QCheckbox, the value is stored in Qt.CheckStateRole. I think it is much simpler than your way.

            H 1 Reply Last reply
            1
            • JonBJ JonB

              @hachbani
              Don't know, and finding it hard looking at my code to remember anything any more! :(

              I don't know all, but here is the nub of what I got from Christian. It's C++, but you seem competent, so should be doable from Python/PySide2:

              // ********** CLASS CheckAlignmentProxyStyle ********** //
              
              /*virtual*/ QRect UIUtils::CheckAlignmentProxyStyle::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const /*override*/
              {
                  // `QProxyStyle` to center checkboxes (from `Qt::ItemIsuserCheckable`) on item views (in `QTableView` etc.)
                  // Code taken from https://forum.qt.io/topic/94049/how-to-center-a-column-with-a-checkbox-in-qtableview/8
                  // To apply this style outside world (e.g. `MainWindow`) must go `qApp->setStyle(new UIUtils::CheckAlignmentProxyStyle);`
                  // and individual models' `flags()` need to go:
                  //     case QtExtra::CheckAlignmentRole:
                  //         return Qt::AlignHCenter;
              
                  // call base class
                  const QRect baseRes = QProxyStyle::subElementRect(element, option, widget);
              
                  if (element == SE_ItemViewItemCheckIndicator)
                  {
                      // `SE_ItemViewItemCheckIndicator` => checkbox
                      const QStyleOptionViewItem* const itemOpt = qstyleoption_cast<const QStyleOptionViewItem*>(option) ;
                      Q_ASSERT(itemOpt);
                      // read item's `CheckAlignmentRole` role value
                      const QVariant alignData = itemOpt->index.data(QtExtra::CheckAlignmentRole);
                      if (alignData.isNull())
                          return baseRes;
                      const int alignFlag = alignData.toInt();
                      const QRect itemRect = option->rect;
                      Q_ASSERT(itemRect.width()>baseRes.width() && itemRect.height()>baseRes.height());
                      int x = 0;
                      if (alignFlag & Qt::AlignLeft)
                          x = baseRes.x();
                      else if (alignFlag & Qt::AlignRight)
                          x = itemRect.x() + itemRect.width() - (baseRes.x() - itemRect.x()) - baseRes.width();
                      else if (alignFlag & Qt::AlignHCenter)
                          x = itemRect.x() + (itemRect.width() / 2) - (baseRes.width() / 2);
                      return QRect(QPoint(x, baseRes.y()), baseRes.size());
                  }
              
                  return baseRes;
              }
              

              Go check out https://forum.qt.io/topic/94049/how-to-center-a-column-with-a-checkbox-in-qtableview/8

              I see that is a @VRonin solution. Yet I remember interacting with @Christian-Ehrlicher. I think he did a fix so a future/current Qt version had it built-in so you wouldn't have to write this code. Hopefully he will clarify, this is what happens to one's memory as one gets older... :(

              I see I added

              namespace QtExtra
              {
              // extra `Qt::ItemDataRole` value for aligning checkboxes in tables
              // see `UIUtils::CheckAlignmentProxyStyle`
              enum { CheckAlignmentRole = Qt::UserRole + Qt::CheckStateRole + Qt::TextAlignmentRole };
              }
              

              I cannot recall, does this give you enough to go on?

              P.S. So if I recall/understand this correctly, I'm not using createEditor() etc. at all. Rather, I'm just using flags() to include Qt.ItemIsUserCheckable. User can check box without going into edit mode. So there is only one checkbox, no overlaying, I never create any QCheckbox, the value is stored in Qt.CheckStateRole. I think it is much simpler than your way.

              H Offline
              H Offline
              hachbani
              wrote on last edited by
              #6

              @JonB Thanks for your help ! It seems that the snippet you gave me does handle the alignement of the checkboxs, right ?
              If this is what it is, I don't think I'm there yet, as I'm still trying to create Editable (with single click event) Checkboxs.

              I think I'm going to give up the Delegate method, and see If I can get the CheckStateRole method to work.

              JonBJ 1 Reply Last reply
              0
              • H hachbani

                @JonB Thanks for your help ! It seems that the snippet you gave me does handle the alignement of the checkboxs, right ?
                If this is what it is, I don't think I'm there yet, as I'm still trying to create Editable (with single click event) Checkboxs.

                I think I'm going to give up the Delegate method, and see If I can get the CheckStateRole method to work.

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

                @hachbani
                Yes, like I wrote at the end, this way you never create any QCheckbox yourself, and you don't go into editable mode with createEditor() etc.

                The code is only to do with getting the checkbox centered. Just start out with using CheckStateRole to store the state and ItemIsUserCheckable in the flags. This allows the user to check the box without anything further.

                1 Reply Last reply
                0

                • Login

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