2012-10-24 53 views
5

我開始學習Qt4模型/視圖編程,我有初學者問題。QTableView中的虛擬列?

我有簡單的應用程序,它顯示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 | 
+---+---+-----+ 

是否有可能修改ModelQTableView像虛擬列?比如像這樣的東西:

+---+---+-----+-----+ 
| a | b | sum | c | 
+---+---+-----+-----+ 
| 1 | 2 | 3 | xxx | 
+---+---+-----+-----+ 
| 6 | 7 | 13 | yyy | 
+---+---+-----+-----+ 

或者我應該以其他方式做?

回答

4

是的,你可以做到這一點。雖然@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) 
+0

謝謝你的這個例子。它工作正常! – Adam

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

+0

我試圖採用模型就像你的例子。但是我有'rowCount'的問題。它由於空的'self.items'而返回0。 'set_items'沒有執行,我也沒有在文檔中看到這個函數。我錯過了什麼? – Adam

+0

你不會錯過任何東西,這是我的錯。 'set_items'不是一個'官方'函數,它只是我使用的一些東西,因爲在我的代碼中,我在模型加載後更改了幾次數據。我已經編輯了答案,直接從數據庫中加載數據,但是因爲我已經完成了數據庫的工作,而且還沒有測試過任何東西,所以它可能無法工作。 – BrtH

+0

非常感謝。 – Adam

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()。

+0

是的,我正在閱讀有關QSqlQueryModel,但我也需要寫訪問數據庫。所以我開始使用QSqlTableModel。但是,感謝這個例子! – Adam