당신이 원하는 것은 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()
어떤 형식 계층 적 데이터가? 나는 보통 헤더와 라인 (예를 들어 청구서)을 사용하지만, 모든 모델을 사용한다. – skuda
(상대적) 단순성 현재 '부모'열이있는 단일 테이블을 사용하고 있습니다. 이는 어떤 노드/레코드에 추가되어야 하는지를 나타냅니다. –
잘 구체적인 데이터를 보지 않고도 확실히 다른 방법으로 저장해야한다고 말할 수는 없지만 같은 테이블에 부모와 자식을 저장하는 것이 좋은 아이디어가 될 확률은 적습니다. 프로덕션에서 한 가지 사례가 있습니다. 하지만 어쨌든 당신이 생각하기에 모델과 SQL을 동기화 상태로 유지해야한다는 생각이들 것입니다. Qt는 SQL 테이블 편집을 완벽하게 지원할 필요가 없으며 QSqlQueryModel 서브 클래스를 작성하여 더 나은 QSqlTableModel을 갖습니다. 어쩌면 당신도 똑같이 할 수 있겠지만, 한 번만 사용한다면, 나는 당신에게 권장하지 않습니다. 훨씬 더 효과적 일 것입니다. – skuda