2011-10-22 3 views
1

QtreeView 위젯을 사용하여 SQLite3 (플랫) 테이블의 계층 적 데이터를 표시하고 QDataWidgetMapper를 사용하여 몇 가지 lineedit 필드를 채우고 사용자가 편집, 차례로 테이블을 업데이 트합니다. 간단한 & 기본 (대부분!).qt pyside - qsql * 모델, qabstractitemmodel 및 qtreeview 상호 작용

  1. 연결 디베이스에
  2. 쿼리 데이터
  3. 가 작성하고 (데이터에서 사용자 정의 QAbstractItemModel를 채울 다음 프로세스가이 일을하는 가장 좋은 방법이 될 것입니다 내가 기초로 일하고있다

    dict을 통해 노드, 부모 및 자식을 동적으로 생성 - 각 부모 노드에 대해 부모 노드와 함께 '노드'가 생성됨)

  4. QDatawidgetmapper를 사용하여 다른 위젯을 채우기
  5. 사용자 편집 데이터
  6. QAbstractItemModel (Qaim의는) 그런 다음 Qaim의 모델에서 새 값을 사용하여 UPDATE, INSERT 또는 어떤 쿼리를 실행해야
  7. 업데이트됩니다.
  8. QAIM 및 연관된 위젯을 새로 고치십시오.

QTableView 또는 QListView를 사용했다면 사용자 지정 모델이 필요 없으며 데이터베이스에 곧바로 쓸 수 있다는 것을 알고 있습니다. 위에서 요약 한 프로세스는 SQLite 테이블과 사용자 지정 QAIM의 두 가지 데이터 세트를 유지해야하며 둘 다 최신 상태로 유지된다는 것을 의미합니다. QTreeView가 SQLite 테이블에서 바로 데이터를 가져 오는 더 좋은 방법이 있어야합니다. 플랫 데이터를 계층 적 데이터로 변환하는 몇 가지 조작이 필요합니다.

내가 QAbstractItemModel과 QSQL * Models 사이의 관계를 완전히 오해하고 있는지 여부와 궁금합니다. 무지로 그것을 지나치게 복잡하게 만드십니까?

감사

+0

어떤 형식 계층 적 데이터가? 나는 보통 헤더와 라인 (예를 들어 청구서)을 사용하지만, 모든 모델을 사용한다. – skuda

+0

(상대적) 단순성 현재 '부모'열이있는 단일 테이블을 사용하고 있습니다. 이는 어떤 노드/레코드에 추가되어야 하는지를 나타냅니다. –

+0

잘 구체적인 데이터를 보지 않고도 확실히 다른 방법으로 저장해야한다고 말할 수는 없지만 같은 테이블에 부모와 자식을 저장하는 것이 좋은 아이디어가 될 확률은 적습니다. 프로덕션에서 한 가지 사례가 있습니다. 하지만 어쨌든 당신이 생각하기에 모델과 SQL을 동기화 상태로 유지해야한다는 생각이들 것입니다. Qt는 SQL 테이블 편집을 완벽하게 지원할 필요가 없으며 QSqlQueryModel 서브 클래스를 작성하여 더 나은 QSqlTableModel을 갖습니다. 어쩌면 당신도 똑같이 할 수 있겠지만, 한 번만 사용한다면, 나는 당신에게 권장하지 않습니다. 훨씬 더 효과적 일 것입니다. – skuda

답변

2

당신이 원하는 것은 QSql*Model과 뷰 사이의 다리 역할을하는 프록시 모델입니다. 이를 위해서는 서브 클래스 QAbstractProxyModel이 필요합니다. 프록시 모델에서 부모 - 자식 관계를 찾고이를 소스 모델에 매핑하는 일관된 방법을 가져야하므로 프록시 모델에서 일부 계산을 유지해야 할 수도 있습니다.

  • 를 rowCount
  • 열 개수
  • 부모
  • 인덱스
  • 데이터
  • :

    당신이 QAbstractProxyModel을 하위 클래스라는, 당신은 다시 정의, 최소한 이러한 방법 필요

  • 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

@ 스페셜 라이 : 당장, 나는 실제 예제가 없습니다. 죄송합니다. 하지만 주어진 열을 기반으로 QSqlTableModel 같은 테이블 모델의 항목을 그룹화하는 사용자 지정 프록시 작업을하고 있습니다. 원한다면, 내가 끝나면 나눌 수 있습니다. – Avaris

+0

대단히 감사하겠습니다. 감사합니다 –