2013-07-17 11 views
12

Vorrei visualizzare un frame di dati panda in una tabella PyQt. Ho fatto qualche progresso con questo, ma non sono stato in grado di derivare correttamente la classe del modello di tabella. Qualsiasi aiuto con questo sarebbe molto apprezzato.PyQt - Implementare un QAbstractTableModel per la visualizzazione in QTableView

** Nota esempio completo di codice here **

Ho difficoltà a generare una QtCore.QAbstractTableModel valida classe derivata. Seguendo una domanda precedente su QItemDelegates, sto provando a generare un modello di tabella da un DataFrame di Pandas per inserire dati reali. Ho un codice di esempio operativo here, ma se sostituisco il mio TableModel con TableModel2 nella classe Widget (ln 152) non riesco a visualizzare la tabella.

class TableModel2(QtCore.QAbstractTableModel): 
    def __init__(self, parent=None, *args): 
     super(TableModel2, self).__init__() 
     #QtCore.QAbstractTableModel.__init__(self, parent, *args) 
     self.datatable = None 
     self.headerdata = None 
     self.dataFrame = None 
     self.model = QtGui.QStandardItemModel(self) 

    def update(self, dataIn): 
     print 'Updating Model' 
     self.datatable = dataIn 
     print 'Datatable : {0}'.format(self.datatable) 
     headers = dataIn.columns.values 
     header_items = [ 
        str(field) 
        for field in headers 
     ] 
     self.headerdata = header_items 
     print 'Headers' 
     print self.headerdata 

     for i in range(len(dataIn.index.values)): 
      for j in range(len(dataIn.columns.values)): 
       #self.datatable.setItem(i,j,QtGui.QTableWidgetItem(str(df.iget_value(i, j)))) 
       self.model.setItem(i,j,QtGui.QStandardItem(str(dataIn.iget_value(i, j)))) 

    def rowCount(self, parent=QtCore.QModelIndex()): 
     return len(self.datatable.index) 

    def columnCount(self, parent=QtCore.QModelIndex()): 
     return len(self.datatable.columns.values) 

    def data(self, index, role=QtCore.Qt.DisplayRole): 
     if not index.isValid(): 
      return QtCore.QVariant() 
     elif role != QtCore.Qt.DisplayRole: 
      return QtCore.QVariant() 
     #return QtCore.QVariant(self.model.data(index)) 
      return QtCore.QVariant(self.model.data(index)) 

    def headerData(self, col, orientation, role): 
     if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: 
      return QtCore.QVariant() 
     return QtCore.QVariant(self.headerdata[col]) 

    def setData(self, index, value, role=QtCore.Qt.DisplayRole): 
     print "setData", index.row(), index.column(), value 

    def flags(self, index): 
     if (index.column() == 0): 
      return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled 
     else: 
      return QtCore.Qt.ItemIsEnabled 

Sto tentando di creare il modello e quindi aggiungerlo alla vista, in questo modo:

class Widget(QtGui.QWidget): 
    """ 
    A simple test widget to contain and own the model and table. 
    """ 
    def __init__(self, parent=None): 
     QtGui.QWidget.__init__(self, parent) 

     l=QtGui.QVBoxLayout(self) 
     cdf = self.get_data_frame() 
     self._tm=TableModel(self) 
     self._tm.update(cdf) 
     self._tv=TableView(self) 
     self._tv.setModel(self._tm) 
     for row in range(0, self._tm.rowCount()): 
      self._tv.openPersistentEditor(self._tm.index(row, 0)) 
     l.addWidget(self._tv) 

    def get_data_frame(self): 
     df = pd.DataFrame({'Name':['a','b','c','d'], 
     'First':[2.3,5.4,3.1,7.7], 'Last':[23.4,11.2,65.3,88.8], 'Class':[1,1,2,1], 'Valid':[True, True, True, False]}) 
     return df 

Grazie per l'attenzione!

Nota: Modifica 2 Ho incorporato QStandardItemModel in TableModel2. Anche cancellata la funzione dataFrameToQtTable dopo il commento di @ mata. Questo si sta avvicinando, ma non funziona ancora.

+0

Panda è un esempio di tabella di Qt nella sua repo . https://github.com/pydata/pandas/commits/master/pandas/sandbox/qtpandas.py –

risposta

10

Ok ho capito questo con il suggerimento di cui sopra e un po 'di aiuto dal libro della GUI di Summerfield. Non esiste un modello sottostante esistente in QAbstractTableModel. Solo tre funzioni devono essere ignorate e i dati possono essere archiviati in qualsiasi formato definito dall'utente, purché venga restituito nella chiamata dati.

un'implementazione molto semplice potrebbe essere:

class TableModel(QtCore.QAbstractTableModel): 
    def __init__(self, parent=None, *args): 
     super(TableModel, self).__init__() 
     self.datatable = None 

    def update(self, dataIn): 
     print 'Updating Model' 
     self.datatable = dataIn 
     print 'Datatable : {0}'.format(self.datatable) 

    def rowCount(self, parent=QtCore.QModelIndex()): 
     return len(self.datatable.index) 

    def columnCount(self, parent=QtCore.QModelIndex()): 
     return len(self.datatable.columns.values) 

    def data(self, index, role=QtCore.Qt.DisplayRole): 
     if role == QtCore.Qt.DisplayRole: 
      i = index.row() 
      j = index.column() 
      return '{0}'.format(self.datatable.iget_value(i, j)) 
     else: 
      return QtCore.QVariant() 

    def flags(self, index): 
     return QtCore.Qt.ItemIsEnabled 

Ciò consente di visualizzare qualsiasi frame di dati compatibile in una vista Qt.

Ho aggiornato il Gist sopra here

Questo dovrebbe farti andare rapidamente se anche avete bisogno di fare questo.

+0

Sfortunatamente i nomi delle colonne e degli indici non vengono visualizzati? Sarà bello se tu possa risolvere il problema. – working4coins

1

Questo è probabilmente il problema:

def rowCount(self, parent=QtCore.QModelIndex()): 
    if type(self.datatable) == pd.DataFrame: 
    ... 


def columnCount(self, parent=QtCore.QModelIndex()): 
    if (self.datatable) == pd.DataFrame: 
    ... 

Si imposta il datatable ad un QTableWidget in dataFrameToQtTable, quindi non può essere un pd.DataFrame, i metodi saranno sempre restituiscono 0.

Senza il tipo controlla, avresti colto immediatamente il problema. Vuoi veramente ignorare silenziosamente tutti i casi in cui il tuo tipo non corrisponde (meglio lasciare che generi un errore se non segue la stessa interfaccia che ti aspetti)? Typeckecks sono in most cases unnecessary.

+0

Sì, so che questo è sbagliato, ma questo è il mio problema. Non penso che dovrebbe essere impostato su un oggetto QTableWidget. Vorrei aggiornare il modello interno in QAbstractTableModel, ma non sono sicuro di come farlo - o trovare il numero di righe o colonne. Grazie. – drexiya

+0

Ho semplificato il codice di esempio per rimuovere questo dataFrameToQtTable, si spera che il problema sia più chiaro. – drexiya

Problemi correlati