[PyQt] Master-Detail model advice: link a QListView to a QTableView

Robert Kent rob at gulon.co.uk
Mon Nov 11 12:11:47 GMT 2013


Hi Dan,

The best way to do this IMHO is to use a tree model to hold your data and
then use the view classes you suggest to view different parts of it. The
QListView would hold the top level tree items and the QTableView would be
set to view its child items. The following example does just that including
implementing a simple tree model (based on the Simple Tree Model Example
<http://qt-project.org/doc/qt-4.8/itemviews-simpletreemodel.html> ):

import sip
sip.setapi('QVariant',2)
sip.setapi('QString',2)

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class TreeItem(object):
    def __init__(self, data, parent=None):
        self._childItems=[]
        self._itemData=data
        self._parentItem=parent

    def appendChild(self, child): self._childItems.append(child)

    def child(self, row): return self._childItems[row]

    def childCount(self): return len(self._childItems)

    def columnCount(self): return len(self._itemData)

    def data(self, column): return self._itemData[column]

    def row(self):
        if self._parentItem: return self._parentItem._childItems.index(self)
        return 0

    def parent(self): return self._parentItem

class TreeModel(QAbstractItemModel):
    def __init__(self, data, parent=None):
        QAbstractItemModel.__init__(self, parent)

        self._rootItem=TreeItem(['',''])
        self.setupModelData(data, self._rootItem)

    def data(self, index, role):
        if not index.isValid() or role!=Qt.DisplayRole: return None
        return index.internalPointer().data(index.column())

    def flags(self, index):
        if not index.isValid(): return 0
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable

    def headerData(self, section, orientation, role):
        if orientation==Qt.Horizontal and role==Qt.DisplayRole: return
self._rootItem.data(section)
        return None

    def index(self, row, column, parent=QModelIndex()):
        if not self.hasIndex(row, column, parent): return QModelIndex()

        if not parent.isValid(): parentItem=self._rootItem
        else: parentItem=parent.internalPointer()

        childItem=parentItem.child(row)
        if childItem: return self.createIndex(row, column, childItem)

        return QModelIndex()

    def parent(self, index):
        if not index.isValid(): return QModelIndex()

        parentItem=index.internalPointer().parent()
        if parentItem==self._rootItem: return QModelIndex()

        return self.createIndex(parentItem.row(), 0, parentItem)

    def rowCount(self, parent=QModelIndex()):
        if parent.column()>0: return 0
        if not parent.isValid(): parentItem=self._rootItem
        else: parentItem=parent.internalPointer()
        return parentItem.childCount()

    def columnCount(self, parent=QModelIndex()):
        if parent.isValid(): return parent.internalPointer().columnCount()
        return self._rootItem.columnCount()

    def setupModelData(self, data, parent):
        for category, details in data.iteritems():
            parent.appendChild(TreeItem([category, ''], parent))

            categoryItem=parent.child(parent.childCount()-1)
            for k,v in details.iteritems():
                categoryItem.appendChild(TreeItem([k,v], categoryItem))

if __name__=="__main__":
    from sys import argv, exit

    class Widget(QWidget):
        def __init__(self, parent=None, **kwargs):
            QWidget.__init__(self, parent, **kwargs)

            self.setWindowTitle("Simple Tree Model")

            l=QHBoxLayout(self)

            data={
                "Category 1": {
                    "Key 1.1": "Value 1.1",
                    "Key 1.2": "Value 1.2",
                    "Key 1.3": "Value 1.3",
                },
                "Category 2": {
                    "Key 2.1": "Value 2.1",
                    "Key 2.2": "Value 2.2",
                    "Key 2.3": "Value 2.3",
                },
                "Category 3": {
                    "Key 3.1": "Value 3.1",
                    "Key 3.2": "Value 3.2",
                    "Key 3.3": "Value 3.3",
                }
            }

            self._model=TreeModel(data)

            self._lview=QListView(self)
            self._lview.setModel(self._model)
            l.addWidget(self._lview)

            self._tview=QTableView(self)
            l.addWidget(self._tview)

            self._lview.clicked.connect(self.indexClicked)

        @pyqtSlot(QModelIndex)
        def indexClicked(self, index):
            if not self._tview.model(): self._tview.setModel(self._model)
            self._tview.setRootIndex(index)

    a=QApplication(argv)
    w=Widget()
    w.show()
    w.raise_()
    exit(a.exec_())

I hope this helps,

Rob

From:  "Dan F." <danfobble at gmail.com>
Date:  Monday, 11 November 2013 02:07
To:  <pyqt at riverbankcomputing.com>
Subject:  [PyQt] Master-Detail model advice: link a QListView to a
QTableView

I'm brand new to PyQT and would like some general advice (and class/method
suggestions) on the appropriate way to implement a
Master-and-Detail/List-and-Table data browser.

The QListView presents a list of entities, and the QTableView presents a
table of key-value pairs for the particular entity currently selected in the
QListView.

Surely there's some example code out there for this...?

Thanks.

_______________________________________________ PyQt mailing list
PyQt at riverbankcomputing.com
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20131111/fc7df763/attachment.html>


More information about the PyQt mailing list