【问题标题】:How to sync multiple views sharing same model如何同步共享同一模型的多个视图
【发布时间】:2014-09-27 08:55:06
【问题描述】:

左侧列表视图单击会填充右侧表视图,其中包含与单击的项目项相对应的内容。问题是右侧表格视图项目列表会随着每次左侧视图单击而不断增长。 我可以继续从每个左侧视图 click s 上的所有项目中清除右侧视图:

for i in range(self.rowCount()):
    self.beginRemoveRows(QtCore.QModelIndex(), i, i)
    self.endRemoveRows()

但这需要一次又一次地重新创建右侧视图项(即使对于那些之前已经创建(然后删除)的单击项也是如此。

我想知道是否有一种方法可以只创建那些从未创建过的右侧视图项。而不是在左侧视图单击时删除它们:隐藏它们以便稍后再次单击相应的左侧视图项目时取消隐藏。

请避免使用简短的非描述性短语或关键字。而是发布示例代码,清楚地显示如何实现它。再一次,目标是在右侧视图中显示一个列表,其中仅列出了使用左侧视图单击类型的那些动物。

import os,sys
from PyQt4 import QtCore, QtGui

app=QtGui.QApplication(sys.argv)
elements={'Animals':{1:'Bison',2:'Panther',3:'Elephant'},'Birds':{1:'Duck',2:'Hawk',3:'Pigeon'},'Fish':{1:'Shark',2:'Salmon',3:'Piranha'}}
class ListModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self) 
        self.items=[]    
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items) 
    def columnCount(self, index=QtCore.QModelIndex()):
        return 1        
    def data(self, index, role):
        if not index.isValid() or not (0<=index.row()<len(self.items)): return QtCore.QVariant()
        key=str(self.items[index.row()])
        if role==QtCore.Qt.UserRole:
            return key
        if role==QtCore.Qt.DisplayRole:
            return key

    def addItem(self, key=None):
        totalItems=self.rowCount()
        self.beginInsertRows(QtCore.QModelIndex(), totalItems, totalItems)
        self.items.append(str(key))
        self.endInsertRows()

    def buildItems(self):
        for key in elements:
            self.addItem(key) 

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.items=[]    
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items)
    def columnCount(self, index=QtCore.QModelIndex()):
        return 4
    def data(self, index, role):
        key=str(self.items[index.row()])
        column=index.column()

        if not index.isValid() or not (0<=index.row()<len(self.items)): return QtCore.QVariant()
        if role==QtCore.Qt.DisplayRole:
            if not column:  return key 
            else:
                print key, column, elements.get(key,{}).get(column)
                return elements.get(key,{}).get(column)

    def rebuildItems(self, index):
        key=index.data(QtCore.Qt.UserRole).toString()  
        totalItems=self.rowCount()

        self.beginInsertRows(QtCore.QModelIndex(), totalItems, totalItems)
        self.items.append(key)
        self.endInsertRows()

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        mainLayout=QtGui.QHBoxLayout()
        self.setLayout(mainLayout) 
        self.dataModel=ListModel()
        self.dataModel.buildItems() 
        self.dataModelB=TableModel()
        self.viewA=QtGui.QListView()
        self.viewA.setModel(self.dataModel)
        self.viewA.clicked.connect(self.onClick) 
        self.viewB=QtGui.QTableView()         
        self.viewB.setModel(self.dataModelB)
        mainLayout.addWidget(self.viewA)
        mainLayout.addWidget(self.viewB)    
        self.show()
    def onClick(self, index):
        self.viewB.model().rebuildItems(index)

window=Window()
sys.exit(app.exec_())

稍后编辑:

模型/代理设置的工作示例:

import os,sys
from PyQt4 import QtCore, QtGui

app=QtGui.QApplication(sys.argv)
elements={'Animals':{1:'Bison',2:'Panther',3:'Elephant'},'Birds':{1:'Duck',2:'Hawk',3:'Pigeon'},'Fish':{1:'Shark',2:'Salmon',3:'Piranha'}}
class ListModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self) 
        self.items=[]    
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items) 
    def columnCount(self, index=QtCore.QModelIndex()):
        return 1        
    def data(self, index, role):
        if not index.isValid() or not (0<=index.row()<len(self.items)): return QtCore.QVariant()
        key=str(self.items[index.row()])
        if role==QtCore.Qt.UserRole:
            return key
        if role==QtCore.Qt.DisplayRole:
            return key

    def addItem(self, key=None):
        totalItems=self.rowCount()
        self.beginInsertRows(QtCore.QModelIndex(), totalItems, totalItems)
        self.items.append(str(key))
        self.endInsertRows()

    def buildItems(self):
        for key in elements:
            self.addItem(key) 

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.items=[]    
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items)
    def columnCount(self, index=QtCore.QModelIndex()):
        return 4
    def data(self, index, role):
        key=str(self.items[index.row()])
        column=index.column()

        if not index.isValid() or not (0<=index.row()<len(self.items)): return QtCore.QVariant()
        if role==QtCore.Qt.DisplayRole:
            if not column:  return key 
            else:           return elements.get(key,{}).get(column)

    def rebuildItems(self, index):
        key=index.data(QtCore.Qt.UserRole).toString() 
        if key in self.items: return

        totalItems=self.rowCount()        
        self.beginInsertRows(QtCore.QModelIndex(), totalItems, totalItems)
        self.items.append(key)
        self.endInsertRows()

