MasterDetail Example

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../../shared")

from DevMachines import __pyside2__, __pyside6__
from DevMachines import QtitanBase
from DevMachines.QtitanBase import Qtitan
from DevMachines.QtitanGrid import (getGridVersion, Grid, GridColumn, GridSummary, GridEditor,
                                    GridViewOptions,
                                    CellButtonClickEventArgs, ContextMenuEventArgs, PreviewRowArgs)

if __pyside2__:
    from PySide2 import QtCore
    from PySide2.QtCore import Qt, QFile, QTextStream, QModelIndex
    from PySide2.QtGui import QPixmap, QStandardItemModel
    from PySide2.QtWidgets import (QWidget, QApplication, QVBoxLayout, QHBoxLayout, QPushButton,
                                   QSlider, QLabel, QCheckBox, QComboBox, QMessageBox)

if __pyside6__:
    from PySide6 import QtCore
    from PySide6.QtCore import Qt, QFile, QTextStream, QModelIndex
    from PySide6.QtGui import QPixmap, QStandardItemModel
    from PySide6.QtWidgets import (QWidget, QApplication, QVBoxLayout, QHBoxLayout, QPushButton,
                                   QSlider, QLabel, QCheckBox, QComboBox, QMessageBox)

import MasterDetail_rc

from DemoMainWindow import DemoMainWindow

def simplified_strings(list):
    for index in range(0, len(list)):
        list[index] = list[index].strip()

