2011-10-22 65 views
1

我想生成一個足夠簡單的應用程序,它使用QTreeView小部件來顯示來自SQLite3(平面)表的分層數據,使用QDataWidgetMapper填充一些lineedit字段,允許用戶編輯,它依次更新表格。簡單的&基本(對於大多數!)。qt pyside - qsql * model,qabstractitemmodel和qtreeview交互

我一直努力的基礎上,下面的過程將是這樣做的最佳方式:

  1. 連接到dBASE
  2. 查詢數據
  3. 創建並從數據填充定製化QAbstractItemModel(操縱它通過dict動態創建節點,父母和孩子 - 對於每個詞典條目,一個「節點」由關聯的父代生成)
  4. 使用QDatawidgetmapper來填充其他小部件
  5. 用戶編輯數據
  6. 化QAbstractItemModel(凱姆)被更新
  7. 然後必須運行在凱姆模型中使用的新值更新,插入或任何查詢。
  8. 刷新QAIM和關聯的小部件。

我意識到如果我只是使用QTableView或QListView我不需要自定義模型,可以直接寫入數據庫。我上面概述的過程似乎意味着必須保留兩組數據 - 即SQLite表和自定義QAIM,並確保它們都保持最新。這對我來說似乎有點麻煩,我相信在QTreeView直接從SQLite表中獲取數據的時候,必須有更好的方法來完成它 - 明顯需要一些操作來將平面數據轉換爲分層數據。

我在想,是否完全誤解了QAbstractItemModel和QSQL *模型之間的關係,以及我是否通過無知過度複雜化了它?

感謝

+0

什麼格式有你的分層數據?我通常使用標題和行(例如發票),但他們每個都使用一個模型。 – skuda

+0

(相對)簡單我現在只是使用一個包含「父」列的單個表。這表明應該將哪個節點/記錄追加到子節點。 –

+0

好吧,沒有看看具體的數據我不能確定你應該用其他方式存儲它,但將父母和孩子存儲在同一張表中的機會是一個好主意的可能性很小,我有一個案例在生產儘管如此,無論如何,我認爲你必須堅持保持你的模型和SQL同步的想法,Qt不必從編輯SQL表的完美支持開始,我已經創建了一個QSqlQueryModel子類來獲得更好的QSqlTableModel,也許你也可以做同樣的事情,但如果你只打算使用一次,我不建議你這樣做,更多的工作。 – skuda

回答

2

你想要的是一個代理模型充當QSql*Model和視圖之間的橋樑。爲此,您需要繼承QAbstractProxyModel。您必須有一種在代理模型中找到父子關係並將其映射到源模型的一致方法,因此可能需要在代理模型中保留一些計數。

當你在子類QAbstractProxyModel,你需要重新定義,至少,這些方法:

  • rowCount時
  • 信息columnCount
  • 指數
  • 數據
  • mapToSource
  • mapFromSource

另外,請記住,QAbstractProxyModel不會自動傳播信號。因此,爲了讓視圖瞭解源模型中的更改(如插入,刪除,更新),您需要將它們傳遞給代理模型(當然,更新代理模型中的映射)。

這將需要一些工作,但最終你會有一個更靈活的結構。它將消除所有你需要做的事情來同步數據庫和自定義QAbstractItemModel

編輯

自定義代理模型,根據給定的列從平面模型組項目:

import sys 
from collections import namedtuple 
import random 

from PyQt4 import QtCore, QtGui 

groupItem = namedtuple("groupItem",["name","children","index"]) 
rowItem = namedtuple("rowItem",["groupIndex","random"]) 


