import sys
from untitled import Ui_Form
from PyQt5.QtWidgets import QApplication, QWidget, QStyleOptionButton, QAbstractItemDelegate, QStyle, QCheckBox, QStyledItemDelegate, QStyleOptionViewItem, QItemDelegate
from PyQt5.QtCore import Qt, QAbstractTableModel, QModelIndex, QVariant, QThread, pyqtSignal, QEvent, QRect
from PyQt5.QtGui import QColor, QMouseEvent
class WorkThread(QThread):
scrollBottomSignal = pyqtSignal()
addDataSignal = pyqtSignal()
def __init__(self, model):
super(WorkThread, self).__init__()
self.model = model
self.run_flag = True
def run(self):
while self.run_flag:
self.addDataSignal.emit()
self.scrollBottomSignal.emit()
self.usleep(1) # 不加延遲界面會卡頓。
def stop(self):
self.run_flag = False
class MyTableModel(QAbstractTableModel):
def __init__(self):
super(MyTableModel, self).__init__()
self._data = [] # 要顯示的數據
self._headers = ['選項', '姓名', '年齡', '性別'] # 表頭
def rowCount(self, parent=QModelIndex()):
"""
返回行數量。
"""
return len(self._data)
def columnCount(self, parent=QModelIndex()):
"""
返回列數量。
"""
return len(self._headers)
def insertRows(self, row, count, parent):
"""
插入行。
:param row: 插入起始行。
:param count: 插入行數量。
:param parent:
:return:
"""
self.beginInsertRows(QModelIndex(), row, row + count - 1)
for i in range(count):
self._data.insert(row, [Qt.Checked, 'CZ', '25', '男'])
self.endInsertRows()
return True
def removeRows(self, row, count, parent):
self.beginRemoveRows(QModelIndex(), 0, row + count - 1)
for i in range(count):
self._data.pop(row + count - 1 - i) # 倒着刪
self.endRemoveRows()
def clearView(self):
self.removeRows(0, self.rowCount(), QModelIndex())
def addData(self):
self.insertRows(self.rowCount(), 1, QModelIndex())
def headerData(self, section, orientation, role):
"""
設置表頭。
"""
if role == Qt.DisplayRole and orientation == Qt.Horizontal: # 限定只更改行表頭
return self._headers[section]
def data(self, index, role=Qt.DisplayRole):
if not index.isValid() or not 0 <= index.row() < self.rowCount():
return QVariant()
row = index.row()
col = index.column()
if role == Qt.DisplayRole:
if col != 0:
return str(self._data[row][col]) # 數據
elif role == Qt.TextColorRole:
return QColor(Qt.red)
elif Qt.UserRole:
if col == 0:
return self._data[row][col]
return QVariant()
def setData(self, index, value, role):
if not index.isValid() or not 0 <= index.row() < self.rowCount():
return False
col = index.column()
row = index.row()
if role == Qt.UserRole or role == Qt.CheckStateRole:
if col == 0:
self._data[row][col] = value
self.layoutChanged.emit()
return True
return False
def flags(self, index):
flags = Qt.ItemFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
return flags
class MyDelegate(QStyledItemDelegate):
def __init__(self):
super(MyDelegate, self).__init__()
def paint(self, painter, option, index):
if index.column() == 0:
data = index.model().data(index, Qt.UserRole)
ckbtn = QStyleOptionButton()
ckbtn.state = QStyle.State_On if data == Qt.Checked else QStyle.State_Off
ckbtn.state |= QStyle.State_Enabled
ckbtn.rect = option.rect
QApplication.style().drawControl(QStyle.CE_CheckBox, ckbtn, painter)
else:
QStyledItemDelegate.paint(self, painter, option, index)
def editorEvent(self, event, model, option, index):
if event.type() == QEvent.KeyPress:
return False
if event.type() == QEvent.MouseButtonRelease:
event = QMouseEvent(event) # 類型強轉
if event.button() == Qt.LeftButton:
value = index.data(Qt.UserRole)
if value == Qt.Checked:
rev_value = Qt.Unchecked
else:
rev_value = Qt.Checked
return model.setData(index, rev_value, Qt.UserRole)
else:
return False
else:
return False
class MainUI(QWidget, Ui_Form):
def __init__(self):
super(MainUI, self).__init__()
self.setupUi(self)
self.workThread = None
self.pushButton.clicked.connect(self.buttonClickedStart)
self.pushButton_2.clicked.connect(self.buttonClickedStop)
self.pushButton_3.clicked.connect(self.buttonClickedClear)
self.model = MyTableModel()
self.myDelegate = MyDelegate()
self.tableView.setItemDelegate(self.myDelegate)
self.tableView.setModel(self.model)
self.tableView.show()
def buttonClickedStart(self):
"""開啟線程,向表中插入數據。"""
self.workThread = WorkThread(self.model)
self.workThread.addDataSignal.connect(self.model.addData)
self.workThread.scrollBottomSignal.connect(self.scrollBottom)
self.workThread.start()
def buttonClickedStop(self):
"""停止線程向表中插入數據。"""
self.workThread.stop()
def buttonClickedClear(self):
"""清空表。"""
self.model.clearView()
def scrollBottom(self):
"""右側滑動條保持在最下面。"""
self.tableView.scrollToBottom()
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = MainUI()
ui.show()
sys.exit(app.exec_())
程序運行界面圖:

