2

With two views:

listView=QtGui.QListView()
tableView==QtGui.QTableView() 

I go ahead and define a custom DataModel to be used by both listView and tableView:

class DataModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.data=[['One':'Two'],['Three','Four']]

def data(self, index, role):
    row=index.row()
    if requested by listView:
        return self.data[row][0]
    elif requested by tableView:
        return self.data[row][1]

Finally assigning the instance of the model to both views:

model=DataModel()
listView.setModel(model)
tableView.setModel(model)

Since both views share the same model there is a chance the model would need to return different values based on what view widget is requesting it. If you take a look at data() method there is fake if/elif statement showing what I need. Is there any way to do this logic from inside of model methods: if requested by ListView... and elif requested by TableView

Edited later:

enter image description here

import os,sys
from PyQt4 import QtCore, QtGui
app=QtGui.QApplication(sys.argv)
elements={'Animals':{0:'Bison',1:'Panther',2:'Elephant'},'Birds':{0:'Duck',1:'Hawk',2:'Pigeon'},'Fish':{0:'Shark',1:'Salmon',2:'Piranha'}}

class DataModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.modelDict={}    
        self.items=[]    
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items)   

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

    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()])
        column=index.column()

        if role==QtCore.Qt.ItemDataRole: return self.modelDict.get(str(index.data().toString()))

        if role==QtCore.Qt.DisplayRole:
            if column==0 and not self.columnCount():
                return key
            else:
                return self.modelDict.get(key).get(column)  

    def addItem(self, itemName=None, column=0):
        totalItems=self.rowCount()+1
        self.beginInsertRows(QtCore.QModelIndex(), totalItems, column)
        if not itemName:            itemName='Item %s'%self.rowCount()
        self.items.append(itemName)
        self.endInsertRows()

    def buildItems(self):
        for key in self.modelDict:
            index=QtCore.QModelIndex()
            self.addItem(key) 

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:
            if column==0:
                return QtCore.QVariant("Spicie 0")
            elif column==1:
                return QtCore.QVariant("Spicie 1")
            elif column==2:
                return QtCore.QVariant("Spicie 2")

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

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

        self.dataModel=DataModel()
        self.dataModel.modelDict=elements
        self.dataModel.buildItems() 

        self.proxyModel=ProxyTableModel()
        self.proxyModel.setFilterKeyColumn(0)    
        self.proxyModel.setSourceModel(self.dataModel)

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

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

    def onClick(self):
        index=self.viewA.currentIndex()
        key=self.dataModel.data(index, QtCore.Qt.DisplayRole)  
        value=self.dataModel.data(index, QtCore.Qt.ItemDataRole)        
        self.proxyModel.setFilterRegExp('%s'%key)
        print 'onClick(): key: %s'%key

window=Window()
sys.exit(app.exec_())
alphanumeric
  • 17,967
  • 64
  • 244
  • 392
  • For the example as you give it, you would simply hide the non-displayed columns in each view, that's simple. If you actually want to process the data in some way (e.g., to provide an icon view or translation), you probably need qitemdelegate. – mdurant Sep 17 '14 at 22:07
  • The issue is that the list view (on a left-side) needs to display the keys dictionary. While the table view on a right needs to display the clicked values. So the `data()` method should NOT return the same value for both listView and tableView. Since for listView it is the dictionary key. While for table view it is dictionary value retrieved using a dictionary key taken from left-view ListView widget. – alphanumeric Sep 17 '14 at 22:14
  • The list view and table view both have same column # 0 (zero). I end up assigning the same value to both list and table view to their column 0... – alphanumeric Sep 17 '14 at 22:17
  • See also qproxymodel – mdurant Sep 18 '14 at 13:31

1 Answers1

3

Done, by hiding the zeroth "key" column:

class DataModel(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.modelDict={}    
        self.names=[]    
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.names)   
    def columnCount(self, index=QtCore.QModelIndex()):
        return 4

    def data(self, index, role):
        if not index.isValid() or not (0<=index.row()<len(self.names)): return QtCore.QVariant()
        row,col = index.row(),index.column()
        if col==0:
            if role==QtCore.Qt.DisplayRole: 
                return self.names[row]
        else:
            if role==QtCore.Qt.DisplayRole: 
                return self.modelDict[self.names[row]][col]

    def addItem(self, itemName=None, column=0):
        totalItems=self.rowCount()+1
        self.beginInsertRows(QtCore.QModelIndex(), totalItems, column)
        if not itemName:            itemName='Item %s'%self.rowCount()
        self.names.append(itemName)
        self.endInsertRows()

    def buildItems(self):
        for key in self.modelDict:
            index=QtCore.QModelIndex()
            self.addItem(key) 

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

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

        self.dataModel=DataModel()
        self.dataModel.modelDict=elements
        self.dataModel.buildItems() 

        self.proxyModel=ProxyModel()
        self.proxyModel.setFilterKeyColumn(0)    
        self.proxyModel.setSourceModel(self.dataModel)

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

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

    def onClick(self):
        index=self.viewA.currentIndex()
        key=self.dataModel.data(index, QtCore.Qt.DisplayRole)  
        self.proxyModel.setFilterRegExp('%s'%key)
mdurant
  • 27,272
  • 5
  • 45
  • 74
  • ..if there are many different view-widgets in use... should I try to keep a single MODEL or it is OK to use many: a model per view-widget... Then all MODELS would share the same `global` variable to get the data from. As it is now with a single MODEL i am using a single class variable `self.modelDict` Should I instead declare this variable as `global`? If it would be global variable all MODELS could use it. each model could be customized to specific widget. Since all the models would share the same global variable a change to global variable will be picked up by all models – alphanumeric Sep 17 '14 at 22:57
  • It's certainly OK to have different models for different views, but with access to the same data; but likely not necessary in most situations. Any such shared data-structure would need to call modelChanged on each Qt model whenever it is edited, to get the displays to update. There are many ways to do this kind of thing, and simple methods like the one above will often get around having to use extra abstraction. – mdurant Sep 18 '14 at 13:30