PyQt5 讓 QTableView 顯示置中的 CheckBox
讓 QTableView 顯示置中的 CheckBox
Categories:
讓 Checkbox 置中
TableVeiw 預設的 checkbox 沒辦法置中,解決方法有兩類
- 使用 Delegate
- 透過 setItemWidget 設 Layout,加 checkbox 元件
Example
import sys
from PyQt5.QtWidgets import QApplication, QTableView, QCheckBox, QVBoxLayout, QWidget, QHBoxLayout
from PyQt5.QtCore import Qt, QAbstractTableModel, QModelIndex
from PyQt5.QtGui import QStandardItemModel
class CustomizeCheckBox(QWidget):
def __init__(self, parent, ftor ):
super().__init__(parent)
l = QHBoxLayout(self)
self.cb = QCheckBox(self)
l.addWidget(self.cb)
l.setAlignment(Qt.AlignCenter)
# You must set margins, otherwise checkbox style is strange.
l.setContentsMargins(0, 0, 0, 0)
# m = l.contentsMargins()
# print(m.left(), m.right(), m.top(), m.bottom(), )
self.setLayout(l)
self.cb.stateChanged.connect(ftor)
def get_checkbox(self, ):
return self.cb
class CheckBoxTableModel(QStandardItemModel):
def __init__(self, rows, columns, parent=None):
super().__init__(rows, columns, parent)
self._rows = rows
self._columns = columns
self._data = [[False for _ in range(columns)] for _ in range(rows)]
def rowCount(self, parent=QModelIndex()):
return self._rows
def columnCount(self, parent=QModelIndex()):
return self._columns
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
if role == Qt.DisplayRole or role == Qt.EditRole:
return ""
if role == Qt.UserRole:
return self._data[index.row()][index.column()]
return None
def setData(self, index, value, role=Qt.EditRole):
'''
setData set value to customize data struct when role is checkStateRole,
avoid data Qt.CheckStateRole modified in itemData.
It should be None, otherwise, checkbox will be shown.
'''
if index.isValid() and role == Qt.CheckStateRole:
self._data[index.row()][index.column()] = value
data = {Qt.DisplayRole: "", Qt.EditRole:"", Qt.UserRole:value}
print(data)
self.setItemData(index, data)
self.dataChanged.emit(index, index, [Qt.CheckStateRole])
return True
return False
# def flags(self, index):
# return Qt.ItemIsEditable | Qt.ItemIsEnabled
class CheckBoxTableView(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.model.dataChanged.connect(self.on_item_changed)
def initUI(self):
layout = QVBoxLayout(self)
self.tableView = QTableView(self)
self.model = CheckBoxTableModel(3, 4)
self.tableView.setModel(self.model)
for row in range(3):
for column in range(4):
index = self.model.index(row, column)
# Method 1
ftor = self.getCheckBoxHandler(index)
w = CustomizeCheckBox(self, ftor)
self.tableView.setIndexWidget(index, w)
# Method 2: Add checkbox directly, it will not cause starange layout.
# cb = QCheckBox(self)
# self.tableView.setIndexWidget(index, cb)
layout.addWidget(self.tableView)
self.setLayout(layout)
self.setGeometry(100, 100, 400, 200)
self.setWindowTitle('QTableView with QCheckBox')
self.show()
def on_item_changed(self, item):
if type(item) == QModelIndex:
# val = self.model.data(item, Qt.CheckStateRole)
val = self.model.data(item, Qt.UserRole)
else:
val = self.model.data(item.index(), Qt.CheckStateRole)
print("Current data is,", val)
def getCheckBoxHandler(self, index):
def handler(state):
self.model.setData(index, state , Qt.CheckStateRole)
return handler
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = CheckBoxTableView()
sys.exit(app.exec_())