CustomFilter 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, GridEditor,
                                    GridFilter,
                                    GridFilterCondition,
                                    GridFilterGroupCondition,
                                    GridViewOptions,
                                    ContextMenuEventArgs)

if __pyside2__:
    from PySide2 import QtCore
    from PySide2.QtCore import Qt, QTime, QDate, QAbstractItemModel, QModelIndex, QXmlStreamWriter, QXmlStreamReader
    from PySide2.QtWidgets import (QWidget, QApplication, QVBoxLayout, QHBoxLayout, QPushButton,
                                   QSlider, QLabel, QCheckBox, QComboBox, QMessageBox, QFileDialog)

if __pyside6__:
    from PySide6 import QtCore
    from PySide6.QtCore import Qt, QTime, QDate, QAbstractItemModel, QModelIndex, QXmlStreamWriter, QXmlStreamReader
    from PySide6.QtWidgets import (QWidget, QApplication, QVBoxLayout, QHBoxLayout, QPushButton,
                                   QSlider, QLabel, QCheckBox, QComboBox, QMessageBox, QFileDialog)

from DemoMainWindow import DemoMainWindow

class DataItem:
    v0 = None
    v1 = None
    v2 = None
    v3 = None

class CustomFilterModel(QAbstractItemModel):
    def __init__(self, parent):
        QAbstractItemModel.__init__(self, parent)

        self.values = list(range(0, 200))
        y = 2012
        m = 1
        d = 1
        for i in self.values:
            item = DataItem()
            item.v0 = i
            item.v1 = "String = " + str(i)
            item.v3 = QTime(12, 0, 0)
            if d > 28:
                d = 1
                m = m + 1
                if m > 12:
                    m = 1
                    y = y + 1

            item.v2 = QDate(y, m, d)
            if not ((i + 1) % 10):
                d = d + 1
            self.values[i] = item

    def headerData(self, section, orientation, role):
        if section == 0:
            return "Integer"
        elif section == 1:
            return "String"
        elif section == 2:
            return "Date"
        elif section == 3:
            return "Time"
        return None

    def parent(self, child):
        return QModelIndex()

    def hasChildren(self, parent):
        if parent.model() == self or not parent.isValid():
            return False
        return False

    def rowCount(self, parent):
        if parent.isValid():
            return 0
        return len(self.values)

    def columnCount(self, parent):
        if parent.isValid():
            return 0
        return 4

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

        if row < 0 or row >= self.rowCount(parent):
            return QModelIndex()

        if column < 0 or column >= self.columnCount(parent):
            return QModelIndex()

        return self.createIndex(row, column, parent)

    def data(self, index, role):
        if not index.isValid():
            return None

        if index.row() < 0 or index.row() >= self.rowCount(index.parent()):
            return None

        if index.column() < 0 or index.column() >= self.columnCount(index.parent()):
            return None

        if role == Qt.DisplayRole or role == Qt.EditRole:
            if index.column() == 0:
                return self.values[index.row()].v0
            elif index.column() == 1:
                return self.values[index.row()].v1
            elif index.column() == 2:
                return self.values[index.row()].v2
            elif index.column() == 3:
                return self.values[index.row()].v3
        elif role == Qt.CheckStateRole:
            if index.column() == 0:
                return self.values[index.row()].v0
        return None

    def setData(self, index, value, role):
        if not index.isValid():
            return False

        if index.row() < 0 or index.row() >= self.rowCount(index.parent()):
            return False

        if index.column() < 0 or index.column() >= self.columnCount(index.parent()):
            return False

        if role != Qt.EditRole:
            return False

        if index.column() == 0:
            self.values[index.row()].v0 = int(value)
        elif index.column() == 1:
            self.values[index.row()].v1 = str(value)
        elif index.column() == 2:
            self.values[index.row()].v2 = value
        elif index.column() == 3:
            self.values[index.row()].v3 = value

        self.dataChanged.emit(index, index)
        return True

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

