Виртуальный столбец в QTableView?
Я начал изучать Qt4 Model/View Programming, и у меня есть вопрос для начинающих.
у меня есть простое приложение, которое показывает таблицу sqlite в QTableView
:
class Model(QtSql.QSqlTableModel):
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
self.setTable("test")
self.select()
class App(QtGui.QMainWindow):
def __init__(self, model):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.tableView.setModel(model)
if __name__ == "__main__":
myDb = QtSql.QSqlDatabase.addDatabase("QSQLITE")
myDb.setDatabaseName("test.db")
if not myDb.open():
print 'FIXME'
model = Model()
app = QtGui.QApplication(sys.argv)
window = App(model)
window.show()
sys.exit(app.exec_())
вот как выглядит база данных:
sqlite> create table test (a INTEGER, b INTEGER, c STRING);
sqlite> insert into test VALUES(1, 2, "xxx");
sqlite> insert into test VALUES(6, 7, "yyy");
так что я получаю что-то вроде:
+---+---+-----+
| a | b | c |
+---+---+-----+
| 1 | 2 | xxx |
+---+---+-----+
| 6 | 7 | yyy |
+---+---+-----+
можно ли изменить Model
у в QTableView
что-то вроде виртуального столбца? Например что-то вроде:
+---+---+-----+-----+
| a | b | sum | c |
+---+---+-----+-----+
| 1 | 2 | 3 | xxx |
+---+---+-----+-----+
| 6 | 7 | 13 | yyy |
+---+---+-----+-----+
или, может быть, я должен сделать это в какой-то другой способ?
3 ответов
Да, вы можете сделать это. Хотя @BrtH это!--5--> актуально, модели сложны, и легко заблудиться. Поэтому я подумал, что более показательный пример будет лучше.
лично я бы использовал прокси-модель, полученную из QAbstractProxyModel
. Но, в вашем случае реорганизации QSqlTableModel
также возможно. Ниже приведена реализация для вашей цели. Обратите внимание, что для вас важно знать основы модель/методология просмотра так что вы понимаете, что каждый способ делает.
class Model(QtSql.QSqlTableModel):
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
self.setTable("test")
self.select()
def columnCount(self, parent=QtCore.QModelIndex()):
# this is probably obvious
# since we are adding a virtual column, we need one more column
return super(Model, self).columnCount()+1
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole and index.column()==2:
# 2nd column is our virtual column.
# if we are there, we need to calculate and return the value
# we take the first two columns, get the data, turn it to integer and sum them
# [0] at the end is necessary because pyqt returns value and a bool
# http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qvariant.html#toInt
return sum(self.data(self.index(index.row(), i)).toInt()[0] for i in range(2))
if index.column() > 2:
# if we are past 2nd column, we need to shift it to left by one
# to get the real value
index = self.index(index.row(), index.column()-1)
# get the value from base implementation
return super(Model, self).data(index, role)
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
# this is similar to `data`
if section==2 and orientation==QtCore.Qt.Horizontal and role==QtCore.Qt.DisplayRole:
return 'Sum'
if section > 2 and orientation==QtCore.Qt.Horizontal:
section -= 1
return super(Model, self).headerData(section, orientation, role)
def flags(self, index):
# since 2nd column is virtual, it doesn't make sense for it to be Editable
# other columns can be Editable (default for QSqlTableModel)
if index.column()==2:
return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable
def setData(self, index, data, role):
# similar to data.
# we need to be careful when setting data (after edit)
# if column is after 2, it is actually the column before that
if index.column() > 2:
index = self.index(index.row(), index.column()-1)
return super(Model, self).setData(index, data, role)
Это, безусловно, возможно. Ниже приведен пример моего собственного кода, измененного для ваших данных.
import sqlite3
conn = sqlite3.connect('test.db')
class MyTreeModel(QAbstractItemModel):
def __init__(self, parent=None):
super(MyTreeModel, self).__init__(parent)
c = conn.cursor()
c.execute("SELECT a, b, c FROM 'test'")
self.items = c.fetchall()
self.columns = ['a', 'b', 'sum', 'c']
def columnCount(self, index):
return len(self.columns)
def rowCount(self, parent):
return len(self.items)
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
col= index.column()
spot = self.items[index.row()]
if role == Qt.DisplayRole:
if col == 0 or col == 1:
return self.items[index.row()][col]
elif col == 2:
return self.items[index.row()][0] + self.items[index.row()][1]
elif col == 3:
return self.items[index.row()][2]
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.columns[section]
def index(self, row, col, index):
if not index.isValid():
return self.createIndex(row, col)
else:
return QModelIndex()
def parent(self, child):
return QModelIndex()
Это модель для QTreeView, но я думаю, что вы можете легко адаптировать ее.
Дополнительные сведения об этих методах и модели см. В разделе http://srinikom.github.com/pyside-docs/PySide/QtCore/QAbstractItemModel.html.
вы смотрели QSqlQueryModel? Это позволяет показать результаты SQL-запроса. Код для вашего примера:
class Model(QtSql.QSqlQueryModel):
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self.setQuery("SELECT a, b, a + b, c FROM test")
self.setHeaderData(0, QtCore.Qt.Horizontal, "a")
self.setHeaderData(1, QtCore.Qt.Horizontal, "b")
self.setHeaderData(2, QtCore.Qt.Horizontal, "sum")
self.setHeaderData(3, QtCore.Qt.Horizontal, "c")
но имейте в виду:
по умолчанию модель доступна только для чтения. Чтобы сделать его чтение-запись, вы должны подкласс его и reimplement setData () и flags ().