PyQt:使用QAbstractTableModel向QTableView添加行
我刚开始学习Qt编程,想做一个简单的表格,可以通过点击按钮来添加行。我能把表格做出来,但就是无法让更新的数据在表格上显示出来。我觉得我的问题可能是因为我不知道怎么正确地通过按钮调用“更改数据”的方法。我在网上试过好几种不同的解决方案,但都发现是四年前的老帖子,没什么用。我现在有的只是基本结构,就是不知道怎么让表格更新显示新数据。
这是基本的界面
我已经设置了一些测试数据。
在最终的实现中,表格一开始是空的,我想添加行,并让它们在表格视图中显示出来。
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MyWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
# create table
self.get_table_data()
self.table = self.createTable()
# layout
self.layout = QVBoxLayout()
self.testButton = QPushButton("test")
self.connect(self.testButton, SIGNAL("released()"), self.test)
self.layout.addWidget(self.testButton)
self.layout.addWidget(self.table)
self.setLayout(self.layout)
def get_table_data(self):
self.tabledata = [[1234567890,2,3,4,5],
[6,7,8,9,10],
[11,12,13,14,15],
[16,17,18,19,20]]
def createTable(self):
# create the view
tv = QTableView()
# set the table model
header = ['col_0', 'col_1', 'col_2', 'col_3', 'col_4']
tablemodel = MyTableModel(self.tabledata, header, self)
tv.setModel(tablemodel)
# set the minimum size
tv.setMinimumSize(400, 300)
# hide grid
tv.setShowGrid(False)
# hide vertical header
vh = tv.verticalHeader()
vh.setVisible(False)
# set horizontal header properties
hh = tv.horizontalHeader()
hh.setStretchLastSection(True)
# set column width to fit contents
tv.resizeColumnsToContents()
# set row height
tv.resizeRowsToContents()
# enable sorting
tv.setSortingEnabled(False)
return tv
def test(self):
self.tabledata.append([1,1,1,1,1])
self.emit(SIGNAL('dataChanged()'))
print 'success'
class MyTableModel(QAbstractTableModel):
def __init__(self, datain, headerdata, parent=None):
"""
Args:
datain: a list of lists\n
headerdata: a list of strings
"""
QAbstractTableModel.__init__(self, parent)
self.arraydata = datain
self.headerdata = headerdata
def rowCount(self, parent):
return len(self.arraydata)
def columnCount(self, parent):
if len(self.arraydata) > 0:
return len(self.arraydata[0])
return 0
def data(self, index, role):
if not index.isValid():
return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
return QVariant(self.arraydata[index.row()][index.column()])
def setData(self, index, value, role):
pass # not sure what to put here
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return QVariant(self.headerdata[col])
return QVariant()
def sort(self, Ncol, order):
"""
Sort table by given column number.
"""
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self.arraydata = sorted(self.arraydata, key=operator.itemgetter(Ncol))
if order == Qt.DescendingOrder:
self.arraydata.reverse()
self.emit(SIGNAL("layoutChanged()"))
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
3 个回答
1
我把你的表格改成了类变量,而不是实例变量,这样你就可以在代码的任何地方修改表格的数据。
# First access the data of the table
self.tv_model = self.tv.model()
其次,我使用了一种类似于处理pandas数据框的方法。假设你想添加的数据存储在一个单独的变量里:
# These can be whatever, but for consistency,
# I used the data in the OP's example
new_values = [1, 1, 1, 1, 1]
接下来的步骤可以有不同的处理方式,这取决于你是要往表格里添加新数据,还是更新已有的数据。添加新数据作为一行的方式如下。
# The headers should also be a class variable,
# but I left it as the OP had it
header = ['col_0', 'col_1', 'col_2', 'col_3', 'col_4']
# There are multiple ways of establishing what the row reference should be,
# this is just one example how to add a new row
new_row = len(self.tv_model.dataFrame.index)
for i, col in enumerate(header):
self.tv_model.dataFrame.loc[new_row, col] = new_values[i]
因为self.tv_model是指向表格实际数据的引用,所以发出以下信号会更新数据,或者说是把它“提交”到模型中。
self.tv_model.layoutChanged.emit()
2
QAbstractTableModel
有两个特别的方法可以用来处理这个问题,分别是 beginInsertRows() 和 endInsertRows()。
你可以在自定义模型中添加 API 接口。比如:
def insertGuest(self, guest):
self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
self.guestsTableData.append(guest)
self.endInsertRows()
14
当模型的基础数据发生变化时,模型应该发出 layoutChanged 或 layoutAboutToBeChanged 的信号,这样视图才能正确更新(如果你想更新特定范围的单元格,还有 dataChanged 这个信号)。
所以你只需要像这样做:
def test(self):
self.tabledata.append([1,1,1,1,1])
self.table.model().layoutChanged.emit()
print 'success'