class ProxyTableModel(QtGui.QSortFilterProxyModel):
    def __init__(self, parent=None):
        super(ProxyTableModel, self).__init__(parent)

    def headerData(self, column, orientation, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.TextAlignmentRole:
            if orientation == QtCore.Qt.Horizontal:
                return QtCore.QVariant(int(QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter))
            return QtCore.QVariant(int(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter))
        if role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()

        if orientation==QtCore.Qt.Horizontal:
            return QtCore.QVariant("Species %s"%column)

        return QtCore.QVariant(int(column + 1))

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        mainLayout=QtGui.QHBoxLayout()
        self.setLayout(mainLayout) 
        listModel=ListModel()
        listModel.buildItems() 

        tableModel=TableModel()
        proxyTableModel=ProxyTableModel()
        proxyTableModel.setSourceModel(tableModel)
        proxyTableModel.setFilterKeyColumn(0)    

        self.viewA=QtGui.QListView()
        self.viewA.setModel(listModel)
        self.viewA.clicked.connect(self.onClick) 
        self.viewB=QtGui.QTableView()         
        self.viewB.setModel(proxyTableModel)
        self.viewB.setColumnHidden(0,True)

        mainLayout.addWidget(self.viewA)
        mainLayout.addWidget(self.viewB)    
        self.show()
    def onClick(self, index):
        self.viewB.model().sourceModel().rebuildItems(index)

        key=index.data().toString()
        self.viewB.model().setFilterRegExp(key)

window=Window()
sys.exit(app.exec_())

编辑 #2

这是一个基于 mdurant 建议的工作代码(非常感谢!)。不是使用proxy model(过滤右侧视图的显示内容),而是使用现有索引(为了使本示例的内容更简单,我将右侧视图切换到 QListView)。


import os,sys
from PyQt4 import QtCore, QtGui

app=QtGui.QApplication(sys.argv)
elements={'Animals':{0:'Bison',1:'Panther',2:'Elephant',3:'Dog'},'Birds':{0:'Duck',1:'Hawk',2:'Pigeon'},'Fish':{0:'Shark',1:'Salmon'}}
class ListModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self) 
        self.items=[]    
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items) 
    def columnCount(self, index=QtCore.QModelIndex()):
        return 1        
    def data(self, index, role):
        if not index.isValid() or not (0<=index.row()<len(self.items)): return QtCore.QVariant()
        key=str(self.items[index.row()])
        if role==QtCore.Qt.UserRole:
            return key
        if role==QtCore.Qt.DisplayRole:
            return key

    def addItem(self, key=None):
        totalItems=self.rowCount()
        self.beginInsertRows(QtCore.QModelIndex(), totalItems, totalItems)
        self.items.append(str(key))
        self.endInsertRows()

    def buildItems(self):
        for key in elements:
            self.addItem(key) 

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.key=None
        self.items=[]    
    def rowCount(self, parent=QtCore.QModelIndex()):   
        if not self.items:   return 0
        else: return len(self.items) 

    def columnCount(self, index=QtCore.QModelIndex()):
        return 2

    def data(self, index, role):
        if not index.isValid() or not (0<=index.row()<len(self.items)): return QtCore.QVariant()       
        if not self.key: return QtCore.QVariant() 

        row=index.row()
        if role==QtCore.Qt.DisplayRole:
            return elements.get(self.key,{}).get(row)

    def rebuildItems(self, index):        
        self.key=str(index.data(QtCore.Qt.UserRole).toString())

        self.items=elements[self.key].keys()
        self.reset()

        self.dataChanged.emit(self.index(1,1), self.index(1,4))

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        mainLayout=QtGui.QHBoxLayout()
        self.setLayout(mainLayout) 
        self.dataModel=ListModel()
        self.dataModel.buildItems() 

        self.viewA=QtGui.QListView()
        self.viewA.setModel(self.dataModel)
        self.viewA.clicked.connect(self.onClick) 

        self.viewB=QtGui.QListView()         
        self.dataModelB=TableModel()   

        self.viewB.setModel(self.dataModelB)
        # self.viewB.setColumnHidden(0,True)        

        mainLayout.addWidget(self.viewA)
        mainLayout.addWidget(self.viewB)    
        self.show()

    def onClick(self, index):
        self.viewB.model().rebuildItems(index)