class Window(DemoMainWindow):
    def __init__(self):
        DemoMainWindow.__init__(self, "QtitanDataGrid", getGridVersion())

        self.detailModelMap = dict()

        self.setWindowTitle(self.tr("QtitanDataGrid - Master-Detail Demo"))
        self.setGeometry(150, 150, 1000, 800)

        Grid.loadTranslation()

        self.grid = Grid()
        model = self.createMasterModel()

        # Configure grid view
        self.grid.setViewType(Grid.BandedTableView)
        view = self.grid.view()
        view.options().setModelDecoration(False)

        view.options().setPreviewRowText("Country Statistics")
        view.options().setRowAutoHeight(True)
        view.options().setPreviewRowEnabled(True)
        view.options().setPreviewRowHeight(200)

        view.bandedOptions().setBandsQuickCustomization(False)

        band = view.addBand("Aggregate and per capita GDP in Europe, 1870-2000")
        band.setTextAlignment(Qt.AlignCenter)

        # Connect Grid's context menu handler.
        self.connect(view, QtCore.SIGNAL("contextMenu(ContextMenuEventArgs*)"), self, QtCore.SLOT("contextMenu(ContextMenuEventArgs*)"))
        self.connect(view, QtCore.SIGNAL("previewRowChanged(PreviewRowArgs*)"), self, QtCore.SLOT("previewRowChanged(PreviewRowArgs*)"))

        view.setModel(model)
        column = view.getColumnByModelColumnName("Flag")
        column.setEditorType(GridEditor.Picture)
        column.editorRepository().setEditable(False)
        column.setMaxWidth(40)
        column.setMaxWidth(50)
        column.setFilterButtonVisible(False)
        column.setSortEnabled(False)

        column = view.getColumnByModelColumnName("Photo")
        column.setEditorType(GridEditor.Picture)
        column.editorRepository().setEditable(False)
        column.setFilterButtonVisible(False)
        column.setSortEnabled(False)

        self.setDemoWidget(self.grid, self.createSettingsWidget())

        view.bestFit()

    def createSettingsWidget(self):
        # Create settings widget
        settings = QWidget(self)
        l = QVBoxLayout(settings)
        autoWidthCheck = QCheckBox(settings)
        autoWidthCheck.setText("Column auto width")
        self.connect(autoWidthCheck, QtCore.SIGNAL("stateChanged(int)"), self, QtCore.SLOT("autoWidthStateChanged(int)"))
        l.addWidget(autoWidthCheck)
        autoWidthCheck.setChecked(True)

        fastScrollCheck = QCheckBox(settings)
        fastScrollCheck.setText("Fast scroll effect")
        self.connect(fastScrollCheck, QtCore.SIGNAL("stateChanged(int)"), self, QtCore.SLOT("fastScrollChanged(int)"))
        l.addWidget(fastScrollCheck)
        fastScrollCheck.setChecked(True)

        dottedLineCheck = QCheckBox(settings)
        dottedLineCheck.setText("Dotted grid line")
        self.connect(dottedLineCheck, QtCore.SIGNAL("stateChanged(int)"), self, QtCore.SLOT("dottedLineChanged(int)"))
        l.addWidget(dottedLineCheck)
        dottedLineCheck.setChecked(False)

        label = QLabel(self)
        hl = QHBoxLayout()
        label.setText("Grid line style:")
        lineStylesSelect = QComboBox(settings)

        lineStylesSelect.addItem("None")
        lineStylesSelect.addItem("Both")
        lineStylesSelect.addItem("Both2D")
        lineStylesSelect.addItem("Horizontal")
        lineStylesSelect.addItem("Horizontal2D")
        lineStylesSelect.addItem("Vertical")
        lineStylesSelect.addItem("Vertical2D")
        self.connect(lineStylesSelect, QtCore.SIGNAL("currentIndexChanged(int)"), self, QtCore.SLOT("selectGridLineStyles(int)"))
        hl.addWidget(label)
        hl.addWidget(lineStylesSelect)
        l.addLayout(hl)
        lineStylesSelect.setCurrentIndex(3)

        zoomEnable = QCheckBox(settings)
        zoomEnable.setText(self.tr("Zoom enabled"))
        zoomEnable.setChecked(True)
        self.connect(zoomEnable, QtCore.SIGNAL("stateChanged(int)"), self, QtCore.SLOT("zoomEnabledChanged(int)"))
        l.addWidget(zoomEnable)

        zoomIndicator = QCheckBox(settings)
        zoomIndicator.setText(self.tr("Show zoom indicator"))
        zoomIndicator.setChecked(True)
        self.connect(zoomIndicator, QtCore.SIGNAL("stateChanged(int)"), self, QtCore.SLOT("zoomIndicatorChanged(int)"))
        l.addWidget(zoomIndicator)

        zoomSlider = QSlider(settings)
        zoomSlider.setOrientation(Qt.Horizontal)
        zoomSlider.setTickPosition(QSlider.TicksBothSides)
        zoomSlider.setMinimum(25)
        zoomSlider.setMaximum(300)
        zoomSlider.setTickInterval(25)
        zoomSlider.setSingleStep(25)
        zoomSlider.setValue(100)
        self.connect(zoomSlider, QtCore.SIGNAL("sliderMoved(int)"), self, QtCore.SLOT("zoomValueChanged(int)"))
        self.connect(self.grid.view(), QtCore.SIGNAL("zoomChanged(int)"), zoomSlider, QtCore.SLOT("setValue(int)"))
        l.addWidget(zoomSlider)

        cellAutoRaise = QCheckBox(settings)
        cellAutoRaise.setText(self.tr("Auto raise cell button"))
        self.connect(cellAutoRaise, QtCore.SIGNAL("stateChanged(int)"), self, QtCore.SLOT("cellButtonAutoRaiseEnabled(int)"))
        cellAutoRaise.setChecked(True)
        l.addWidget(cellAutoRaise)

        previewExpandButtonLabel = QLabel(self)
        hl = QHBoxLayout()
        previewExpandButtonLabel.setText(self.tr("Preview Expand Style (new):"))
        previewExpandButtonComboBox = QComboBox(settings)
        self.connect(previewExpandButtonComboBox, QtCore.SIGNAL("currentIndexChanged(int)"), self,
            QtCore.SLOT("selectPreviewRowExpandStyle(int)"))
        previewExpandButtonComboBox.addItem(self.tr("Keep Expanded"))
        previewExpandButtonComboBox.addItem(self.tr("Text Button"))
        previewExpandButtonComboBox.addItem(self.tr("Button"))
        hl.addWidget(previewExpandButtonLabel)
        hl.addWidget(previewExpandButtonComboBox)
        l.addLayout(hl)
        previewExpandButtonComboBox.setCurrentIndex(2)

        transparentBox = QCheckBox(settings)
        transparentBox.setText(self.tr("Transparent Background"))
        self.connect(transparentBox, QtCore.SIGNAL("stateChanged(int)"), self,
            QtCore.SLOT("transparentBackgroundEnabled(int)"))
        transparentBox.setChecked(False)
        l.addWidget(transparentBox)

        printButton = QPushButton(settings)
        printButton.setText(self.tr("Print Preview"))
        self.connect(printButton, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("printPreview()"))
        l.addWidget(printButton)
        return settings

    @QtCore.Slot(int)
    def autoWidthStateChanged(self, state):
        view = self.grid.view()
        view.tableOptions().setColumnAutoWidth(Qt.CheckState(state) == Qt.Checked)

    @QtCore.Slot(int)
    def fastScrollChanged(self, state):
        view = self.grid.view()
        view.options().setFastScrollEffect(Qt.CheckState(state) == Qt.Checked)

    @QtCore.Slot(int)
    def dottedLineChanged(self, state):
        view = self.grid.view()
        pen = view.options().gridLinePen()
        if Qt.CheckState(state) == Qt.Checked:
            pen.setStyle(Qt.DotLine)
        else:
            pen.setStyle(Qt.SolidLine)
        view.options().setGridLinePen(pen)

    @QtCore.Slot(int)
    def selectGridLineStyles(self, index):
        view = self.grid.view()
        if index == 0:
            view.options().setGridLines(Qtitan.LinesNone)
        elif index == 1:
            view.options().setGridLines(Qtitan.LinesBoth)
        elif index == 2:
            view.options().setGridLines(Qtitan.LinesBoth2D)
        elif index == 3:
            view.options().setGridLines(Qtitan.LinesHorizontal)
        elif index == 4:
            view.options().setGridLines(Qtitan.LinesHorizontal2D)
        elif index == 5:
            view.options().setGridLines(Qtitan.LinesVertical)
        elif index == 6:
            view.options().setGridLines(Qtitan.LinesVertical2D)
        else:
            view.options().setGridLines(Qtitan.LinesBoth)

    @QtCore.Slot(int)
    def zoomEnabledChanged(self, state):
        view = self.grid.view()
        view.options().setZoomEnabled(Qt.CheckState(state) == Qt.Checked)

    @QtCore.Slot(int)
    def zoomIndicatorChanged(self, state):
        view = self.grid.view()
        view.options().setZoomIndicatorActive(Qt.CheckState(state) == Qt.Checked)

    @QtCore.Slot(int)
    def zoomValueChanged(self, value):
        factor = (float(value) / 25) * 25
        view = self.grid.view()
        view.options().setZoomFactor(factor / 100)

    @QtCore.Slot(int)
    def cellButtonAutoRaiseEnabled(self, state):
        view = self.grid.view()
        view.options().setCellButtonAutoRaise(Qt.CheckState(state) == Qt.Checked)

    @QtCore.Slot(int)
    def transparentBackgroundEnabled(self, state):
        view = self.grid.view()
        view.options().setTransparentBackground(Qt.CheckState(state) == Qt.Checked)
        view.options().setAlternatingRowColors(not view.options().alternatingRowColors())

    @QtCore.Slot(ContextMenuEventArgs)
    def contextMenu(self, args):
        args.contextMenu().addAction("Print Preview", self, QtCore.SLOT("printPreview()"))
        args.contextMenu().addSeparator()
        args.contextMenu().addAction("Developer Machines on the Web", self, QtCore.SLOT("showCompanyWebSite()"))

    @QtCore.Slot(int)
    def selectPreviewRowExpandStyle(self, index):
        view = self.grid.view()
        if index == 0:
            view.options().setPreviewRowExpandStyle(GridViewOptions.PreviewKeepExpanded)
        elif index == 1:
            view.options().setPreviewRowExpandStyle(GridViewOptions.PreviewExpandTextButton)
        else:
            view.options().setPreviewRowExpandStyle(GridViewOptions.PreviewExpandButton)

    @QtCore.Slot(CellButtonClickEventArgs)
    def cellButtonClicked(self, args):
        QMessageBox.information(self, "Cell button clicked",
            "Clicked: Button - " + str(args.buttonIndex()) + ", Column Title - " + args.column().caption() + ", RowIndex - " + str(args.row().rowIndex()))

    @QtCore.Slot()
    def printPreview(self):
        self.grid.view().printPreview()

    def setShadeColor(self, color):
        self.grid.themeManager().setShadeColor(color)

    @QtCore.Slot(PreviewRowArgs)
    def previewRowChanged(self, args):
        country = args.index().data()
        if not country in self.detailModelMap:
            return
        model = self.detailModelMap[country]

        detailGrid = args.widget()
        if detailGrid == None or not detailGrid.inherits("Qtitan::Grid"):
            detailGrid = Grid()
            detailGrid.setAttribute(Qt.WA_NoSystemBackground, True)
            detailGrid.setViewType(Grid.TableView)

        view = detailGrid.view()
        view.setModel(model)
        args.setWidget(detailGrid)
        args.setHandled(True)

    def addDetailModel(self, country, gdp, population, gdpPerCapita):
        model = QStandardItemModel(0, 4, self)
        model.setHeaderData(0, Qt.Horizontal, self.tr("Year"))
        model.setHeaderData(1, Qt.Horizontal, self.tr("GDP ($ international prices)"))
        model.setHeaderData(2, Qt.Horizontal, self.tr("Population (Millions)"))
        model.setHeaderData(3, Qt.Horizontal, self.tr("GDP Per Capita ($ international prices)"))

        self.detailModelMap[country] = model

        gdp_data = gdp.split(",")
        simplified_strings(gdp_data)
        population_data = population.split(",")
        simplified_strings(population_data)
        gdpPerCapita_data = gdpPerCapita.split(",")
        simplified_strings(gdpPerCapita_data)

        years = ["1870", "1890", "1913", "1929", "1937", "1950", "1973", "1990", "2000"]
        for row in range(0, len(years)):
            model.insertRows(row, 1)
            index = model.index(row, 0)
            model.setData(index, years[row])
            index = model.index(row, 1)
            model.setData(index, gdp_data[row + 1])
            index = model.index(row, 2)
            model.setData(index, population_data[row + 1])
            index = model.index(row, 3)
            model.setData(index, gdpPerCapita_data[row + 1])

    def createMasterModel(self):
        model = QStandardItemModel(0, 7, self)
        model.setHeaderData(0, Qt.Horizontal, self.tr("Country"))
        model.setHeaderData(1, Qt.Horizontal, self.tr("Capital"))
        model.setHeaderData(2, Qt.Horizontal, self.tr("Language"))
        model.setHeaderData(3, Qt.Horizontal, self.tr("Flag"))
        model.setHeaderData(4, Qt.Horizontal, self.tr("Part"))
        model.setHeaderData(5, Qt.Horizontal, self.tr("Photo"))
        model.setHeaderData(6, Qt.Horizontal, self.tr("Info"))
        countriesFile = QFile(":/Countries_in_Europe.data")
        gdpFile = QFile(":/GDP_in_Europe.data")
        populationFile = QFile(":/Population_in_Europe.data")
        gdpPerCapitaFile = QFile(":/Per_capita_GDP_in_Europe.data")

        if not countriesFile.open(QFile.ReadOnly | QFile.Text):
            return None
        if not gdpFile.open(QFile.ReadOnly | QFile.Text):
            return None
        if not populationFile.open(QFile.ReadOnly | QFile.Text):
            return None
        if not gdpPerCapitaFile.open(QFile.ReadOnly | QFile.Text):
            return None

        countriesStream = QTextStream(countriesFile)
        gdpStream = QTextStream(gdpFile)
        populationStream = QTextStream(populationFile)
        gdpPerCapitaStream = QTextStream(gdpPerCapitaFile)

        row = 0
        countriesLine = countriesStream.readLine()
        gdpLine = gdpStream.readLine()
        populationLine = populationStream.readLine()
        gdpPerCapitaLine = gdpPerCapitaStream.readLine()

        while countriesLine != "":
            if countriesLine[0] != '#':
                model.insertRows(row, 1, QModelIndex())

                columns = countriesLine.split(",")
                simplified_strings(columns)
                country = columns[0]
                resname = country
                resname.replace(" ", "")

                self.addDetailModel(country, gdpLine, populationLine, gdpPerCapitaLine)

                index = model.index(row, 0, QModelIndex())
                model.setData(index, country)

                index = model.index(row, 1, QModelIndex())
                model.setData(index, columns[1])

                index = model.index(row, 2, QModelIndex())
                model.setData(index, columns[2])

                index = model.index(row, 3, QModelIndex())

                flag = QPixmap(":/res/Flag_of_" + resname + ".png")
                if not flag.isNull():
                    model.setData(index, flag)

                index = model.index(row, 4, QModelIndex())
                model.setData(index, columns[3])

                index = model.index(row, 5, QModelIndex())
                photo = QPixmap(":/res/" + resname + ".jpg")
                if not photo.isNull():
                   model.setData(index, photo)

                index = model.index(row, 6, QModelIndex())
                model.setData(index, "-")
                row = row + 1

            countriesLine = countriesStream.readLine()
            gdpLine = gdpStream.readLine()
            gdpPerCapitaLine = gdpPerCapitaStream.readLine()
            populationLine = populationStream.readLine()

        return model

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