Reputation: 552
I am trying to put a checkbox in the horizontal (column) header of my tablewidget. Based on the other post here (because the base object type is the same), I have tried this:
item = QtGui.QTableWidgetItem()
item.setCheckState(QtCore.Qt.Checked)
self.tableWidget.setHorizontalHeaderItem(1, item)
and I have also tried this:
self.tableWidget.horizontalHeaderItem(1).setCheckState(QtCore.Qt.Checked)
Neither of these produce a checkbox in the horizontal header. Suggestions are appreciated.
Upvotes: 5
Views: 6591
Reputation: 38
Adapted from the qt-blog http://blog.qt.io/blog/2014/04/11/qt-weekly-5-widgets-on-a-qheaderview/
This can also be used to place arbitrary widgets into the header
# coding=utf-8
from PySide.QtCore import Qt
from PySide.QtGui import QHeaderView, QCheckBox
from qtpy import QtCore
class CustomHeaderView(QHeaderView):
def __init__(self, parent=None):
QHeaderView.__init__(self, Qt.Horizontal, parent)
self.setMovable(True)
self.boxes = []
self.sectionResized.connect(self.handleSectionResized)
self.sectionMoved.connect(self.handleSectionMoved)
def scrollContentsBy(self, dx, dy):
super().scrollContentsBy(dx, dy)
if dx != 0:
self.fixComboPositions()
def fixComboPositions(self):
for i in range(self.count() + 1):
self.boxes[i].setGeometry(self.sectionViewportPosition(i), 0,
self.sectionSize(i) - 5, self.height())
@QtCore.Slot()
def showEvent(self, e):
for i in range(self.count() + 1):
if len(self.boxes) <= i:
self.boxes.append(QCheckBox(self))
self.boxes[i].setGeometry(self.sectionViewportPosition(i), 0,
self.sectionSize(i) - 5, self.height())
self.boxes[i].show()
super().showEvent(e)
@QtCore.Slot()
def handleSectionMoved(self, logical, oldVisualIndex, newVisualIndex):
for i in range(min(oldVisualIndex, newVisualIndex),self.count()):
logical = self.logicalIndex(i)
self.boxes[logical].setGeometry(self.sectionViewportPosition(logical), 0,
self.sectionSize(logical) - 5, self.height())
@QtCore.Slot()
def handleSectionResized(self, i):
for j in range(self.visualIndex(i),self.count()):
logical = self.logicalIndex(j)
self.boxes[logical].setGeometry(self.sectionViewportPosition(logical), 0,
self.sectionSize(logical) - 5, self.height())
self.boxes[logical].updateGeometry()
Upvotes: 0
Reputation: 521
As a thank to Gary for his answer, here's a modified version of his checkable header, where all the sections are checkable separately
class QCheckableHeader(QHeaderView):
def __init__(self, orientation, parent=None):
QHeaderView.__init__(self, orientation, parent)
self.lisCheckboxes = []
self.sectionCountChanged.connect(self.onSectionCountChanged)
def paintSection(self, painter, rect, logicalIndex):
print "paintSection", logicalIndex
painter.save()
QHeaderView.paintSection(self, painter, rect, logicalIndex)
painter.restore()
painter.save()
painter.translate(rect.topLeft())
option = QStyleOptionButton()
option.rect = QRect(10, 10, 10, 10)
if (len(self.lisCheckboxes) != self.count()):
self.onSectionCountChanged(len(self.lisCheckboxes), self.count())
if self.lisCheckboxes[logicalIndex]:
option.state = QStyle.State_On
else:
option.state = QStyle.State_Off
self.style().drawControl(QStyle.CE_CheckBox, option, painter)
painter.restore()
def mousePressEvent(self, event):
iIdx = self.logicalIndexAt(event.pos())
self.lisCheckboxes[iIdx] = not self.lisCheckboxes[iIdx]
self.updateSection(iIdx)
QHeaderView.mousePressEvent(self, event)
@QtCore.Slot()
def onSectionCountChanged(self, oldCount, newCount):
if newCount > oldCount:
for i in range(newCount - oldCount):
self.lisCheckboxes.append(False)
else:
self.lisCheckboxes = self.lisCheckboxes[0:newCount]
Hope it helps someone else other than me too :-)
Upvotes: 3
Reputation: 552
I gave Gary Hughes the credit because his actually puts the checkbox into the header section itself, but I thought I would go ahead an post my simpler solution in case anyone wants to do it the easy way. This is based off the advice I got over at the Qt Developer forums:
I subclassed QTableWidget to make checkboxes child widgets of horizontalHeader(), and to manually reposition the checkboxes when table resizes:
class custom_table(QtGui.QTableWidget):
def __init__(self, parent=None):
QtGui.QTableWidget.__init__(self, parent)
self.chkbox1 = QtGui.QCheckBox(self.horizontalHeader())
def resizeEvent(self, event=None):
super().resizeEvent(event)
self.chkbox1.setGeometry(QtCore.QRect((self.columnWidth(0)/2), 2, 16, 17))
The "(self.columnWidth(0)/2)" keeps the checkbox in the middle of the column header.
Upvotes: 2
Reputation: 4510
It's not all that pretty, but a solution to this was posted in a FAQ on the qt-project.org site.
I've adapted the solution for Python and made some of the changes suggested in the comments.
from PyQt4.QtCore import Qt, QRect
from PyQt4.QtGui import QTableWidget, QApplication, QHeaderView, QStyleOptionButton, QStyle
import sys
class MyHeader(QHeaderView):
isOn = False
def __init__(self, orientation, parent=None):
QHeaderView.__init__(self, orientation, parent)
def paintSection(self, painter, rect, logicalIndex):
painter.save()
QHeaderView.paintSection(self, painter, rect, logicalIndex)
painter.restore()
if logicalIndex == 0:
option = QStyleOptionButton()
option.rect = QRect(10, 10, 10, 10)
if self.isOn:
option.state = QStyle.State_On
else:
option.state = QStyle.State_Off
self.style().drawControl(QStyle.CE_CheckBox, option, painter)
def mousePressEvent(self, event):
self.isOn = not self.isOn
self.updateSection(0)
QHeaderView.mousePressEvent(self, event)
class MyTable(QTableWidget):
def __init__(self):
QTableWidget.__init__(self, 3, 3)
myHeader = MyHeader(Qt.Horizontal, self)
self.setHorizontalHeader(myHeader)
if __name__ == '__main__':
app = QApplication(sys.argv)
myTable = MyTable()
myTable.show()
sys.exit(app.exec_())
Upvotes: 6