window=Window()
sys.exit(app.exec_())

【问题讨论】:

    标签: python qt pyqt


    【解决方案1】:

    现在是单行。当尚未选择任何内容时,可以隐藏此行 - 我只是将其显示为空。

    class TableModel(QtCore.QAbstractTableModel):
    
        sel = None
    
        def __init__(self):
            QtCore.QAbstractTableModel.__init__(self)
            self.items=[]    
        def rowCount(self, parent=QtCore.QModelIndex()):
            return 1
        def columnCount(self, index=QtCore.QModelIndex()):
            return 4
        def data(self, index, role):
            if not index.isValid() or not (0<=index.row()<len(self.items)): return 
            key=self.sel
            column=index.column()
            if role==QtCore.Qt.DisplayRole:
                if not column:  return str(key)
                else:
                    return elements.get(key,{}).get(column) if (self.sel is not None) else QtCore.QVariant()
    
        def rebuildItems(self, index):
            key=index.data(QtCore.Qt.UserRole)
            if not key in self.items:
                self.items.append(key)
            self.sel = key
            self.dataChanged.emit(self.index(1,1), self.index(1,4) )
    
    class Window(QtGui.QWidget):
        def __init__(self):
            super(Window, self).__init__()
            mainLayout=QtGui.QHBoxLayout()
            self.setLayout(mainLayout) 
            self.dataModel=ListModel()
            self.dataModel.buildItems() 
            self.dataModelB=TableModel()
            self.viewA=QtGui.QListView()
            self.viewA.setModel(self.dataModel)
            self.viewA.clicked.connect(self.onClick) 
            self.viewB=QtGui.QTableView()         
            self.viewB.setModel(self.dataModelB)
            mainLayout.addWidget(self.viewA)
            mainLayout.addWidget(self.viewB)    
            self.show()
        def onClick(self, index):
            self.viewB.model().rebuildItems(index)
    

    【讨论】:

    • 让我看看...同时请查看我在“稍后编辑”部分的原始问题下发布的代码。它使用 proxymodel 根据左侧显示中选择的内容从显示中过滤右侧视图项...
    • OK - 但对于当前任务来说看起来相当复杂。我希望你的用例证明它是合理的。请注意,数据更自然地用树来描述,而不是列表/表格。
    • 阅读您的示例后提出的问题。 self.dataChanged.emit(self.index(1,1), self.index(1,4)) - 这行触发了什么方法?
    • 这是一个内部信号,它告诉视图在这些索引之间重新显示它们的数据。如果您忽略它,则在显示因某些其他原因(例如调整大小)发生更改之前不会重新绘制。
    • 所以我们通过发出dataChanged 信号来命令重绘视图!很高兴知道!请查看我原始问题中的“EDITED #2”部分。我将您的代码修改为原始问题示例代码。不幸的是,右侧视图没有重绘...
    【解决方案2】:

    只要不重复插入,就可以满足您的要求:

    def rebuildItems(self, index):
        key=index.data(QtCore.Qt.UserRole)
        totalItems=self.rowCount()
        if not key in self.items:
            self.beginInsertRows(QtCore.QModelIndex(), totalItems, totalItems)
            self.items.append(key)
            self.endInsertRows()
    

    但是,对于隐藏项目,这是由视图(或代理模型)完成的,而不是模型本身。

    【讨论】:

    • ...但我真希望我能理解你在这里想要达到的目标。
    • 另一种方法是让模型具有一个包含当前选定行的实例变量,并在第 1 行显示该项目的信息 - 模型始终保持 1 行长。
    • 我希望两个视图共享同一个 elements 变量。因此,当数据发生更改时,两个视图都会自动更新。左视图显示项目列表。而右侧视图显示子项目...假设左侧视图用于列出所有书名。单击左侧视图中的单个书名会在右侧视图中显示本书的章节。我是否正确实施它?应该使用单个模型还是多个模型?我计划使用第三个视图,当单击“章节”视图时,它将显示子子项,它将显示页面等。
    猜你喜欢
    • 1970-01-01
    • 2012-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多