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