PyQt5 讓 QTableView 顯示置中的 CheckBox

讓 QTableView 顯示置中的 CheckBox

讓 Checkbox 置中

TableVeiw 預設的 checkbox 沒辦法置中,解決方法有兩類

  1. 使用 Delegate
  2. 透過 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_())