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