class CustomFilterCondition(GridFilterCondition):
    xml_name = "CustomCondition"
    def __init__(self, _filter = None):
        GridFilterCondition.__init__(self, _filter)
        self.modelRows = set()

    def isTrue(self, index):
        return index.row() in self.modelRows

    def clone(self):
        retval = CustomFilterCondition(self.filter())
        retval.modelRows = self.modelRows.copy()
        return retval

    def createPresentation(self):
        retval = "custom filter: row in (";
        modelRowsList = list(self.modelRows)
        sorted(modelRowsList)
        startvar = True
        for row in modelRowsList:
            if not startvar:
                retval = retval + ", "
            retval = retval + str(row)
            startvar = False

        retval = retval + ")"
        return retval

    def conditionCount(self):
        return len(self.modelRows)

    def saveToXML(self, xmlwriter):
        xmlwriter.writeStartElement("Qtitan:" + CustomFilterCondition.xml_name)
        for row in self.modelRows:
            xmlwriter.writeStartElement("Qtitan:Row")
            xmlwriter.writeAttribute("index", str(row))
            xmlwriter.writeEndElement()
        xmlwriter.writeEndElement()
        return True

    def loadFromXML(self, xmlreader):
        self.modelRows.clear()
        if (xmlreader.tokenType() != QXmlStreamReader.StartElement or
            xmlreader.qualifiedName() != "Qtitan:" + CustomFilterCondition.xml_name):
            return False
        while xmlreader.readNextStartElement():
            if xmlreader.qualifiedName() != "Qtitan:Row":
                return False
            attrs = xmlreader.attributes()
            if attrs.hasAttribute("index"):
                self.modelRows.add(int(attrs.value("index")))
            xmlreader.skipCurrentElement()
        return True

    def addRow(self, modelRowIndex):
        self.modelRows.add(modelRowIndex)

    def removeRow(self, modelRowIndex):
        self.modelRows.discard(modelRowIndex)

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

        self.setWindowTitle(self.tr("Custom Filters"))
        self.setGeometry(50, 50, 800, 500)
        self.setMinimumHeight(10)

        Grid.loadTranslation()

        self.grid = Grid()
        model = CustomFilterModel(self.grid)
        # qRegisterMetaType<CustomFilterCondition>(CustomFilterCondition.xml_name)
        # For Python we use special method to specify the factory for conditions.
        GridFilter.addFactory(CustomFilterCondition.xml_name, CustomFilterCondition())

        # Configure grid view
        self.grid.setViewType(Grid.TableView)
        view = self.grid.view()
        view.options().setGridLineWidth(0)
        view.options().setSelectionPolicy(GridViewOptions.MultiRowSelection)
        view.options().setFastScrollEffect(True)
        view.tableOptions().setColumnAutoWidth(True)

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

        column = view.getColumn(0)
        column.setEditorType(GridEditor.Numeric)
        column.editorRepository().setMinimum(-10000)
        column.editorRepository().setMaximum(10000)
        column.editorRepository().setEditorActivationPolicy(
            GridEditor.ActivationPolicy(GridEditor.ActivateByDblClick | GridEditor.ActivateByEditPress))

        column = view.getColumn(1)
        column.setEditorType(GridEditor.String)
        column.editorRepository().setValidateOnEnter(False)
        column.editorRepository().setEditorActivationPolicy(
            GridEditor.ActivationPolicy(GridEditor.ActivateByDblClick | GridEditor.ActivateByEditPress))

        column = view.getColumn(2)
        column.setEditorType(GridEditor.Date)
        column.editorRepository().setAutoSelect(True)
        column.editorRepository().setEditorActivationPolicy(
            GridEditor.ActivationPolicy(GridEditor.ActivateByDblClick | GridEditor.ActivateByEditPress))

        column = view.getColumn(3)
        column.setEditorType(GridEditor.Time)
        column.editorRepository().setAutoSelect(True)
        column.editorRepository().setEditorActivationPolicy(
            GridEditor.ActivationPolicy(GridEditor.ActivateByDblClick | GridEditor.ActivateByEditPress))

        # Create settings widget
        settings = QWidget(self)
        l = QVBoxLayout(settings)

        text = QLabel(settings)
        text.setText("Select several rows and press 'Set Filter' button to apply filter to these rows.")
        text.setWordWrap(True)
        l.addWidget(text)

        filterButton = QPushButton(settings)
        filterButton.setText(self.tr("Set Filter"))
        self.connect(filterButton, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("applyFilter()"))
        l.addWidget(filterButton)

        hblayout = QHBoxLayout()
        saveButton = QPushButton(settings)
        saveButton.setText(self.tr("Save Filter (new)"))
        self.connect(saveButton, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("saveFilter()"))
        hblayout.addWidget(saveButton)
        loadButton = QPushButton(settings)
        loadButton.setText(self.tr("Load Filter (new)"))
        self.connect(loadButton, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("loadFilter()"))
        hblayout.addWidget(loadButton)
        l.addLayout(hblayout)

        self.setDemoWidget(self.grid, settings)

    @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()"))

    def applyFilter(self):
        view = self.grid.view()
        groupCondition = GridFilterGroupCondition(view.filter())
        condition = CustomFilterCondition(view.filter())
        groupCondition.addCondition(condition)

        selection = view.selection()
        while not selection.end():
            row = selection.row()
            condition.addRow(row.modelIndex().row())
            selection.next()

        view.filter().setCondition(groupCondition, True)
        view.filter().setActive(True)
        view.showFilterPanel()

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

    def saveFilter(self):
        fileName = QFileDialog.getSaveFileName(self, self.tr("Save QtitanDataGrid Filter to File"),
            "qtitan-filter.xml", self.tr("Filter-XML (*.xml)"))[0]
        if fileName == "":
            return
        view = self.grid.view()
        view.filter().saveToFile(fileName)

    def loadFilter(self):
        fileName = QFileDialog.getOpenFileName(self, self.tr("Open QtitanDataGrid Filter File"), "",
                                               self.tr("Filter-XML (*.xml)"))[0]
        if not fileName:
            return
        view = self.grid.view()
        view.filter().loadFromFile(fileName)

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