0

名单:

items = [['Pet', 'Dog'],['Pet', 'Cat'],['Bird','Eagle'],['Bird','Jay'],['Bird','Falcon']]

model分配给QTableView和使用QComboBox

http://i.imgur.com/Us551Nh.png

我只想Combobox显示“宠物”和“鸟”,而QTableView显示:“狗”、“鹰”和“杰伊”。如何做到这一点?

from PySide import QtGui, QtCore

class Model(QAbstractTableModel):
    def __init__(self, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.items = [['Pet', 'Dog'],['Pet', 'Cat'],['Bird','Eagle'],['Bird','Jay'],['Bird','Falcon']]

    def rowCount(self, parent=QModelIndex()):
        return len(self.items)      
    def columnCount(self, parent=QModelIndex()):
        return 2
    def data(self, index, role):
        if not index.isValid(): return 
        row=index.row()
        column=index.column()
        return self.items[row][column]

class Proxy(QSortFilterProxyModel):
    def __init__(self):
        super(Proxy, self).__init__()

    def filterAcceptsRow(self, rowProc, parentProc):  
        modelIndex=self.sourceModel().index(rowProc, 0, parentProc)
        item=self.sourceModel().data(modelIndex, Qt.DisplayRole)
        return True

class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        vLayout=QVBoxLayout(self)
        self.setLayout(vLayout)

        model=Model(self)  
        proxy=Proxy()
        proxy.setSourceModel(model)

        self.combo=QtGui.QComboBox()
        self.combo.activated.connect(self.comboActivated)
        vLayout.addWidget(self.combo)

        self.combo.setModel(proxy)

        self.ViewA=QTableView(self)
        self.ViewA.setModel(model)
        self.ViewA.clicked.connect(self.viewClicked)
        vLayout.addWidget(self.ViewA)

    def viewClicked(self, indexClicked):
        print 'indexClicked() row: %s  column: %s'%(indexClicked.row(), indexClicked.column() )
        proxy=indexClicked.model()

    def comboActivated(self, arg):
        print 'comboClicked() arg:', arg

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())
4

2 回答 2

3

可以做你想做的事您需要为每个视图实现一个单独的 QAbstractProxyModel。组合框应过滤并仅保留第一列的非重复元素。另一个应该将第二行过滤到仅与组合框视图的当前状态相关的行。

如果你这样做了,在这个例子中,你最终会得到四个与模型相关的对象:你的数据(in Model.items)、yourModel和两个代理。

但我认为这根本不是一个好方法。这两个代理都很复杂,完全改变了原始模型的行、列结构。想象一下当模型第一列中的数据发生变化时,在每个代理中处理信号所需的复杂性。dataChanged

在实践中,我认为使用三个对象版本会更好:一个适当的(稍微详细说明的)数据类,然后是一对 QAbstractItemModels 共享该数据并且每个都专门用于它们支持的视图。可以将其想象为同一文件系统上的一对 QFileSystemModel。

必须将数据更改通知给两个 QAbstractItemModel,而您将无法享受由 Qt 框架自动实现的好处。但是,正如我上面指出的,在实践中,无论如何代理都不会轻易工作。

因此,这是一个基于您的代码的示例,并且更改尽可能少。请注意,我必须修复很多问题 - 您的导入不起作用,并且您的组合模型为某些角色返回了不正确的数据。

from PySide import QtGui, QtCore
import sys

class Data(object):

    def __init__(self):
        self.items = [['Pet', 'Dog'],['Pet', 'Cat'],['Bird','Eagle'],['Bird','Jay'],['Bird','Falcon']]
        self.selectors = list({ k[0] for k in self.items})

    def currentItems(self,select_on):
        return [k[1] for k in self.items if k[0] == select_on]

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.data_obj = data
        self.currentSelection = None
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.data_obj.currentItems(self.currentSelection))      
    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1
    def data(self, index, role):
        if not index.isValid() or role != QtCore.Qt.DisplayRole: return 
        row=index.row()
        return self.data_obj.currentItems(self.currentSelection)[row]
    def setSelection(self,combo_row):
        self.currentSelection = self.data_obj.selectors[combo_row]
        self.layoutChanged.emit()

class ComboModel(QtCore.QAbstractListModel):
    def __init__(self, data, parent=None):
        QtCore.QAbstractListModel.__init__(self, parent)
        self.data_obj = data
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.data_obj.selectors)      
    def data(self, index, role):
        if not index.isValid() or role != QtCore.Qt.DisplayRole: return 
        row=index.row()
        return self.data_obj.selectors[row]

class MyWindow(QtGui.QWidget):
    def __init__(self, *args):
        QtGui.QWidget.__init__(self, *args)
        vLayout=QtGui.QVBoxLayout(self)
        self.setLayout(vLayout)

        self.data=Data()
        self.tableModel = TableModel(self.data)
        self.comboModel = ComboModel(self.data)

        self.combo=QtGui.QComboBox()
        self.combo.setModel(self.comboModel)
        self.combo.activated.connect(self.tableModel.setSelection)
        vLayout.addWidget(self.combo)

        self.ViewA=QtGui.QTableView(self)
        self.ViewA.setModel(self.tableModel)
        self.ViewA.clicked.connect(self.viewClicked)
        vLayout.addWidget(self.ViewA)

        self.tableModel.setSelection(0)

    def viewClicked(self, indexClicked):
        print('indexClicked() row: %s  column: %s'%(indexClicked.row(), indexClicked.column() ))

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

