2012-10-24 14 views
5

Ho iniziato ad imparare Qt4 Model/View Programming e ho una domanda per principianti.Colonna virtuale in QTableView?

devo semplice applicazione che mostrano tabella sqlite in 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_()) 

Ecco come banca dati si presenta come:

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"); 

Così sto ottenendo qualcosa di simile:

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

È possibile modificare Model da avere in QTableView qualcosa come la colonna virtuale? Ad esempio qualcosa come:

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

O forse dovrei farlo in qualche altro modo?

risposta

4

Sì, lo puoi fare. Anche se lo @BrtH's answer è pertinente, i modelli sono complicati ed è facile perdersi. Quindi ho pensato che un esempio più puntuale sarebbe stato meglio.

Personalmente, vorrei utilizzare un modello proxy derivato da QAbstractProxyModel. Ma, nel tuo caso, è anche possibile reimplementare QSqlTableModel. Di seguito è una implementazione per il tuo obiettivo. Tieni presente che è essenziale conoscere le nozioni di base di Model/View methodology in modo da capire cosa fa ciascun metodo.

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

Grazie per questo esempio. Funziona bene! – Adam

3

È sicuramente possibile. Di seguito è riportato un esempio di un mio codice personale, modificato per i tuoi dati.

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

Questo è un modello per un QTreeView, ma penso che si possa facilmente adattarlo.
Per ulteriori informazioni su questi metodi e sul modello, vedere http://srinikom.github.com/pyside-docs/PySide/QtCore/QAbstractItemModel.html.

+0

Sto provando ad adottare Modello come nel tuo esempio. Ma ho problemi con 'rowCount'. Restituisce 0 a causa di 'self.items 'vuoti. 'set_items' non viene eseguito, e non vedo questa funzione nella documentazione. Cosa mi manca? – Adam

+0

Non ti manca niente, è un po 'colpa mia. 'set_items' non è una funzione 'ufficiale', è solo una cosa che ho usato perché nel mio codice ho cambiato i dati alcune volte dopo il caricamento del modello. Ho modificato la risposta per caricare i dati direttamente da un database, ma è passato un po 'di tempo da quando ho fatto le cose sui database e non ho provato nulla, quindi potrebbe non funzionare. – BrtH

+0

Grazie mille. – Adam

1

Hai guardato QSqlQueryModel? Permette di mostrare i risultati di qualsiasi query SQL. Codice per il tuo esempio:

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") 

Ma prendere in considerazione:

Il modello è di sola lettura per impostazione predefinita. Per renderlo in lettura-scrittura, devi eseguirne la sottoclasse e reimplementare setData() e flags().

+0

Sì, stavo leggendo su QSqlQueryModel ma ho bisogno anche di accedere in scrittura al database. Così ho iniziato con QSqlTableModel. Ma grazie per questo esempio! – Adam