WebBrowser/History/HistoryFilterModel.py

branch
QtWebEngine
changeset 4734
ce0b1f024da9
parent 4631
5c1a96925da4
child 5389
9b1c800daff3
equal deleted inserted replaced
4733:ae291a307ea6 4734:ce0b1f024da9
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the history filter model.
8 """
9
10 from __future__ import unicode_literals
11
12 from PyQt5.QtCore import Qt, QDateTime, QModelIndex, QAbstractProxyModel
13
14 from .HistoryModel import HistoryModel
15
16
17 class HistoryData(object):
18 """
19 Class storing some history data.
20 """
21 def __init__(self, offset, frequency=0):
22 """
23 Constructor
24
25 @param offset tail offset (integer)
26 @param frequency frequency (integer)
27 """
28 self.tailOffset = offset
29 self.frequency = frequency
30
31 def __eq__(self, other):
32 """
33 Special method implementing equality.
34
35 @param other reference to the object to check against (HistoryData)
36 @return flag indicating equality (boolean)
37 """
38 return self.tailOffset == other.tailOffset and \
39 (self.frequency == -1 or other.frequency == -1 or
40 self.frequency == other.frequency)
41
42 def __lt__(self, other):
43 """
44 Special method determining less relation.
45
46 Note: Like the actual history entries the index mapping is sorted in
47 reverse order by offset
48
49 @param other reference to the history data object to compare against
50 (HistoryEntry)
51 @return flag indicating less (boolean)
52 """
53 return self.tailOffset > other.tailOffset
54
55
56 class HistoryFilterModel(QAbstractProxyModel):
57 """
58 Class implementing the history filter model.
59 """
60 FrequencyRole = HistoryModel.MaxRole + 1
61 MaxRole = FrequencyRole
62
63 def __init__(self, sourceModel, parent=None):
64 """
65 Constructor
66
67 @param sourceModel reference to the source model (QAbstractItemModel)
68 @param parent reference to the parent object (QObject)
69 """
70 super(HistoryFilterModel, self).__init__(parent)
71
72 self.__loaded = False
73 self.__filteredRows = []
74 self.__historyDict = {}
75 self.__scaleTime = QDateTime()
76
77 self.setSourceModel(sourceModel)
78
79 def historyContains(self, url):
80 """
81 Public method to check the history for an entry.
82
83 @param url URL to check for (string)
84 @return flag indicating success (boolean)
85 """
86 self.__load()
87 return url in self.__historyDict
88
89 def historyLocation(self, url):
90 """
91 Public method to get the row number of an entry in the source model.
92
93 @param url URL to check for (tring)
94 @return row number in the source model (integer)
95 """
96 self.__load()
97 if url not in self.__historyDict:
98 return 0
99
100 return self.sourceModel().rowCount() - self.__historyDict[url]
101
102 def data(self, index, role=Qt.DisplayRole):
103 """
104 Public method to get data from the model.
105
106 @param index index of history entry to get data for (QModelIndex)
107 @param role data role (integer)
108 @return history entry data
109 """
110 if role == self.FrequencyRole and index.isValid():
111 return self.__filteredRows[index.row()].frequency
112
113 return QAbstractProxyModel.data(self, index, role)
114
115 def setSourceModel(self, sourceModel):
116 """
117 Public method to set the source model.
118
119 @param sourceModel reference to the source model (QAbstractItemModel)
120 """
121 if self.sourceModel() is not None:
122 self.sourceModel().modelReset.disconnect(self.__sourceReset)
123 self.sourceModel().dataChanged.disconnect(self.__sourceDataChanged)
124 self.sourceModel().rowsInserted.disconnect(
125 self.__sourceRowsInserted)
126 self.sourceModel().rowsRemoved.disconnect(self.__sourceRowsRemoved)
127
128 super(HistoryFilterModel, self).setSourceModel(sourceModel)
129
130 if self.sourceModel() is not None:
131 self.__loaded = False
132 self.sourceModel().modelReset.connect(self.__sourceReset)
133 self.sourceModel().dataChanged.connect(self.__sourceDataChanged)
134 self.sourceModel().rowsInserted.connect(self.__sourceRowsInserted)
135 self.sourceModel().rowsRemoved.connect(self.__sourceRowsRemoved)
136
137 def __sourceDataChanged(self, topLeft, bottomRight):
138 """
139 Private slot to handle the change of data of the source model.
140
141 @param topLeft index of top left data element (QModelIndex)
142 @param bottomRight index of bottom right data element (QModelIndex)
143 """
144 self.dataChanged.emit(
145 self.mapFromSource(topLeft), self.mapFromSource(bottomRight))
146
147 def headerData(self, section, orientation, role=Qt.DisplayRole):
148 """
149 Public method to get the header data.
150
151 @param section section number (integer)
152 @param orientation header orientation (Qt.Orientation)
153 @param role data role (integer)
154 @return header data
155 """
156 return self.sourceModel().headerData(section, orientation, role)
157
158 def recalculateFrequencies(self):
159 """
160 Public method to recalculate the frequencies.
161 """
162 self.__sourceReset()
163
164 def __sourceReset(self):
165 """
166 Private slot to handle a reset of the source model.
167 """
168 self.beginResetModel()
169 self.__loaded = False
170 self.endResetModel()
171
172 def rowCount(self, parent=QModelIndex()):
173 """
174 Public method to determine the number of rows.
175
176 @param parent index of parent (QModelIndex)
177 @return number of rows (integer)
178 """
179 self.__load()
180 if parent.isValid():
181 return 0
182 return len(self.__historyDict)
183
184 def columnCount(self, parent=QModelIndex()):
185 """
186 Public method to get the number of columns.
187
188 @param parent index of parent (QModelIndex)
189 @return number of columns (integer)
190 """
191 return self.sourceModel().columnCount(self.mapToSource(parent))
192
193 def mapToSource(self, proxyIndex):
194 """
195 Public method to map an index to the source model index.
196
197 @param proxyIndex reference to a proxy model index (QModelIndex)
198 @return source model index (QModelIndex)
199 """
200 self.__load()
201 sourceRow = self.sourceModel().rowCount() - proxyIndex.internalId()
202 return self.sourceModel().index(sourceRow, proxyIndex.column())
203
204 def mapFromSource(self, sourceIndex):
205 """
206 Public method to map an index to the proxy model index.
207
208 @param sourceIndex reference to a source model index (QModelIndex)
209 @return proxy model index (QModelIndex)
210 """
211 self.__load()
212 url = sourceIndex.data(HistoryModel.UrlStringRole)
213 if url not in self.__historyDict:
214 return QModelIndex()
215
216 sourceOffset = self.sourceModel().rowCount() - sourceIndex.row()
217
218 try:
219 row = self.__filteredRows.index(HistoryData(sourceOffset, -1))
220 except ValueError:
221 return QModelIndex()
222
223 return self.createIndex(row, sourceIndex.column(), sourceOffset)
224
225 def index(self, row, column, parent=QModelIndex()):
226 """
227 Public method to create an index.
228
229 @param row row number for the index (integer)
230 @param column column number for the index (integer)
231 @param parent index of the parent item (QModelIndex)
232 @return requested index (QModelIndex)
233 """
234 self.__load()
235 if row < 0 or row >= self.rowCount(parent) or \
236 column < 0 or column >= self.columnCount(parent):
237 return QModelIndex()
238
239 return self.createIndex(row, column,
240 self.__filteredRows[row].tailOffset)
241
242 def parent(self, index):
243 """
244 Public method to get the parent index.
245
246 @param index index of item to get parent (QModelIndex)
247 @return index of parent (QModelIndex)
248 """
249 return QModelIndex()
250
251 def __load(self):
252 """
253 Private method to load the model data.
254 """
255 if self.__loaded:
256 return
257
258 self.__filteredRows = []
259 self.__historyDict = {}
260 self.__scaleTime = QDateTime.currentDateTime()
261
262 for sourceRow in range(self.sourceModel().rowCount()):
263 idx = self.sourceModel().index(sourceRow, 0)
264 url = idx.data(HistoryModel.UrlStringRole)
265 if url not in self.__historyDict:
266 sourceOffset = self.sourceModel().rowCount() - sourceRow
267 self.__filteredRows.append(
268 HistoryData(sourceOffset, self.__frequencyScore(idx)))
269 self.__historyDict[url] = sourceOffset
270 else:
271 # the url is known already, so just update the frequency score
272 row = self.__filteredRows.index(
273 HistoryData(self.__historyDict[url], -1))
274 self.__filteredRows[row].frequency += \
275 self.__frequencyScore(idx)
276
277 self.__loaded = True
278
279 def __sourceRowsInserted(self, parent, start, end):
280 """
281 Private slot to handle the insertion of data in the source model.
282
283 @param parent reference to the parent index (QModelIndex)
284 @param start start row (integer)
285 @param end end row (integer)
286 """
287 if start == end and start == 0:
288 if not self.__loaded:
289 return
290
291 idx = self.sourceModel().index(start, 0, parent)
292 url = idx.data(HistoryModel.UrlStringRole)
293 currentFrequency = 0
294 if url in self.__historyDict:
295 row = self.__filteredRows.index(
296 HistoryData(self.__historyDict[url], -1))
297 currentFrequency = self.__filteredRows[row].frequency
298 self.beginRemoveRows(QModelIndex(), row, row)
299 del self.__filteredRows[row]
300 del self.__historyDict[url]
301 self.endRemoveRows()
302
303 self.beginInsertRows(QModelIndex(), 0, 0)
304 self.__filteredRows.insert(
305 0, HistoryData(
306 self.sourceModel().rowCount(),
307 self.__frequencyScore(idx) + currentFrequency))
308 self.__historyDict[url] = self.sourceModel().rowCount()
309 self.endInsertRows()
310
311 def __sourceRowsRemoved(self, parent, start, end):
312 """
313 Private slot to handle the removal of data in the source model.
314
315 @param parent reference to the parent index (QModelIndex)
316 @param start start row (integer)
317 @param end end row (integer)
318 """
319 self.__sourceReset()
320
321 def removeRows(self, row, count, parent=QModelIndex()):
322 """
323 Public method to remove entries from the model.
324
325 @param row row of the first entry to remove (integer)
326 @param count number of entries to remove (integer)
327 @param parent index of the parent entry (QModelIndex)
328 @return flag indicating successful removal (boolean)
329 """
330 if row < 0 or \
331 count <= 0 or \
332 row + count > self.rowCount(parent) or \
333 parent.isValid():
334 return False
335
336 lastRow = row + count - 1
337 self.sourceModel().rowsRemoved.disconnect(self.__sourceRowsRemoved)
338 self.beginRemoveRows(parent, row, lastRow)
339 oldCount = self.rowCount()
340 start = self.sourceModel().rowCount() - \
341 self.__filteredRows[row].tailOffset
342 end = self.sourceModel().rowCount() - \
343 self.__filteredRows[lastRow].tailOffset
344 self.sourceModel().removeRows(start, end - start + 1)
345 self.endRemoveRows()
346 self.sourceModel().rowsRemoved.connect(self.__sourceRowsRemoved)
347 self.__loaded = False
348 if oldCount - count != self.rowCount():
349 self.beginResetModel()
350 self.endResetModel()
351 return True
352
353 def __frequencyScore(self, sourceIndex):
354 """
355 Private method to calculate the frequency score.
356
357 @param sourceIndex index of the source model (QModelIndex)
358 @return frequency score (integer)
359 """
360 loadTime = \
361 self.sourceModel().data(sourceIndex, HistoryModel.DateTimeRole)
362 days = loadTime.daysTo(self.__scaleTime)
363
364 if days <= 1:
365 return 100
366 elif days < 8: # within the last week
367 return 90
368 elif days < 15: # within the last two weeks
369 return 70
370 elif days < 31: # within the last month
371 return 50
372 elif days < 91: # within the last 3 months
373 return 30
374 else:
375 return 10

eric ide

mercurial