class GrouperProxyModel(QtGui.QAbstractProxyModel): 
    def __init__(self, parent=None): 
     super(GrouperProxyModel, self).__init__(parent) 

     self._rootItem = QtCore.QModelIndex() 
     self._groups = []  # list of groupItems 
     self._groupMap = {}  # map of group names to group indexes 
     self._groupIndexes = [] # list of groupIndexes for locating group row 
     self._sourceRows = [] # map of source rows to group index 
     self._groupColumn = 0 # grouping column. 

    def setSourceModel(self, source, groupColumn=0): 
     super(GrouperProxyModel, self).setSourceModel(source) 

     # connect signals 
     self.sourceModel().columnsAboutToBeInserted.connect(self.columnsAboutToBeInserted.emit) 
     self.sourceModel().columnsInserted.connect(self.columnsInserted.emit) 
     self.sourceModel().columnsAboutToBeRemoved.connect(self.columnsAboutToBeRemoved.emit) 
     self.sourceModel().columnsRemoved.connect(self.columnsRemoved.emit) 

     self.sourceModel().rowsInserted.connect(self._rowsInserted) 
     self.sourceModel().rowsRemoved.connect(self._rowsRemoved) 
     self.sourceModel().dataChanged.connect(self._dataChanged) 

     # set grouping 
     self.groupBy(groupColumn) 

    def rowCount(self, parent): 
     if parent == self._rootItem: 
      # root level 
      return len(self._groups) 
     elif parent.internalPointer() == self._rootItem: 
      # children level 
      return len(self._groups[parent.row()].children) 
     else: 
      return 0 

    def columnCount(self, parent): 
     if self.sourceModel(): 
      return self.sourceModel().columnCount(QtCore.QModelIndex()) 
     else: 
      return 0 

    def index(self, row, column, parent): 
     if parent == self._rootItem: 
      # this is a group 
      return self.createIndex(row,column,self._rootItem) 
     elif parent.internalPointer() == self._rootItem: 
      return self.createIndex(row,column,self._groups[parent.row()].index) 
     else: 
      return QtCore.QModelIndex() 

    def parent(self, index): 
     parent = index.internalPointer() 
     if parent == self._rootItem: 
      return self._rootItem 
     else: 
      parentRow = self._getGroupRow(parent) 
      return self.createIndex(parentRow,0,self._rootItem) 

    def data(self, index, role): 
     if role == QtCore.Qt.DisplayRole: 
      parent = index.internalPointer() 
      if parent == self._rootItem: 
       return self._groups[index.row()].name 
      else: 
       parentRow = self._getGroupRow(parent) 
       sourceRow = self._sourceRows.index(self._groups[parentRow].children[index.row()]) 
       sourceIndex = self.createIndex(sourceRow, index.column(), 0) 
       return self.sourceModel().data(sourceIndex, role) 
     return None 

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

    def headerData(self, section, orientation, role): 
     return self.sourceModel().headerData(section, orientation, role) 

    def mapToSource(self, index): 
     if not index.isValid(): 
      return QtCore.QModelIndex() 

     parent = index.internalPointer() 
     if not parent.isValid(): 
      return QtCore.QModelIndex() 
     elif parent == self._rootItem: 
      return QtCore.QModelIndex() 
     else: 
      rowItem_ = self._groups[parent.row()].children[index.row()] 
      sourceRow = self._sourceRows.index(rowItem_) 
      return self.createIndex(sourceRow, index.column(), QtCore.QModelIndex()) 

    def mapFromSource(self, index): 
     rowItem_ = self._sourceRows[index.row()] 
     groupRow = self._getGroupRow(rowItem_.groupIndex) 
     itemRow = self._groups[groupRow].children.index(rowItem_) 
     return self.createIndex(itemRow,index.column(),self._groupIndexes[groupRow]) 

    def _clearGroups(self): 
     self._groupMap = {} 
     self._groups = [] 
     self._sourceRows = [] 

    def groupBy(self,column=0): 
     self.beginResetModel() 
     self._clearGroups() 
     self._groupColumn = column 
     sourceModel = self.sourceModel() 
     for row in range(sourceModel.rowCount(QtCore.QModelIndex())): 
      groupName = sourceModel.data(self.createIndex(row,column,0), 
             QtCore.Qt.DisplayRole) 

      groupIndex = self._getGroupIndex(groupName) 
      rowItem_ = rowItem(groupIndex,random.random()) 
      self._groups[groupIndex.row()].children.append(rowItem_) 
      self._sourceRows.append(rowItem_) 

     self.endResetModel() 

    def _getGroupIndex(self, groupName): 
     """ return the index for a group denoted with name. 
     if there is no group with given name, create and then return""" 
     if groupName in self._groupMap: 
      return self._groupMap[groupName] 
     else: 
      groupRow = len(self._groupMap) 
      groupIndex = self.createIndex(groupRow,0,self._rootItem) 
      self._groupMap[groupName] = groupIndex 
      self._groups.append(groupItem(groupName,[],groupIndex)) 
      self._groupIndexes.append(groupIndex) 
      self.layoutChanged.emit() 
      return groupIndex 

    def _getGroupRow(self, groupIndex): 
     for i,x in enumerate(self._groupIndexes): 
      if id(groupIndex)==id(x): 
       return i 
     return 0 

    def _rowsInserted(self, parent, start, end): 
     for row in range(start, end+1): 
      groupName = self.sourceModel().data(self.createIndex(row,self._groupColumn,0), 
               QtCore.Qt.DisplayRole) 
      groupIndex = self._getGroupIndex(groupName) 
      self._getGroupRow(groupIndex) 
      groupItem_ = self._groups[self._getGroupRow(groupIndex)] 
      rowItem_ = rowItem(groupIndex,random.random()) 
      groupItem_.children.append(rowItem_) 
      self._sourceRows.insert(row, rowItem_) 
     self.layoutChanged.emit() 

    def _rowsRemoved(self, parent, start, end): 
     for row in range(start, end+1): 
      rowItem_ = self._sourceRows[start] 
      groupIndex = rowItem_.groupIndex 
      groupItem_ = self._groups[self._getGroupRow(groupIndex)] 
      childrenRow = groupItem_.children.index(rowItem_) 
      groupItem_.children.pop(childrenRow) 
      self._sourceRows.pop(start) 
      if not len(groupItem_.children): 
       # remove the group 
       groupRow = self._getGroupRow(groupIndex) 
       groupName = self._groups[groupRow].name 
       self._groups.pop(groupRow) 
       self._groupIndexes.pop(groupRow) 
       del self._groupMap[groupName] 
     self.layoutChanged.emit() 

    def _dataChanged(self, topLeft, bottomRight): 
     topRow = topLeft.row() 
     bottomRow = bottomRight.row() 
     sourceModel = self.sourceModel() 
     # loop through all the changed data 
     for row in range(topRow,bottomRow+1): 
      oldGroupIndex = self._sourceRows[row].groupIndex 
      oldGroupItem = self._groups[self._getGroupRow(oldGroupIndex)] 
      newGroupName = sourceModel.data(self.createIndex(row,self._groupColumn,0),QtCore.Qt.DisplayRole) 
      if newGroupName != oldGroupItem.name: 
       # move to new group... 
       newGroupIndex = self._getGroupIndex(newGroupName) 
       newGroupItem = self._groups[self._getGroupRow(newGroupIndex)] 

       rowItem_ = self._sourceRows[row] 
       newGroupItem.children.append(rowItem_) 

       # delete from old group 
       oldGroupItem.children.remove(rowItem_) 
       if not len(oldGroupItem.children): 
        # remove the group 
        groupRow = self._getGroupRow(oldGroupItem.index) 
        groupName = oldGroupItem.name 
        self._groups.pop(groupRow) 
        self._groupIndexes.pop(groupRow) 
        del self._groupMap[groupName] 

     self.layoutChanged.emit() 
+0

謝謝你。我一直在做一些研究,並得出QAbstractProxyModel可能是路線的結論。它現在看起來對我來說很複雜:) –

+0

@StevenLee:現在,我沒有一個工作的例子。抱歉。但我正在處理一個自定義代理,該代理根據給定列對來自表模型(如QSqlTableModel)的項目進行分組。如果你喜歡,我可以分享它,當我完成。 – Avaris

+0

這將非常感激。謝謝 –