QTableView中的虚拟列?

时间:2012-10-24 18:27:36

标签: python qt pyqt pyside

我开始学习Qt4模型/视图编程,我有初学者的问题。

我有一个简单的应用程序,它在QTableView中显示了sqlite表:

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 个答案:

答案 0 :(得分:6)

是的,你可以这样做。虽然@BrtH's answer是相关的,但模型很棘手,很容易丢失。所以我认为更好的例子会更好。

就个人而言,我使用的是从QAbstractProxyModel派生的代理模型。但是,在您的情况下,重新实现QSqlTableModel也是可行的。以下是您的目标实现。请注意,您必须了解Model/View methodology的基础知识,以便了解每种方法的作用。

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)

答案 1 :(得分:3)

这当然是可能的。下面是我自己的一些代码示例,为您的数据进行了修改。

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

答案 2 :(得分:1)

你看过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")

但请记住:

  

默认情况下,模型是只读的。要使其成为读写,必须将其子类化并重新实现setData()和flags()。