1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2013 Riverbank Computing Limited
7## Copyright (C) 2010 Hans-Peter Jansen <hpj@urpla.net>.
8## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
9## All rights reserved.
10##
11## This file is part of the examples of PyQt.
12##
13## $QT_BEGIN_LICENSE:BSD$
14## You may use this file under the terms of the BSD license as follows:
15##
16## "Redistribution and use in source and binary forms, with or without
17## modification, are permitted provided that the following conditions are
18## met:
19##   * Redistributions of source code must retain the above copyright
20##     notice, this list of conditions and the following disclaimer.
21##   * Redistributions in binary form must reproduce the above copyright
22##     notice, this list of conditions and the following disclaimer in
23##     the documentation and/or other materials provided with the
24##     distribution.
25##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
26##     the names of its contributors may be used to endorse or promote
27##     products derived from this software without specific prior written
28##     permission.
29##
30## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
41## $QT_END_LICENSE$
42##
43#############################################################################
44
45
46from PyQt5.QtCore import (QDate, QDateTime, QRegExp, QSortFilterProxyModel, Qt,
47        QTime)
48from PyQt5.QtGui import QStandardItemModel
49from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QDateEdit,
50        QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit, QTreeView,
51        QVBoxLayout, QWidget)
52
53
54class MySortFilterProxyModel(QSortFilterProxyModel):
55    def __init__(self, parent=None):
56        super(MySortFilterProxyModel, self).__init__(parent)
57
58        self.minDate = QDate()
59        self.maxDate = QDate()
60
61    def setFilterMinimumDate(self, date):
62        self.minDate = date
63        self.invalidateFilter()
64
65    def filterMinimumDate(self):
66        return self.minDate
67
68    def setFilterMaximumDate(self, date):
69        self.maxDate = date
70        self.invalidateFilter()
71
72    def filterMaximumDate(self):
73        return self.maxDate
74
75    def filterAcceptsRow(self, sourceRow, sourceParent):
76        index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
77        index1 = self.sourceModel().index(sourceRow, 1, sourceParent)
78        index2 = self.sourceModel().index(sourceRow, 2, sourceParent)
79
80        return (   (self.filterRegExp().indexIn(self.sourceModel().data(index0)) >= 0
81                    or self.filterRegExp().indexIn(self.sourceModel().data(index1)) >= 0)
82                and self.dateInRange(self.sourceModel().data(index2)))
83
84    def lessThan(self, left, right):
85        leftData = self.sourceModel().data(left)
86        rightData = self.sourceModel().data(right)
87
88        if not isinstance(leftData, QDate):
89            emailPattern = QRegExp("([\\w\\.]*@[\\w\\.]*)")
90
91            if left.column() == 1 and emailPattern.indexIn(leftData) != -1:
92                leftData = emailPattern.cap(1)
93
94            if right.column() == 1 and emailPattern.indexIn(rightData) != -1:
95                rightData = emailPattern.cap(1)
96
97        return leftData < rightData
98
99    def dateInRange(self, date):
100        if isinstance(date, QDateTime):
101            date = date.date()
102
103        return (    (not self.minDate.isValid() or date >= self.minDate)
104                and (not self.maxDate.isValid() or date <= self.maxDate))
105
106
107class Window(QWidget):
108    def __init__(self):
109        super(Window, self).__init__()
110
111        self.proxyModel = MySortFilterProxyModel(self)
112        self.proxyModel.setDynamicSortFilter(True)
113
114        self.sourceView = QTreeView()
115        self.sourceView.setRootIsDecorated(False)
116        self.sourceView.setAlternatingRowColors(True)
117
118        sourceLayout = QHBoxLayout()
119        sourceLayout.addWidget(self.sourceView)
120        sourceGroupBox = QGroupBox("Original Model")
121        sourceGroupBox.setLayout(sourceLayout)
122
123        self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")
124        self.filterCaseSensitivityCheckBox.setChecked(True)
125        self.filterPatternLineEdit = QLineEdit()
126        self.filterPatternLineEdit.setText("Grace|Sports")
127        filterPatternLabel = QLabel("&Filter pattern:")
128        filterPatternLabel.setBuddy(self.filterPatternLineEdit)
129        self.filterSyntaxComboBox = QComboBox()
130        self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp)
131        self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
132        self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString)
133        self.fromDateEdit = QDateEdit()
134        self.fromDateEdit.setDate(QDate(2006, 12, 22))
135        self.fromDateEdit.setCalendarPopup(True)
136        fromLabel = QLabel("F&rom:")
137        fromLabel.setBuddy(self.fromDateEdit)
138        self.toDateEdit = QDateEdit()
139        self.toDateEdit.setDate(QDate(2007, 1, 5))
140        self.toDateEdit.setCalendarPopup(True)
141        toLabel = QLabel("&To:")
142        toLabel.setBuddy(self.toDateEdit)
143
144        self.filterPatternLineEdit.textChanged.connect(self.textFilterChanged)
145        self.filterSyntaxComboBox.currentIndexChanged.connect(self.textFilterChanged)
146        self.filterCaseSensitivityCheckBox.toggled.connect(self.textFilterChanged)
147        self.fromDateEdit.dateChanged.connect(self.dateFilterChanged)
148        self.toDateEdit.dateChanged.connect(self.dateFilterChanged)
149
150        self.proxyView = QTreeView()
151        self.proxyView.setRootIsDecorated(False)
152        self.proxyView.setAlternatingRowColors(True)
153        self.proxyView.setModel(self.proxyModel)
154        self.proxyView.setSortingEnabled(True)
155        self.proxyView.sortByColumn(1, Qt.AscendingOrder)
156
157        self.textFilterChanged()
158        self.dateFilterChanged()
159
160        proxyLayout = QGridLayout()
161        proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
162        proxyLayout.addWidget(filterPatternLabel, 1, 0)
163        proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1)
164        proxyLayout.addWidget(self.filterSyntaxComboBox, 1, 2)
165        proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 2, 0, 1, 3)
166        proxyLayout.addWidget(fromLabel, 3, 0)
167        proxyLayout.addWidget(self.fromDateEdit, 3, 1, 1, 2)
168        proxyLayout.addWidget(toLabel, 4, 0)
169        proxyLayout.addWidget(self.toDateEdit, 4, 1, 1, 2)
170        proxyGroupBox = QGroupBox("Sorted/Filtered Model")
171        proxyGroupBox.setLayout(proxyLayout)
172
173        mainLayout = QVBoxLayout()
174        mainLayout.addWidget(sourceGroupBox)
175        mainLayout.addWidget(proxyGroupBox)
176        self.setLayout(mainLayout)
177
178        self.setWindowTitle("Custom Sort/Filter Model")
179        self.resize(500, 450)
180
181    def setSourceModel(self, model):
182        self.proxyModel.setSourceModel(model)
183        self.sourceView.setModel(model)
184
185    def textFilterChanged(self):
186        syntax = QRegExp.PatternSyntax(
187            self.filterSyntaxComboBox.itemData(
188                self.filterSyntaxComboBox.currentIndex()))
189        caseSensitivity = (
190            self.filterCaseSensitivityCheckBox.isChecked()
191            and Qt.CaseSensitive or Qt.CaseInsensitive)
192        regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax)
193        self.proxyModel.setFilterRegExp(regExp)
194
195    def dateFilterChanged(self):
196        self.proxyModel.setFilterMinimumDate(self.fromDateEdit.date())
197        self.proxyModel.setFilterMaximumDate(self.toDateEdit.date())
198
199
200def addMail(model, subject, sender, date):
201    model.insertRow(0)
202    model.setData(model.index(0, 0), subject)
203    model.setData(model.index(0, 1), sender)
204    model.setData(model.index(0, 2), date)
205
206
207def createMailModel(parent):
208    model = QStandardItemModel(0, 3, parent)
209
210    model.setHeaderData(0, Qt.Horizontal, "Subject")
211    model.setHeaderData(1, Qt.Horizontal, "Sender")
212    model.setHeaderData(2, Qt.Horizontal, "Date")
213
214    addMail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
215            QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
216    addMail(model, "Radically new concept", "Grace K. <grace@software-inc.com>",
217            QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
218    addMail(model, "Accounts", "pascale@nospam.com",
219            QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
220    addMail(model, "Expenses", "Joe Bloggs <joe@bloggs.com>",
221            QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
222    addMail(model, "Re: Expenses", "Andy <andy@nospam.com>",
223            QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
224    addMail(model, "Re: Accounts", "Joe Bloggs <joe@bloggs.com>",
225            QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
226    addMail(model, "Re: Accounts", "Andy <andy@nospam.com>",
227            QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
228    addMail(model, "Sports", "Linda Smith <linda.smith@nospam.com>",
229            QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
230    addMail(model, "AW: Sports", "Rolf Newschweinstein <rolfn@nospam.com>",
231            QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
232    addMail(model, "RE: Sports", "Petra Schmidt <petras@nospam.com>",
233            QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
234
235    return model
236
237
238if __name__ == "__main__":
239
240    import sys
241
242    app = QApplication(sys.argv)
243
244    window = Window()
245    window.setSourceModel(createMailModel(window))
246    window.show()
247
248    sys.exit(app.exec_())
249