但这是另一个更基于您的想法的版本,并且是我过去使用的模式。在这里,我们按照您的要求提供了一个模型,但提供了两个实际上都是 QAbstractItemModels 的接口。在这个例子中并没有什么不同,但是使单一模型的想法更加清晰。

第一个版本有一个由底层数据表示驱动的数据对象,因此可以非常干净和清晰。但它需要一个自定义的信号方法来向两个模型发出信号变化。

第二个版本有一个单一的集成模型对象,它封装了该信号。但在实际系统中,它可能仍需要连接到单独的数据对象——因此在实践中,此版本中可能有更多对象。

from PySide import QtGui, QtCore
import sys

class CombinedModel(object):

    def __init__(self):
        self.items = [['Pet', 'Dog'],['Pet', 'Cat'],['Bird','Eagle'],['Bird','Jay'],['Bird','Falcon']]
        self.selectors = list({ k[0] for k in self.items})
        self.table_if = TableModel(self)
        self.combo_if = ComboModel(self)
        self.currentSelection = None

    def currentItems(self):
        return [k[1] for k in self.items if k[0] == self.currentSelection]

    def setSelection(self,combo_row):
        self.currentSelection = self.selectors[combo_row]
        self.table_if.layoutChanged.emit()

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, model, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.model = model

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.model.currentItems())      
    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1
    def data(self, index, role):
        if not index.isValid() or role != QtCore.Qt.DisplayRole: return 
        row=index.row()
        return self.model.currentItems()[row]

class ComboModel(QtCore.QAbstractListModel):
    def __init__(self, model, parent=None):
        QtCore.QAbstractListModel.__init__(self, parent)
        self.model = model

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.model.selectors)      
    def data(self, index, role):
        if not index.isValid() or role != QtCore.Qt.DisplayRole: return 
        row=index.row()
        return self.model.selectors[row]

class MyWindow(QtGui.QWidget):
    def __init__(self, *args):
        QtGui.QWidget.__init__(self, *args)
        vLayout=QtGui.QVBoxLayout(self)
        self.setLayout(vLayout)

        self.model=CombinedModel()

        self.combo=QtGui.QComboBox()
        self.combo.setModel(self.model.combo_if)
        self.combo.activated.connect(self.model.setSelection)
        vLayout.addWidget(self.combo)

        self.ViewA=QtGui.QTableView(self)
        self.ViewA.setModel(self.model.table_if)
        self.ViewA.clicked.connect(self.viewClicked)
        vLayout.addWidget(self.ViewA)

        self.model.setSelection(0)

    def viewClicked(self, indexClicked):
        print('indexClicked() row: %s  column: %s'%(indexClicked.row(), indexClicked.column() ))

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())
于 2015-08-15T23:27:51.117 回答
0

我希望 ComboBox 显示两个通用类别(宠物和鸟类)。用户选择类别后,QTableView 将显示与所选类别相关的每个物种的列表。

为此,您不需要对任何模型进行子类化。您可以使用字符串列表直接填充 a QComboBox,并且可以使用 aQListWidget来显示动物名称。

为了方便起见,我将您的列表转换为字典:

self.items = [['Pet', 'Dog'],['Pet', 'Cat'],['Bird','Eagle'],['Bird','Jay'],['Bird','Falcon']]

变成

self.myDict={"Pet":['Dog','Cat'],"Bird":['Eagle','Jay','Falcon']}

字典的键(宠物、鸟)可用于QComboBox. 当用户选择一个物种(信号:QComboBox.currentIndexChanged)时,您会显示与 a 中的键关联的列表QListWidget

这是完整的工作示例:

import sys, signal
from PyQt4 import QtCore, QtGui

class myWidget(QtGui.QWidget):
    def __init__( self, parent=None):
        super(myWidget, self ).__init__( parent )

        self.myDict={"Pet":['Dog','Cat'],"Bird":['Eagle','Jay','Falcon']}

        self.listWidget=QtGui.QListWidget()

        self.comboBox=QtGui.QComboBox()
        self.comboBox.addItems(list(self.myDict.keys()))
        #use overload signal: emits the text associated to the index 
        self.comboBox.currentIndexChanged[str].connect(self.on_change)
        #set initial index so the listWidget is not empty
        self.comboBox.setCurrentIndex(1)

        layout=QtGui.QVBoxLayout()
        layout.addWidget(self.comboBox)
        layout.addWidget(self.listWidget)
        self.setLayout(layout)

    def on_change(self,key):
        #clear everything
        self.listWidget.clear()
        #fill with list of corresponding key
        for name in self.myDict[key]:
            item=QtGui.QListWidgetItem(name)
            item.setFlags(item.flags()|QtCore.Qt.ItemIsUserCheckable)
            item.setCheckState(QtCore.Qt.Unchecked)
            self.listWidget.addItem(item)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    win= myWidget()
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    win.show() 
    sys.exit(app.exec_())
于 2015-08-14T08:07:39.897 回答