Виртуальный столбец в 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 ().