eric6/Helpviewer/History/HistoryFilterModel.py

branch
maintenance
changeset 7286
7eb04391adf7
parent 7226
babe80d84a3e
parent 7285
1ff497f33f31
child 7287
1c17f2191bdd
equal deleted inserted replaced
7226:babe80d84a3e 7286:7eb04391adf7
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2019 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=None):
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 if parent is None:
180 parent = QModelIndex()
181
182 self.__load()
183 if parent.isValid():
184 return 0
185 return len(self.__historyDict)
186
187 def columnCount(self, parent=None):
188 """
189 Public method to get the number of columns.
190
191 @param parent index of parent (QModelIndex)
192 @return number of columns (integer)
193 """
194 if parent is None:
195 parent = QModelIndex()
196
197 return self.sourceModel().columnCount(self.mapToSource(parent))
198
199 def mapToSource(self, proxyIndex):
200 """
201 Public method to map an index to the source model index.
202
203 @param proxyIndex reference to a proxy model index (QModelIndex)
204 @return source model index (QModelIndex)
205 """
206 self.__load()
207 sourceRow = self.sourceModel().rowCount() - proxyIndex.internalId()
208 return self.sourceModel().index(sourceRow, proxyIndex.column())
209
210 def mapFromSource(self, sourceIndex):
211 """
212 Public method to map an index to the proxy model index.
213
214 @param sourceIndex reference to a source model index (QModelIndex)
215 @return proxy model index (QModelIndex)
216 """
217 self.__load()
218 url = sourceIndex.data(HistoryModel.UrlStringRole)
219 if url not in self.__historyDict:
220 return QModelIndex()
221
222 sourceOffset = self.sourceModel().rowCount() - sourceIndex.row()
223
224 try:
225 row = self.__filteredRows.index(HistoryData(sourceOffset, -1))
226 except ValueError:
227 return QModelIndex()
228
229 return self.createIndex(row, sourceIndex.column(), sourceOffset)
230
231 def index(self, row, column, parent=None):
232 """
233 Public method to create an index.
234
235 @param row row number for the index (integer)
236 @param column column number for the index (integer)
237 @param parent index of the parent item (QModelIndex)
238 @return requested index (QModelIndex)
239 """
240 if parent is None:
241 parent = QModelIndex()
242
243 self.__load()
244 if row < 0 or row >= self.rowCount(parent) or \
245 column < 0 or column >= self.columnCount(parent):
246 return QModelIndex()
247
248 return self.createIndex(row, column,
249 self.__filteredRows[row].tailOffset)
250
251 def parent(self, index):
252 """
253 Public method to get the parent index.
254
255 @param index index of item to get parent (QModelIndex)
256 @return index of parent (QModelIndex)
257 """
258 return QModelIndex()
259
260 def __load(self):
261 """
262 Private method to load the model data.
263 """
264 if self.__loaded:
265 return
266
267 self.__filteredRows = []
268 self.__historyDict = {}
269 self.__scaleTime = QDateTime.currentDateTime()
270
271 for sourceRow in range(self.sourceModel().rowCount()):
272 idx = self.sourceModel().index(sourceRow, 0)
273 url = idx.data(HistoryModel.UrlStringRole)
274 if url not in self.__historyDict:
275 sourceOffset = self.sourceModel().rowCount() - sourceRow
276 self.__filteredRows.append(
277 HistoryData(sourceOffset, self.__frequencyScore(idx)))
278 self.__historyDict[url] = sourceOffset
279 else:
280 # the url is known already, so just update the frequency score
281 row = self.__filteredRows.index(
282 HistoryData(self.__historyDict[url], -1))
283 self.__filteredRows[row].frequency += \
284 self.__frequencyScore(idx)
285
286 self.__loaded = True
287
288 def __sourceRowsInserted(self, parent, start, end):
289 """
290 Private slot to handle the insertion of data in the source model.
291
292 @param parent reference to the parent index (QModelIndex)
293 @param start start row (integer)
294 @param end end row (integer)
295 """
296 if start == end and start == 0:
297 if not self.__loaded:
298 return
299
300 idx = self.sourceModel().index(start, 0, parent)
301 url = idx.data(HistoryModel.UrlStringRole)
302 currentFrequency = 0
303 if url in self.__historyDict:
304 row = self.__filteredRows.index(
305 HistoryData(self.__historyDict[url], -1))
306 currentFrequency = self.__filteredRows[row].frequency
307 self.beginRemoveRows(QModelIndex(), row, row)
308 del self.__filteredRows[row]
309 del self.__historyDict[url]
310 self.endRemoveRows()
311
312 self.beginInsertRows(QModelIndex(), 0, 0)
313 self.__filteredRows.insert(
314 0, HistoryData(
315 self.sourceModel().rowCount(),
316 self.__frequencyScore(idx) + currentFrequency))
317 self.__historyDict[url] = self.sourceModel().rowCount()
318 self.endInsertRows()
319
320 def __sourceRowsRemoved(self, parent, start, end):
321 """
322 Private slot to handle the removal of data in the source model.
323
324 @param parent reference to the parent index (QModelIndex)
325 @param start start row (integer)
326 @param end end row (integer)
327 """
328 self.__sourceReset()
329
330 def removeRows(self, row, count, parent=None):
331 """
332 Public method to remove entries from the model.
333
334 @param row row of the first entry to remove (integer)
335 @param count number of entries to remove (integer)
336 @param parent index of the parent entry (QModelIndex)
337 @return flag indicating successful removal (boolean)
338 """
339 if parent is None:
340 parent = QModelIndex()
341
342 if row < 0 or \
343 count <= 0 or \
344 row + count > self.rowCount(parent) or \
345 parent.isValid():
346 return False
347
348 lastRow = row + count - 1
349 self.sourceModel().rowsRemoved.disconnect(self.__sourceRowsRemoved)
350 self.beginRemoveRows(parent, row, lastRow)
351 oldCount = self.rowCount()
352 start = self.sourceModel().rowCount() - \
353 self.__filteredRows[row].tailOffset
354 end = self.sourceModel().rowCount() - \
355 self.__filteredRows[lastRow].tailOffset
356 self.sourceModel().removeRows(start, end - start + 1)
357 self.endRemoveRows()
358 self.sourceModel().rowsRemoved.connect(self.__sourceRowsRemoved)
359 self.__loaded = False
360 if oldCount - count != self.rowCount():
361 self.beginResetModel()
362 self.endResetModel()
363 return True
364
365 def __frequencyScore(self, sourceIndex):
366 """
367 Private method to calculate the frequency score.
368
369 @param sourceIndex index of the source model (QModelIndex)
370 @return frequency score (integer)
371 """
372 loadTime = \
373 self.sourceModel().data(sourceIndex, HistoryModel.DateTimeRole)
374 days = loadTime.daysTo(self.__scaleTime)
375
376 if days <= 1:
377 return 100
378 elif days < 8: # within the last week
379 return 90
380 elif days < 15: # within the last two weeks
381 return 70
382 elif days < 31: # within the last month
383 return 50
384 elif days < 91: # within the last 3 months
385 return 30
386 else:
387 return 10

eric ide

mercurial