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. resizing columns in headers with checkboxes
Forum Updated to NodeBB v4.3 + New Features

resizing columns in headers with checkboxes

Scheduled Pinned Locked Moved Unsolved Qt for Python
3 Posts 2 Posters 69 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.
  • K Offline
    K Offline
    kevl
    wrote last edited by kevl
    #1

    (Edit): using PyQt6 on Python 3.10.5, Windows 11

    Hi I'm kinda new to qt development and I found some code to add a checkbox into the header of a QTableView but it's not working like how I want it to.

    The functionality of the checkbox works great, I just need it to look correct.

    Right now, after I paint the checkbox, it is covering the text of the header after I call resizeColumnsToContents() as shown below. I tried to reimplement the sectionSizeHint() and sectionSize() in my custom header so it adds space for the checkbox but those functions don't seem to be getting called.

    Is there any way to kind of "attach" the checkbox to the text so that it's being taken into account when the columns are resized? or is there any better way to add a checkbox into the header of a QTableView?

    Issue where checkbox is covering the header text.
    fbbe00fc-e5bb-42b3-88f9-fe85c007894f-image.png

    I want it to look something like this, without the checkbox covering the header text.
    4fa15672-4935-4b52-8ee0-f533ca075f2c-image.png

    Minimal example below.

    import sys
    from PyQt6.QtWidgets import QApplication, QTableView, QHeaderView, QStyleOptionButton, QWidget, QVBoxLayout, QStyle
    from PyQt6.QtCore import Qt, QRect, QModelIndex, QAbstractTableModel
    
    
    class QCheckableHeader(QHeaderView):
        def __init__(self, checkable, orientation, parent=None):
            super().__init__(orientation, parent)
            self.checkboxes = {k: False for k in checkable}
    
        def paintSection(self, painter, rect, logicalIndex):
            painter.save()
            super().paintSection(painter, rect, logicalIndex)
            painter.restore()
    
            painter.save()
            
            if logicalIndex in self.checkboxes:
                option = QStyleOptionButton()
                option.rect = QRect(5, 7, 10, 10)
                option.state = QStyle.StateFlag.State_Enabled
    
                if self.checkboxes[logicalIndex]:
                    option.state |= QStyle.StateFlag.State_On
                else:
                    option.state |= QStyle.StateFlag.State_Off
    
                self.style().drawControl(QStyle.ControlElement.CE_CheckBox, option, painter)
            painter.restore()
    
        def mousePressEvent(self, event):
            idx = self.logicalIndexAt(event.pos())
    
            if idx >= 0 and idx in self.checkboxes:  # Ensure the index is valid
                self.checkboxes[idx] = not self.checkboxes[idx]
                self.updateSection(idx)
    
            super().mousePressEvent(event)
    
        def sectionSizeHint(self, logicalIndex):
            if logicalIndex in self.checkboxes:
                return super().sectionSizeHint(logicalIndex) + 20
            else:
                return super().sectionSizeHint(logicalIndex)
            
        def sectionSize(self, logicalIndex):
            if logicalIndex in self.checkboxes:
                return super().sectionSizeHint(logicalIndex) + 20
            else:
                return super().sectionSizeHint(logicalIndex)
            
    class CustomTableModel(QAbstractTableModel):
        def __init__(self, data):
            super().__init__()
            self._data = data
    
        def data(self, index: QModelIndex, role=Qt.ItemDataRole.DisplayRole):
            if role == Qt.ItemDataRole.DisplayRole:
                return self._data[index.row()][index.column()]
            return None
    
        def rowCount(self, index):
            return len(self._data)
    
        def columnCount(self, index):
            return len(self._data[0]) if self._data else 0
        
        def headerData(self, section, orientation, role):
            if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
                return f"Column {section + 1}"
            
            return None
         
    class MainWindow(QWidget):
        def __init__(self):
            super().__init__()
            self.setGeometry(40, 80, 300, 200)
            layout = QVBoxLayout(self)
            
            data = [['Row1-Col1', 'Row1-Col2', 'Row1-Col3', 'Row1-Col4'], ['Row2-Col1', 'Row2-Col2', 'Row2-Col3', 'Row2-Col4']]
            self.model = CustomTableModel(data)
            
            self.table_view = QTableView()
            self.table_view.setModel(self.model)
    
            header = QCheckableHeader([0], Qt.Orientation.Horizontal)
            self.table_view.setHorizontalHeader(header)
    
            layout.addWidget(self.table_view)
            self.setLayout(layout)
    
            self.table_view.resizeColumnsToContents()
            self.table_view.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch)
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
    
        window = MainWindow()
        window.show()
    
        sys.exit(app.exec())
    
    JonBJ 1 Reply Last reply
    0
    • K kevl

      (Edit): using PyQt6 on Python 3.10.5, Windows 11

      Hi I'm kinda new to qt development and I found some code to add a checkbox into the header of a QTableView but it's not working like how I want it to.

      The functionality of the checkbox works great, I just need it to look correct.

      Right now, after I paint the checkbox, it is covering the text of the header after I call resizeColumnsToContents() as shown below. I tried to reimplement the sectionSizeHint() and sectionSize() in my custom header so it adds space for the checkbox but those functions don't seem to be getting called.

      Is there any way to kind of "attach" the checkbox to the text so that it's being taken into account when the columns are resized? or is there any better way to add a checkbox into the header of a QTableView?

      Issue where checkbox is covering the header text.
      fbbe00fc-e5bb-42b3-88f9-fe85c007894f-image.png

      I want it to look something like this, without the checkbox covering the header text.
      4fa15672-4935-4b52-8ee0-f533ca075f2c-image.png

      Minimal example below.

      import sys
      from PyQt6.QtWidgets import QApplication, QTableView, QHeaderView, QStyleOptionButton, QWidget, QVBoxLayout, QStyle
      from PyQt6.QtCore import Qt, QRect, QModelIndex, QAbstractTableModel
      
      
      class QCheckableHeader(QHeaderView):
          def __init__(self, checkable, orientation, parent=None):
              super().__init__(orientation, parent)
              self.checkboxes = {k: False for k in checkable}
      
          def paintSection(self, painter, rect, logicalIndex):
              painter.save()
              super().paintSection(painter, rect, logicalIndex)
              painter.restore()
      
              painter.save()
              
              if logicalIndex in self.checkboxes:
                  option = QStyleOptionButton()
                  option.rect = QRect(5, 7, 10, 10)
                  option.state = QStyle.StateFlag.State_Enabled
      
                  if self.checkboxes[logicalIndex]:
                      option.state |= QStyle.StateFlag.State_On
                  else:
                      option.state |= QStyle.StateFlag.State_Off
      
                  self.style().drawControl(QStyle.ControlElement.CE_CheckBox, option, painter)
              painter.restore()
      
          def mousePressEvent(self, event):
              idx = self.logicalIndexAt(event.pos())
      
              if idx >= 0 and idx in self.checkboxes:  # Ensure the index is valid
                  self.checkboxes[idx] = not self.checkboxes[idx]
                  self.updateSection(idx)
      
              super().mousePressEvent(event)
      
          def sectionSizeHint(self, logicalIndex):
              if logicalIndex in self.checkboxes:
                  return super().sectionSizeHint(logicalIndex) + 20
              else:
                  return super().sectionSizeHint(logicalIndex)
              
          def sectionSize(self, logicalIndex):
              if logicalIndex in self.checkboxes:
                  return super().sectionSizeHint(logicalIndex) + 20
              else:
                  return super().sectionSizeHint(logicalIndex)
              
      class CustomTableModel(QAbstractTableModel):
          def __init__(self, data):
              super().__init__()
              self._data = data
      
          def data(self, index: QModelIndex, role=Qt.ItemDataRole.DisplayRole):
              if role == Qt.ItemDataRole.DisplayRole:
                  return self._data[index.row()][index.column()]
              return None
      
          def rowCount(self, index):
              return len(self._data)
      
          def columnCount(self, index):
              return len(self._data[0]) if self._data else 0
          
          def headerData(self, section, orientation, role):
              if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
                  return f"Column {section + 1}"
              
              return None
           
      class MainWindow(QWidget):
          def __init__(self):
              super().__init__()
              self.setGeometry(40, 80, 300, 200)
              layout = QVBoxLayout(self)
              
              data = [['Row1-Col1', 'Row1-Col2', 'Row1-Col3', 'Row1-Col4'], ['Row2-Col1', 'Row2-Col2', 'Row2-Col3', 'Row2-Col4']]
              self.model = CustomTableModel(data)
              
              self.table_view = QTableView()
              self.table_view.setModel(self.model)
      
              header = QCheckableHeader([0], Qt.Orientation.Horizontal)
              self.table_view.setHorizontalHeader(header)
      
              layout.addWidget(self.table_view)
              self.setLayout(layout)
      
              self.table_view.resizeColumnsToContents()
              self.table_view.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch)
      
      if __name__ == "__main__":
          app = QApplication(sys.argv)
      
          window = MainWindow()
          window.show()
      
          sys.exit(app.exec())
      
      JonBJ Online
      JonBJ Online
      JonB
      wrote last edited by JonB
      #2

      @kevl
      I have no idea whether this is the way it is supposed to be done, but given your approach:

      super().paintSection(painter, rect, logicalIndex)
      

      For this call, to paint the header text, don't you want to subtract out (i.e. increase x, decrease width) of the rect you pass on the area which will be allocated for a checkbox if you're going to show one?

      K 1 Reply Last reply
      0
      • JonBJ JonB

        @kevl
        I have no idea whether this is the way it is supposed to be done, but given your approach:

        super().paintSection(painter, rect, logicalIndex)
        

        For this call, to paint the header text, don't you want to subtract out (i.e. increase x, decrease width) of the rect you pass on the area which will be allocated for a checkbox if you're going to show one?

        K Offline
        K Offline
        kevl
        wrote last edited by kevl
        #3

        @JonB

        Thanks for the suggestion. That kinda seems to work and the checkbox is no longer overlapped with the text, but the resizeColumnstoContents() still seems to be taking into account of only the size of the text and I'm not sure how to recalculate that as I dont see sectionSizeHint() or sectionSize() being called.

        I added this right before the super() call:

                if logicalIndex in self.checkboxes:
                    rect.adjust(23, 0, 0, 0)
        

        b2d062e0-fb28-458d-ab5a-d9fb52dfcabd-image.png

        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