【问题标题】:Virtual column in QTableView?QTableView中的虚拟列?
【发布时间】:2012-10-24 18:27:36
【问题描述】:

我开始学习 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 |
+---+---+-----+-----+

或者我应该以其他方式来做?

【问题讨论】:

    标签: python qt pyqt pyside


    【解决方案1】:

    是的,你可以这样做。虽然@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)
    

    【讨论】:

      【解决方案2】:

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

      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

      【讨论】:

      • 我正在尝试采用您的示例中的模型。但我对rowCount 有疑问。它返回 0 因为空的self.itemsset_items 没有被执行,我也没有在文档中看到这个函数。我错过了什么?
      • 你没有错过任何东西,这有点怪我。 set_items 不是“官方”功能,它只是我使用的东西,因为在我的代码中,我在加载模型后多次更改了数据。我已经编辑了直接从数据库加载数据的答案,但是我已经有一段时间没有做过数据库的事情了,我还没有测试过任何东西,所以它可能不起作用。
      【解决方案3】:

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

      【讨论】:

      • 是的,我正在阅读有关 QSqlQueryModel 的信息,但我还需要对数据库的写访问权限。所以我从 QSqlTableModel 开始。但是感谢这个例子!
      猜你喜欢
      • 1970-01-01
      • 2010-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-09
      • 2019-09-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多