我正在构建一个用于电影摄影的python / PySide工具。我创建了一个代表镜头的对象。它具有开始时间,结束时间和actor对象引用列表的属性。 actor对象具有简单的属性(名称,构建,年龄等),并且可以在镜头之间共享。
我想在PySide的两个表视图中显示它。一个表格视图列出镜头(和列中的属性),而另一个表格视图显示选定的镜头中引用的actor。如果未选择镜头,则第二个表视图为空。如果选择了多个镜头,则所有引用的actor都将显示在actor表视图中。
我为我的镜头数据创建了一个abstractTableModel,并且在相应的表视图中,所有内容都能正常运行。但是,我不确定如何甚至接近演员的表格视图。我应该为演员使用另一个abstractTableModel吗?我似乎无法弄清楚如何使用abstractTableModel将数据提供/连接到所选镜头中包含的actor的第二个表视图。
我认为我的一部分问题是我只想显示一次演员信息,无论多个选定的镜头是否引用同一个演员。在多次失败尝试之后,我认为我需要将所选镜头信息(他们的actor列表属性)重定向并解析为主窗口的自定义属性,以包含所有引用的列表actor索引并创建一个abstractTableModel,它使用它来获取显示的实际actor属性。我不完全相信这会有效,更不用说我的直觉告诉我这是一个混乱的方法,所以我来这里寻求建议。
这是正确的做法吗?如果没有,那么“正确的”是什么?在PySide / python中设置它的方法。
请记住,这是我第一次涉足数据模型和PySide。
这是拍摄模型。
class ShotTableModel(QtCore.QAbstractTableModel):
def __init__(self, data=[], parent=None, *args):
super(ShotTableModel, self).__init__(parent)
self._data = data
def rowCount(self, parent):
return len(self._data.shots)
def columnCount(self, parent):
return len(self._data._headers_shotList)
def getItemFromIndex(self, index):
if index.isValid():
item = self._data.shots[index.row()]
if item:
return item
return None
def flags(self, index):
if index.isValid():
item = self.getItemFromIndex(index)
return item.qt_flags(index.column())
def data(self, index, role):
if not index.isValid():
return None
item = self.getItemFromIndex(index)
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
return item.qt_data(index.column())
if role == QtCore.Qt.CheckStateRole:
if index.column() is 0:
return item.qt_checked
# if role == QtCore.Qt.BackgroundColorRole:
# return QtGui.QBrush()
# if role == QtCore.Qt.FontRole:
# return QtGui.QFont()
if role == QtCore.Qt.DecorationRole:
if index.column() == 0:
resource = item.qt_resource()
return QtGui.QIcon(QtGui.QPixmap(resource))
if role == QtCore.Qt.ToolTipRole:
return item.qt_toolTip()
return None
def setData(self, index, value, role = QtCore.Qt.EditRole):
if index.isValid():
item = self.getItemFromIndex(index)
if role == QtCore.Qt.EditRole:
item.qt_setData(index.column(), value)
self.dataChanged.emit(index, index)
return value
if role == QtCore.Qt.CheckStateRole:
if index.column() is 0:
item.qt_checked = value
return True
return value
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return self._data._headers_shotList[section]
def insertRows(self, position, rows, parent = QtCore.QModelIndex()):
self.beginInsertRows(parent, position, position + rows - 1)
for row in range(rows):
newShotName = self._data.getUniqueName(self._data.shots, 'New_Shot')
newShot = Shot(newShotName)
self._data.shots.insert(position, newShot)
self.endInsertRows()
return True
def removeRows(self, position, rows, parent = QtCore.QModelIndex()):
self.beginRemoveRows(parent, position, position + rows - 1)
for row in range(rows):
self._data.shots.pop(position)
self.endRemoveRows()
return True
这里是包含镜头和actor实例的数据块。这就是我传递给镜头模型的内容。
class ShotManagerData(BaseObject):
def __init__(self, name='', shots=[], actors=[]):
super(ShotManagerData, self).__init__(name)
self._shots = shots # Shot(name="New_Shot", start=0, end=100, actors=[])
self._actors = actors # Actor(name="New_Actor", size="Average", age=0)
self.selectedShotsActors = [] #decided to move to this data block
self._headers_shotList = ['Name', 'Start Frame', 'End Frame']
self._headers_actorList = ['Name', 'Type', 'RootNode', 'File']
def save(self, file=None):
mEmbed.save('ShotManagerData', self)
@classmethod
def load(cls, file=None):
return(mEmbed.load('ShotManagerData'))
@staticmethod
def getUniqueName(dataList, baseName='New_Item'):
name = baseName
increment = 0
list_of_names = [data.name if issubclass(data.__class__, BaseObject) else str(data) for data in dataList]
while name in list_of_names:
increment += 1
name = baseName + '_{0:02d}'.format(increment)
return name
@property
def actors(self):
return self._actors
@property
def shots(self):
return self._shots
def actorsOfShots(self, shots):
actorsOfShots = []
for shot in shots:
for actor in shot.actors:
if actor not in actorsOfShots:
actorsOfShots.append(actor)
return actorsOfShots
def shotsOfActors(self, actors):
shotsOfActors = []
for actor in actors:
for shot in self.shots:
if actor in shot.actors and actor not in shotsOfActors:
shotsOfActors.append(shot)
return shotsOfActors
最后是主要工具。
class ShotManager(form, base):
def __init__(self, parent=None):
super(ShotManager, self).__init__(parent)
self.setupUi(self)
#=======================================================================
# Properties
#=======================================================================
self.data = ShotManagerData() #do any loading if necessary here
self.actorsInSelectedShots = []
#test data
actor1 = Actor('Actor1')
actor2 = Actor('Actor2')
actor3 = Actor('Actor3')
shot1 = Shot('Shot1', [actor1, actor2])
shot2 = Shot('Shot2', [actor2, actor3])
shot3 = Shot('Shot3', [actor1])
self.data.actors.append(actor1)
self.data.actors.append(actor2)
self.data.actors.append(actor3)
self.data.shots.append(shot1)
self.data.shots.append(shot2)
self.data.shots.append(shot3)
#=======================================================================
# Models
#=======================================================================
self._model_shotList = ShotTableModel(self.data)
self._proxyModel_shotList = QtGui.QSortFilterProxyModel()
self._proxyModel_shotList.setSourceModel(self._model_shotList)
self.shotList.setModel(self._proxyModel_shotList) #this is the QTableView
self._selModel_shotList = self.shotList.selectionModel()
self.shotList.setSortingEnabled(True)
self._proxyModel_shotList.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self._model_actorList = SelectedShotsActorTableModel(self.data)
self._proxyModel_actorList = QtGui.QSortFilterProxyModel()
self._proxyModel_actorList.setSourceModel(self._model_actorList)
self.actorList.setModel(self._proxyModel_actorList)
self._selModel_actorList = self.actorList.selectionModel()
#=======================================================================
# Events
#=======================================================================
self.addShot.clicked.connect(self.addShot_clicked)
self.delShot.clicked.connect(self.delShot_clicked)
self._selModel_shotList.selectionChanged.connect(self.shotList_selectionChanged)
#===========================================================================
# General Functions
#===========================================================================
def getSelectedRows(self, widget):
selModel = widget.selectionModel()
proxyModel = widget.model()
model = proxyModel.sourceModel()
rows = [proxyModel.mapToSource(index).row() for index in selModel.selectedRows()]
rows.sort()
return rows
def getSelectedItems(self, widget):
selModel = widget.selectionModel()
proxyModel = widget.model()
model = proxyModel.sourceModel()
indices = [proxyModel.mapToSource(index) for index in selModel.selectedRows()]
items = [model.getItemFromIndex(index) for index in indices]
return items
#===========================================================================
# Event Functions
#===========================================================================
def addShot_clicked(self):
position = len(self.data.shots)
self._proxyModel_shotList.insertRows(position,1)
def delShot_clicked(self):
rows = self.getSelectedRows(self.shotList)
for row in reversed(rows):
self._proxyModel_shotList.removeRows(row, 1)
def shotList_selectionChanged(self, selected, deselected):
selectedShots = self.getSelectedItems(self.shotList)
print 'SelectedShots: {}'.format(selectedShots)
self.data.selectedShotsActors = self.data.actorsOfShots(selectedShots)
print 'ActorsOfShots: {}'.format(self.data.selectedShotsActors)
self._proxyModel_actorList.setData() # this line reports missing variables
这是selectedShotActors模型:
class SelectedShotsActorTableModel(QtCore.QAbstractTableModel):
def __init__(self, data=[], headers=[], parent=None, *args):
super(SelectedShotsActorTableModel, self).__init__(parent)
self._data = data
def rowCount(self, parent):
return len(self._data.selectedShotsActors)
def columnCount(self, parent):
return len(self._data._headers_actorList)
def getItemFromIndex(self, index):
if index.isValid():
item = self._data.selectedShotsActors[index.row()]
if item:
return item
return None
def flags(self, index):
if index.isValid():
item = self.getItemFromIndex(index)
return item.qt_flags(index.column())
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return None
item = self.getItemFromIndex(index)
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
return item.qt_data(index.column())
if role == QtCore.Qt.CheckStateRole:
if index.column() is 0:
return item.qt_checked
#
# if role == QtCore.Qt.BackgroundColorRole:
# return QtGui.QBrush()
#
# if role == QtCore.Qt.FontRole:
# return QtGui.QFont()
if role == QtCore.Qt.DecorationRole:
if index.column() == 0:
resource = item.qt_resource()
return QtGui.QIcon(QtGui.QPixmap(resource))
if role == QtCore.Qt.ToolTipRole:
return item.qt_toolTip()
def setData(self, index, value, role = QtCore.Qt.EditRole):
if index.isValid():
item = self.getItemFromIndex(index)
if role == QtCore.Qt.EditRole:
item.qt_setData(index.column(), value)
self.dataChanged.emit(index, index)
return value
if role == QtCore.Qt.CheckStateRole:
if index.column() is 0:
item.qt_checked = value
return True
return value
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return self._data._headers_actorList[section]
答案 0 :(得分:1)
我的建议是更新来自selectionChanged()
selectionModel
的{{1}}信号的演员模型。
每次发出信号时(更改镜头选择时),您需要重置actor模型,然后迭代选择中的选定模型索引,并获取每个选定行的镜头对象的引用。你的模特。对于每个镜头对象,您可以获得一个actor列表。然后你需要检查actor是否在actor模型中,如果不是,则将其添加进去。
现在这样效率有点低,因为每次更改镜头模型的选择时都会重置actor模型。您确实从QTableView
信号中获取了有关取消选择哪些行的信息,因此您可以在取消选择镜头模型的行时从您的actor模型中删除条目,但仅当没有其他选定的镜头行包含从你取消选择的镜头中给出演员。
这个代码可以在你的项目中存在一些选项,但我可能会在你已经可以访问演员和镜头的视图和模型的地方。这可能在你的QMainWindow的子类中,但是没有看到代码就很难说出来了!
希望有所帮助:)
答案 1 :(得分:1)
我建议您使用代理模型。代理模型不保存数据,它们链接到现有模型,并提供根据需要对数据进行排序,过滤或重组的机会。
具体而言,您可以使用QSortFilterProxyModel
的{{1}}作为来源创建QTableView
对象。然后,您可以创建一个自定义过滤器,根据所选镜头构建一个actor列表。此方法的优点是代理模型和视图将随着选择的更改而自动更新。我认为这种方法比将代码添加到MainWindow更符合MVC的意图。
有关如何执行此操作的详细信息,请参阅自定义排序/过滤器模型示例。 http://qt-project.org/doc/qt-5/qtwidgets-itemviews-customsortfiltermodel-example.html
如果您需要一些背景信息(我也是Qt MVC的新手;我感到很痛苦)这里有一些其他有用的链接:
模型 - 视图编程:代理模型 http://qt-project.org/doc/qt-5/model-view-programming.html#proxy-models
QSortFilterProxyModel http://qt-project.org/doc/qt-5/qsortfilterproxymodel.html