src/eric7/WebBrowser/Download/DownloadManager.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
6 """ 6 """
7 Module implementing the download manager class. 7 Module implementing the download manager class.
8 """ 8 """
9 9
10 from PyQt6.QtCore import ( 10 from PyQt6.QtCore import (
11 pyqtSlot, pyqtSignal, Qt, QModelIndex, QFileInfo, QUrl, QBasicTimer 11 pyqtSlot,
12 pyqtSignal,
13 Qt,
14 QModelIndex,
15 QFileInfo,
16 QUrl,
17 QBasicTimer,
12 ) 18 )
13 from PyQt6.QtGui import QCursor, QKeySequence, QShortcut 19 from PyQt6.QtGui import QCursor, QKeySequence, QShortcut
14 from PyQt6.QtWidgets import ( 20 from PyQt6.QtWidgets import QDialog, QStyle, QFileIconProvider, QMenu, QApplication
15 QDialog, QStyle, QFileIconProvider, QMenu, QApplication
16 )
17 21
18 from EricWidgets import EricMessageBox 22 from EricWidgets import EricMessageBox
19 from EricWidgets.EricApplication import ericApp 23 from EricWidgets.EricApplication import ericApp
20 24
21 from .Ui_DownloadManager import Ui_DownloadManager 25 from .Ui_DownloadManager import Ui_DownloadManager
32 36
33 37
34 class DownloadManager(QDialog, Ui_DownloadManager): 38 class DownloadManager(QDialog, Ui_DownloadManager):
35 """ 39 """
36 Class implementing the download manager. 40 Class implementing the download manager.
37 41
38 @signal downloadsCountChanged() emitted to indicate a change of the 42 @signal downloadsCountChanged() emitted to indicate a change of the
39 count of download items 43 count of download items
40 """ 44 """
45
41 RemoveNever = 0 46 RemoveNever = 0
42 RemoveExit = 1 47 RemoveExit = 1
43 RemoveSuccessFullDownload = 2 48 RemoveSuccessFullDownload = 2
44 49
45 UpdateTimerTimeout = 1000 50 UpdateTimerTimeout = 1000
46 51
47 downloadsCountChanged = pyqtSignal() 52 downloadsCountChanged = pyqtSignal()
48 53
49 def __init__(self, parent=None): 54 def __init__(self, parent=None):
50 """ 55 """
51 Constructor 56 Constructor
52 57
53 @param parent reference to the parent widget (QWidget) 58 @param parent reference to the parent widget (QWidget)
54 """ 59 """
55 super().__init__(parent) 60 super().__init__(parent)
56 self.setupUi(self) 61 self.setupUi(self)
57 self.setWindowFlags(Qt.WindowType.Window) 62 self.setWindowFlags(Qt.WindowType.Window)
58 63
59 self.__winTaskbarButton = None 64 self.__winTaskbarButton = None
60 65
61 self.__saveTimer = AutoSaver(self, self.save) 66 self.__saveTimer = AutoSaver(self, self.save)
62 67
63 self.__model = DownloadModel(self) 68 self.__model = DownloadModel(self)
64 self.__manager = WebBrowserWindow.networkManager() 69 self.__manager = WebBrowserWindow.networkManager()
65 70
66 self.__iconProvider = None 71 self.__iconProvider = None
67 self.__downloads = [] 72 self.__downloads = []
68 self.__downloadDirectory = "" 73 self.__downloadDirectory = ""
69 self.__loaded = False 74 self.__loaded = False
70 75
71 self.__rowHeightMultiplier = 1.1 76 self.__rowHeightMultiplier = 1.1
72 77
73 self.setDownloadDirectory(Preferences.getUI("DownloadPath")) 78 self.setDownloadDirectory(Preferences.getUI("DownloadPath"))
74 79
75 self.downloadsView.setShowGrid(False) 80 self.downloadsView.setShowGrid(False)
76 self.downloadsView.verticalHeader().hide() 81 self.downloadsView.verticalHeader().hide()
77 self.downloadsView.horizontalHeader().hide() 82 self.downloadsView.horizontalHeader().hide()
78 self.downloadsView.setAlternatingRowColors(True) 83 self.downloadsView.setAlternatingRowColors(True)
79 self.downloadsView.horizontalHeader().setStretchLastSection(True) 84 self.downloadsView.horizontalHeader().setStretchLastSection(True)
80 self.downloadsView.setModel(self.__model) 85 self.downloadsView.setModel(self.__model)
81 self.downloadsView.setContextMenuPolicy( 86 self.downloadsView.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
82 Qt.ContextMenuPolicy.CustomContextMenu)
83 self.downloadsView.customContextMenuRequested.connect( 87 self.downloadsView.customContextMenuRequested.connect(
84 self.__customContextMenuRequested) 88 self.__customContextMenuRequested
85 89 )
90
86 self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self) 91 self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self)
87 self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked) 92 self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked)
88 93
89 self.__load() 94 self.__load()
90 95
91 self.__updateTimer = QBasicTimer() 96 self.__updateTimer = QBasicTimer()
92 97
93 def __customContextMenuRequested(self, pos): 98 def __customContextMenuRequested(self, pos):
94 """ 99 """
95 Private slot to handle the context menu request for the bookmarks tree. 100 Private slot to handle the context menu request for the bookmarks tree.
96 101
97 @param pos position the context menu was requested (QPoint) 102 @param pos position the context menu was requested (QPoint)
98 """ 103 """
99 menu = QMenu() 104 menu = QMenu()
100 105
101 selectedRowsCount = len( 106 selectedRowsCount = len(self.downloadsView.selectionModel().selectedRows())
102 self.downloadsView.selectionModel().selectedRows()) 107
103
104 if selectedRowsCount == 1: 108 if selectedRowsCount == 1:
105 row = self.downloadsView.selectionModel().selectedRows()[0].row() 109 row = self.downloadsView.selectionModel().selectedRows()[0].row()
106 itm = self.__downloads[row] 110 itm = self.__downloads[row]
107 if itm.downloadedSuccessfully(): 111 if itm.downloadedSuccessfully():
108 menu.addAction( 112 menu.addAction(
109 UI.PixmapCache.getIcon("open"), 113 UI.PixmapCache.getIcon("open"),
110 self.tr("Open"), self.__contextMenuOpen) 114 self.tr("Open"),
115 self.__contextMenuOpen,
116 )
111 elif itm.downloading(): 117 elif itm.downloading():
112 menu.addAction( 118 menu.addAction(
113 UI.PixmapCache.getIcon("stopLoading"), 119 UI.PixmapCache.getIcon("stopLoading"),
114 self.tr("Cancel"), self.__contextMenuCancel) 120 self.tr("Cancel"),
121 self.__contextMenuCancel,
122 )
115 menu.addSeparator() 123 menu.addSeparator()
116 menu.addAction( 124 menu.addAction(
117 self.tr("Open Containing Folder"), 125 self.tr("Open Containing Folder"), self.__contextMenuOpenFolder
118 self.__contextMenuOpenFolder) 126 )
119 menu.addSeparator() 127 menu.addSeparator()
120 menu.addAction( 128 menu.addAction(self.tr("Go to Download Page"), self.__contextMenuGotoPage)
121 self.tr("Go to Download Page"), 129 menu.addAction(self.tr("Copy Download Link"), self.__contextMenuCopyLink)
122 self.__contextMenuGotoPage)
123 menu.addAction(
124 self.tr("Copy Download Link"),
125 self.__contextMenuCopyLink)
126 menu.addSeparator() 130 menu.addSeparator()
127 menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) 131 menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll)
128 if ( 132 if selectedRowsCount > 1 or (
129 selectedRowsCount > 1 or 133 selectedRowsCount == 1
130 (selectedRowsCount == 1 and 134 and not self.__downloads[
131 not self.__downloads[ 135 self.downloadsView.selectionModel().selectedRows()[0].row()
132 self.downloadsView.selectionModel().selectedRows()[0].row()] 136 ].downloading()
133 .downloading())
134 ): 137 ):
135 menu.addSeparator() 138 menu.addSeparator()
136 menu.addAction( 139 menu.addAction(
137 self.tr("Remove From List"), 140 self.tr("Remove From List"), self.__contextMenuRemoveSelected
138 self.__contextMenuRemoveSelected) 141 )
139 142
140 menu.exec(QCursor.pos()) 143 menu.exec(QCursor.pos())
141 144
142 def shutdown(self): 145 def shutdown(self):
143 """ 146 """
144 Public method to stop the download manager. 147 Public method to stop the download manager.
145 """ 148 """
146 self.save() 149 self.save()
147 self.close() 150 self.close()
148 151
149 def activeDownloadsCount(self): 152 def activeDownloadsCount(self):
150 """ 153 """
151 Public method to get the number of active downloads. 154 Public method to get the number of active downloads.
152 155
153 @return number of active downloads (integer) 156 @return number of active downloads (integer)
154 """ 157 """
155 count = 0 158 count = 0
156 159
157 for download in self.__downloads: 160 for download in self.__downloads:
158 if download.downloading(): 161 if download.downloading():
159 count += 1 162 count += 1
160 return count 163 return count
161 164
162 def allowQuit(self): 165 def allowQuit(self):
163 """ 166 """
164 Public method to check, if it is ok to quit. 167 Public method to check, if it is ok to quit.
165 168
166 @return flag indicating allowance to quit (boolean) 169 @return flag indicating allowance to quit (boolean)
167 """ 170 """
168 if self.activeDownloadsCount() > 0: 171 if self.activeDownloadsCount() > 0:
169 res = EricMessageBox.yesNo( 172 res = EricMessageBox.yesNo(
170 self, 173 self,
171 self.tr(""), 174 self.tr(""),
172 self.tr("""There are %n downloads in progress.\n""" 175 self.tr(
173 """Do you want to quit anyway?""", "", 176 """There are %n downloads in progress.\n"""
174 self.activeDownloadsCount()), 177 """Do you want to quit anyway?""",
175 icon=EricMessageBox.Warning) 178 "",
179 self.activeDownloadsCount(),
180 ),
181 icon=EricMessageBox.Warning,
182 )
176 if not res: 183 if not res:
177 self.show() 184 self.show()
178 return False 185 return False
179 186
180 self.close() 187 self.close()
181 return True 188 return True
182 189
183 def __testWebBrowserView(self, view, url): 190 def __testWebBrowserView(self, view, url):
184 """ 191 """
185 Private method to test a web browser view against an URL. 192 Private method to test a web browser view against an URL.
186 193
187 @param view reference to the web browser view to be tested 194 @param view reference to the web browser view to be tested
188 @type WebBrowserView 195 @type WebBrowserView
189 @param url URL to test against 196 @param url URL to test against
190 @type QUrl 197 @type QUrl
191 @return flag indicating, that the view is the one for the URL 198 @return flag indicating, that the view is the one for the URL
192 @rtype bool 199 @rtype bool
193 """ 200 """
194 if view.tabWidget().count() < 2: 201 if view.tabWidget().count() < 2:
195 return False 202 return False
196 203
197 page = view.page() 204 page = view.page()
198 if page.history().count() != 0: 205 if page.history().count() != 0:
199 return False 206 return False
200 207
201 if ( 208 if not page.url().isEmpty() and page.url().host() == url.host():
202 not page.url().isEmpty() and
203 page.url().host() == url.host()
204 ):
205 return True 209 return True
206 210
207 requestedUrl = page.requestedUrl() 211 requestedUrl = page.requestedUrl()
208 if requestedUrl.isEmpty(): 212 if requestedUrl.isEmpty():
209 requestedUrl = QUrl(view.tabWidget().urlBarForView(view).text()) 213 requestedUrl = QUrl(view.tabWidget().urlBarForView(view).text())
210 return requestedUrl.isEmpty() or requestedUrl.host() == url.host() 214 return requestedUrl.isEmpty() or requestedUrl.host() == url.host()
211 215
212 def __closeDownloadTab(self, url): 216 def __closeDownloadTab(self, url):
213 """ 217 """
214 Private method to close an empty tab, that was opened only for loading 218 Private method to close an empty tab, that was opened only for loading
215 the download URL. 219 the download URL.
216 220
217 @param url download URL 221 @param url download URL
218 @type QUrl 222 @type QUrl
219 """ 223 """
220 if self.__testWebBrowserView( 224 if self.__testWebBrowserView(
221 WebBrowserWindow.getWindow().currentBrowser(), url): 225 WebBrowserWindow.getWindow().currentBrowser(), url
226 ):
222 WebBrowserWindow.getWindow().closeCurrentBrowser() 227 WebBrowserWindow.getWindow().closeCurrentBrowser()
223 return 228 return
224 229
225 for window in WebBrowserWindow.mainWindows(): 230 for window in WebBrowserWindow.mainWindows():
226 for browser in window.browsers(): 231 for browser in window.browsers():
227 if self.__testWebBrowserView(browser, url): 232 if self.__testWebBrowserView(browser, url):
228 window.closeBrowser(browser) 233 window.closeBrowser(browser)
229 return 234 return
230 235
231 def download(self, downloadRequest): 236 def download(self, downloadRequest):
232 """ 237 """
233 Public method to download a file. 238 Public method to download a file.
234 239
235 @param downloadRequest reference to the download object containing the 240 @param downloadRequest reference to the download object containing the
236 download data. 241 download data.
237 @type QWebEngineDownloadRequest 242 @type QWebEngineDownloadRequest
238 """ 243 """
239 url = downloadRequest.url() 244 url = downloadRequest.url()
240 if url.isEmpty(): 245 if url.isEmpty():
241 return 246 return
242 247
243 self.__closeDownloadTab(url) 248 self.__closeDownloadTab(url)
244 249
245 # Safe Browsing 250 # Safe Browsing
246 from WebBrowser.SafeBrowsing.SafeBrowsingManager import ( 251 from WebBrowser.SafeBrowsing.SafeBrowsingManager import SafeBrowsingManager
247 SafeBrowsingManager 252
248 )
249 if SafeBrowsingManager.isEnabled(): 253 if SafeBrowsingManager.isEnabled():
250 threatLists = ( 254 threatLists = WebBrowserWindow.safeBrowsingManager().lookupUrl(url)[0]
251 WebBrowserWindow.safeBrowsingManager().lookupUrl(url)[0]
252 )
253 if threatLists: 255 if threatLists:
254 threatMessages = ( 256 threatMessages = (
255 WebBrowserWindow.safeBrowsingManager() 257 WebBrowserWindow.safeBrowsingManager().getThreatMessages(
256 .getThreatMessages(threatLists) 258 threatLists
259 )
257 ) 260 )
258 res = EricMessageBox.warning( 261 res = EricMessageBox.warning(
259 WebBrowserWindow.getWindow(), 262 WebBrowserWindow.getWindow(),
260 self.tr("Suspicuous URL detected"), 263 self.tr("Suspicuous URL detected"),
261 self.tr("<p>The URL <b>{0}</b> was found in the Safe" 264 self.tr(
262 " Browsing database.</p>{1}").format( 265 "<p>The URL <b>{0}</b> was found in the Safe"
263 url.toString(), "".join(threatMessages)), 266 " Browsing database.</p>{1}"
267 ).format(url.toString(), "".join(threatMessages)),
264 EricMessageBox.Abort | EricMessageBox.Ignore, 268 EricMessageBox.Abort | EricMessageBox.Ignore,
265 EricMessageBox.Abort) 269 EricMessageBox.Abort,
270 )
266 if res == EricMessageBox.Abort: 271 if res == EricMessageBox.Abort:
267 downloadRequest.cancel() 272 downloadRequest.cancel()
268 return 273 return
269 274
270 window = WebBrowserWindow.getWindow() 275 window = WebBrowserWindow.getWindow()
271 pageUrl = window.currentBrowser().url() if window else QUrl() 276 pageUrl = window.currentBrowser().url() if window else QUrl()
272 from .DownloadItem import DownloadItem 277 from .DownloadItem import DownloadItem
273 itm = DownloadItem(downloadRequest=downloadRequest, pageUrl=pageUrl, 278
274 parent=self) 279 itm = DownloadItem(
280 downloadRequest=downloadRequest, pageUrl=pageUrl, parent=self
281 )
275 self.__addItem(itm) 282 self.__addItem(itm)
276 283
277 if Preferences.getWebBrowser("DownloadManagerAutoOpen"): 284 if Preferences.getWebBrowser("DownloadManagerAutoOpen"):
278 self.show() 285 self.show()
279 else: 286 else:
280 self.__startUpdateTimer() 287 self.__startUpdateTimer()
281 288
282 def show(self): 289 def show(self):
283 """ 290 """
284 Public slot to show the download manager dialog. 291 Public slot to show the download manager dialog.
285 """ 292 """
286 self.__startUpdateTimer() 293 self.__startUpdateTimer()
287 294
288 super().show() 295 super().show()
289 self.activateWindow() 296 self.activateWindow()
290 self.raise_() 297 self.raise_()
291 298
292 def __addItem(self, itm, append=False): 299 def __addItem(self, itm, append=False):
293 """ 300 """
294 Private method to add a download to the list of downloads. 301 Private method to add a download to the list of downloads.
295 302
296 @param itm reference to the download item 303 @param itm reference to the download item
297 @type DownloadItem 304 @type DownloadItem
298 @param append flag indicating to append the item 305 @param append flag indicating to append the item
299 @type bool 306 @type bool
300 """ 307 """
301 itm.statusChanged.connect(lambda: self.__updateRow(itm)) 308 itm.statusChanged.connect(lambda: self.__updateRow(itm))
302 itm.downloadFinished.connect(self.__finished) 309 itm.downloadFinished.connect(self.__finished)
303 310
304 # insert at top of window 311 # insert at top of window
305 row = self.downloadsCount() if append else 0 312 row = self.downloadsCount() if append else 0
306 self.__model.beginInsertRows(QModelIndex(), row, row) 313 self.__model.beginInsertRows(QModelIndex(), row, row)
307 if append: 314 if append:
308 self.__downloads.append(itm) 315 self.__downloads.append(itm)
309 else: 316 else:
310 self.__downloads.insert(0, itm) 317 self.__downloads.insert(0, itm)
311 self.__model.endInsertRows() 318 self.__model.endInsertRows()
312 319
313 self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) 320 self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm)
314 icon = self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon) 321 icon = self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon)
315 itm.setIcon(icon) 322 itm.setIcon(icon)
316 self.downloadsView.setRowHeight( 323 self.downloadsView.setRowHeight(
317 row, int(itm.sizeHint().height() * self.__rowHeightMultiplier)) 324 row, int(itm.sizeHint().height() * self.__rowHeightMultiplier)
325 )
318 # just in case the download finished before the constructor returned 326 # just in case the download finished before the constructor returned
319 self.__updateRow(itm) 327 self.__updateRow(itm)
320 self.changeOccurred() 328 self.changeOccurred()
321 329
322 self.downloadsCountChanged.emit() 330 self.downloadsCountChanged.emit()
323 331
324 def __updateRow(self, itm): 332 def __updateRow(self, itm):
325 """ 333 """
326 Private slot to update a download item. 334 Private slot to update a download item.
327 335
328 @param itm reference to the download item 336 @param itm reference to the download item
329 @type DownloadItem 337 @type DownloadItem
330 """ 338 """
331 if itm not in self.__downloads: 339 if itm not in self.__downloads:
332 return 340 return
333 341
334 row = self.__downloads.index(itm) 342 row = self.__downloads.index(itm)
335 343
336 if self.__iconProvider is None: 344 if self.__iconProvider is None:
337 self.__iconProvider = QFileIconProvider() 345 self.__iconProvider = QFileIconProvider()
338 346
339 icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) 347 icon = self.__iconProvider.icon(QFileInfo(itm.fileName()))
340 if icon.isNull(): 348 if icon.isNull():
341 icon = self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon) 349 icon = self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon)
342 itm.setIcon(icon) 350 itm.setIcon(icon)
343 351
344 self.downloadsView.setRowHeight( 352 self.downloadsView.setRowHeight(
345 row, 353 row, int(itm.minimumSizeHint().height() * self.__rowHeightMultiplier)
346 int(itm.minimumSizeHint().height() * self.__rowHeightMultiplier)) 354 )
347 355
348 remove = False 356 remove = False
349 357
350 if ( 358 if (
351 itm.downloadedSuccessfully() and 359 itm.downloadedSuccessfully()
352 self.removePolicy() == DownloadManager.RemoveSuccessFullDownload 360 and self.removePolicy() == DownloadManager.RemoveSuccessFullDownload
353 ): 361 ):
354 remove = True 362 remove = True
355 363
356 if remove: 364 if remove:
357 self.__model.removeRow(row) 365 self.__model.removeRow(row)
358 366
359 self.cleanupButton.setEnabled( 367 self.cleanupButton.setEnabled(
360 (self.downloadsCount() - self.activeDownloadsCount()) > 0) 368 (self.downloadsCount() - self.activeDownloadsCount()) > 0
361 369 )
370
362 # record the change 371 # record the change
363 self.changeOccurred() 372 self.changeOccurred()
364 373
365 def removePolicy(self): 374 def removePolicy(self):
366 """ 375 """
367 Public method to get the remove policy. 376 Public method to get the remove policy.
368 377
369 @return remove policy (integer) 378 @return remove policy (integer)
370 """ 379 """
371 return Preferences.getWebBrowser("DownloadManagerRemovePolicy") 380 return Preferences.getWebBrowser("DownloadManagerRemovePolicy")
372 381
373 def setRemovePolicy(self, policy): 382 def setRemovePolicy(self, policy):
374 """ 383 """
375 Public method to set the remove policy. 384 Public method to set the remove policy.
376 385
377 @param policy policy to be set 386 @param policy policy to be set
378 (DownloadManager.RemoveExit, DownloadManager.RemoveNever, 387 (DownloadManager.RemoveExit, DownloadManager.RemoveNever,
379 DownloadManager.RemoveSuccessFullDownload) 388 DownloadManager.RemoveSuccessFullDownload)
380 """ 389 """
381 if policy in (DownloadManager.RemoveExit, 390 if policy in (
382 DownloadManager.RemoveNever, 391 DownloadManager.RemoveExit,
383 DownloadManager.RemoveSuccessFullDownload): 392 DownloadManager.RemoveNever,
384 393 DownloadManager.RemoveSuccessFullDownload,
394 ):
395
385 if policy == self.removePolicy(): 396 if policy == self.removePolicy():
386 return 397 return
387 398
388 Preferences.setWebBrowser("DownloadManagerRemovePolicy", 399 Preferences.setWebBrowser("DownloadManagerRemovePolicy", self.policy)
389 self.policy) 400
390
391 def save(self): 401 def save(self):
392 """ 402 """
393 Public method to save the download settings. 403 Public method to save the download settings.
394 """ 404 """
395 if not self.__loaded: 405 if not self.__loaded:
396 return 406 return
397 407
398 Preferences.setWebBrowser("DownloadManagerSize", self.size()) 408 Preferences.setWebBrowser("DownloadManagerSize", self.size())
399 Preferences.setWebBrowser("DownloadManagerPosition", self.pos()) 409 Preferences.setWebBrowser("DownloadManagerPosition", self.pos())
400 if self.removePolicy() == DownloadManager.RemoveExit: 410 if self.removePolicy() == DownloadManager.RemoveExit:
401 return 411 return
402 412
403 from WebBrowser.WebBrowserWindow import WebBrowserWindow 413 from WebBrowser.WebBrowserWindow import WebBrowserWindow
414
404 if WebBrowserWindow.isPrivate(): 415 if WebBrowserWindow.isPrivate():
405 return 416 return
406 417
407 downloads = [] 418 downloads = []
408 for download in self.__downloads: 419 for download in self.__downloads:
409 downloads.append(download.getData()) 420 downloads.append(download.getData())
410 Preferences.setWebBrowser("DownloadManagerDownloads", downloads) 421 Preferences.setWebBrowser("DownloadManagerDownloads", downloads)
411 422
412 def __load(self): 423 def __load(self):
413 """ 424 """
414 Private method to load the download settings. 425 Private method to load the download settings.
415 """ 426 """
416 if self.__loaded: 427 if self.__loaded:
417 return 428 return
418 429
419 size = Preferences.getWebBrowser("DownloadManagerSize") 430 size = Preferences.getWebBrowser("DownloadManagerSize")
420 if size.isValid(): 431 if size.isValid():
421 self.resize(size) 432 self.resize(size)
422 pos = Preferences.getWebBrowser("DownloadManagerPosition") 433 pos = Preferences.getWebBrowser("DownloadManagerPosition")
423 self.move(pos) 434 self.move(pos)
424 435
425 from WebBrowser.WebBrowserWindow import WebBrowserWindow 436 from WebBrowser.WebBrowserWindow import WebBrowserWindow
437
426 if not WebBrowserWindow.isPrivate(): 438 if not WebBrowserWindow.isPrivate():
427 downloads = Preferences.getWebBrowser("DownloadManagerDownloads") 439 downloads = Preferences.getWebBrowser("DownloadManagerDownloads")
428 for download in downloads: 440 for download in downloads:
429 if ( 441 if not download["URL"].isEmpty() and bool(download["Location"]):
430 not download["URL"].isEmpty() and
431 bool(download["Location"])
432 ):
433 from .DownloadItem import DownloadItem 442 from .DownloadItem import DownloadItem
443
434 itm = DownloadItem(parent=self) 444 itm = DownloadItem(parent=self)
435 itm.setData(download) 445 itm.setData(download)
436 self.__addItem(itm, append=True) 446 self.__addItem(itm, append=True)
437 self.cleanupButton.setEnabled( 447 self.cleanupButton.setEnabled(
438 (self.downloadsCount() - self.activeDownloadsCount()) > 0) 448 (self.downloadsCount() - self.activeDownloadsCount()) > 0
439 449 )
450
440 self.__loaded = True 451 self.__loaded = True
441 452
442 self.downloadsCountChanged.emit() 453 self.downloadsCountChanged.emit()
443 454
444 def closeEvent(self, evt): 455 def closeEvent(self, evt):
445 """ 456 """
446 Protected event handler for the close event. 457 Protected event handler for the close event.
447 458
448 @param evt reference to the close event 459 @param evt reference to the close event
449 @type QCloseEvent 460 @type QCloseEvent
450 """ 461 """
451 self.save() 462 self.save()
452 463
453 def cleanup(self): 464 def cleanup(self):
454 """ 465 """
455 Public slot to cleanup the downloads. 466 Public slot to cleanup the downloads.
456 """ 467 """
457 self.on_cleanupButton_clicked() 468 self.on_cleanupButton_clicked()
458 469
459 @pyqtSlot() 470 @pyqtSlot()
460 def on_cleanupButton_clicked(self): 471 def on_cleanupButton_clicked(self):
461 """ 472 """
462 Private slot to cleanup the downloads. 473 Private slot to cleanup the downloads.
463 """ 474 """
464 if self.downloadsCount() == 0: 475 if self.downloadsCount() == 0:
465 return 476 return
466 477
467 self.__model.removeRows(0, self.downloadsCount()) 478 self.__model.removeRows(0, self.downloadsCount())
468 if ( 479 if self.downloadsCount() == 0 and self.__iconProvider is not None:
469 self.downloadsCount() == 0 and
470 self.__iconProvider is not None
471 ):
472 self.__iconProvider = None 480 self.__iconProvider = None
473 481
474 self.changeOccurred() 482 self.changeOccurred()
475 483
476 self.downloadsCountChanged.emit() 484 self.downloadsCountChanged.emit()
477 485
478 def __finished(self, success): 486 def __finished(self, success):
479 """ 487 """
480 Private slot to handle a finished download. 488 Private slot to handle a finished download.
481 489
482 @param success flag indicating a successful download 490 @param success flag indicating a successful download
483 @type bool 491 @type bool
484 """ 492 """
485 if self.isVisible(): 493 if self.isVisible():
486 QApplication.alert(self) 494 QApplication.alert(self)
487 495
488 self.downloadsCountChanged.emit() 496 self.downloadsCountChanged.emit()
489 497
490 if self.activeDownloadsCount() == 0: 498 if self.activeDownloadsCount() == 0:
491 # all active downloads are done 499 # all active downloads are done
492 if success and ericApp().activeWindow() is not self: 500 if success and ericApp().activeWindow() is not self:
493 WebBrowserWindow.showNotification( 501 WebBrowserWindow.showNotification(
494 UI.PixmapCache.getPixmap("downloads48"), 502 UI.PixmapCache.getPixmap("downloads48"),
495 self.tr("Downloads finished"), 503 self.tr("Downloads finished"),
496 self.tr("All files have been downloaded.") 504 self.tr("All files have been downloaded."),
497 ) 505 )
498 if not Preferences.getWebBrowser("DownloadManagerAutoClose"): 506 if not Preferences.getWebBrowser("DownloadManagerAutoClose"):
499 self.raise_() 507 self.raise_()
500 self.activateWindow() 508 self.activateWindow()
501 509
502 self.__stopUpdateTimer() 510 self.__stopUpdateTimer()
503 self.infoLabel.clear() 511 self.infoLabel.clear()
504 self.setWindowTitle(self.tr("Download Manager")) 512 self.setWindowTitle(self.tr("Download Manager"))
505 if Globals.isWindowsPlatform(): 513 if Globals.isWindowsPlatform():
506 self.__taskbarButton().progress().hide() 514 self.__taskbarButton().progress().hide()
507 515
508 if Preferences.getWebBrowser("DownloadManagerAutoClose"): 516 if Preferences.getWebBrowser("DownloadManagerAutoClose"):
509 self.close() 517 self.close()
510 518
511 def setDownloadDirectory(self, directory): 519 def setDownloadDirectory(self, directory):
512 """ 520 """
513 Public method to set the current download directory. 521 Public method to set the current download directory.
514 522
515 @param directory current download directory (string) 523 @param directory current download directory (string)
516 """ 524 """
517 self.__downloadDirectory = directory 525 self.__downloadDirectory = directory
518 if self.__downloadDirectory != "": 526 if self.__downloadDirectory != "":
519 self.__downloadDirectory += "/" 527 self.__downloadDirectory += "/"
520 528
521 def downloadDirectory(self): 529 def downloadDirectory(self):
522 """ 530 """
523 Public method to get the current download directory. 531 Public method to get the current download directory.
524 532
525 @return current download directory (string) 533 @return current download directory (string)
526 """ 534 """
527 return self.__downloadDirectory 535 return self.__downloadDirectory
528 536
529 def downloadsCount(self): 537 def downloadsCount(self):
530 """ 538 """
531 Public method to get the number of downloads. 539 Public method to get the number of downloads.
532 540
533 @return number of downloads 541 @return number of downloads
534 @rtype int 542 @rtype int
535 """ 543 """
536 return len(self.__downloads) 544 return len(self.__downloads)
537 545
538 def downloads(self): 546 def downloads(self):
539 """ 547 """
540 Public method to get a reference to the downloads. 548 Public method to get a reference to the downloads.
541 549
542 @return reference to the downloads (list of DownloadItem) 550 @return reference to the downloads (list of DownloadItem)
543 """ 551 """
544 return self.__downloads 552 return self.__downloads
545 553
546 def changeOccurred(self): 554 def changeOccurred(self):
547 """ 555 """
548 Public method to signal a change. 556 Public method to signal a change.
549 """ 557 """
550 self.__saveTimer.changeOccurred() 558 self.__saveTimer.changeOccurred()
551 559
552 def __taskbarButton(self): 560 def __taskbarButton(self):
553 """ 561 """
554 Private method to get a reference to the task bar button (Windows 562 Private method to get a reference to the task bar button (Windows
555 only). 563 only).
556 564
557 @return reference to the task bar button 565 @return reference to the task bar button
558 @rtype QWinTaskbarButton or None 566 @rtype QWinTaskbarButton or None
559 """ 567 """
560 if Globals.isWindowsPlatform(): 568 if Globals.isWindowsPlatform():
561 from PyQt6.QtWinExtras import QWinTaskbarButton 569 from PyQt6.QtWinExtras import QWinTaskbarButton
570
562 if self.__winTaskbarButton is None: 571 if self.__winTaskbarButton is None:
563 window = WebBrowserWindow.mainWindow() 572 window = WebBrowserWindow.mainWindow()
564 self.__winTaskbarButton = QWinTaskbarButton( 573 self.__winTaskbarButton = QWinTaskbarButton(window.windowHandle())
565 window.windowHandle())
566 self.__winTaskbarButton.progress().setRange(0, 100) 574 self.__winTaskbarButton.progress().setRange(0, 100)
567 575
568 return self.__winTaskbarButton 576 return self.__winTaskbarButton
569 577
570 def timerEvent(self, evt): 578 def timerEvent(self, evt):
571 """ 579 """
572 Protected event handler for timer events. 580 Protected event handler for timer events.
573 581
574 @param evt reference to the timer event 582 @param evt reference to the timer event
575 @type QTimerEvent 583 @type QTimerEvent
576 """ 584 """
577 if evt.timerId() == self.__updateTimer.timerId(): 585 if evt.timerId() == self.__updateTimer.timerId():
578 if self.activeDownloadsCount() == 0: 586 if self.activeDownloadsCount() == 0:
582 if Globals.isWindowsPlatform(): 590 if Globals.isWindowsPlatform():
583 self.__taskbarButton().progress().hide() 591 self.__taskbarButton().progress().hide()
584 else: 592 else:
585 progresses = [] 593 progresses = []
586 for itm in self.__downloads: 594 for itm in self.__downloads:
587 if ( 595 if itm is None or itm.downloadCanceled() or not itm.downloading():
588 itm is None or
589 itm.downloadCanceled() or
590 not itm.downloading()
591 ):
592 continue 596 continue
593 597
594 progresses.append(( 598 progresses.append(
595 itm.downloadProgress(), 599 (
596 itm.remainingTime(), 600 itm.downloadProgress(),
597 itm.currentSpeed() 601 itm.remainingTime(),
598 )) 602 itm.currentSpeed(),
599 603 )
604 )
605
600 if not progresses: 606 if not progresses:
601 return 607 return
602 608
603 remaining = 0 609 remaining = 0
604 progress = 0 610 progress = 0
605 speed = 0.0 611 speed = 0.0
606 612
607 for progressData in progresses: 613 for progressData in progresses:
608 if progressData[1] > remaining: 614 if progressData[1] > remaining:
609 remaining = progressData[1] 615 remaining = progressData[1]
610 progress += progressData[0] 616 progress += progressData[0]
611 speed += progressData[2] 617 speed += progressData[2]
612 progress /= len(progresses) 618 progress /= len(progresses)
613 619
614 if self.isVisible(): 620 if self.isVisible():
615 self.infoLabel.setText(self.tr( 621 self.infoLabel.setText(
616 "{0}% of %n file(s) ({1}) {2}", "", 622 self.tr(
617 len(progresses)).format( 623 "{0}% of %n file(s) ({1}) {2}", "", len(progresses)
618 progress, 624 ).format(
619 speedString(speed), 625 progress,
620 timeString(remaining), 626 speedString(speed),
621 )) 627 timeString(remaining),
628 )
629 )
622 self.setWindowTitle(self.tr("{0}% - Download Manager")) 630 self.setWindowTitle(self.tr("{0}% - Download Manager"))
623 631
624 if Globals.isWindowsPlatform(): 632 if Globals.isWindowsPlatform():
625 self.__taskbarButton().progress().show() 633 self.__taskbarButton().progress().show()
626 self.__taskbarButton().progress().setValue(progress) 634 self.__taskbarButton().progress().setValue(progress)
627 635
628 super().timerEvent(evt) 636 super().timerEvent(evt)
629 637
630 def __startUpdateTimer(self): 638 def __startUpdateTimer(self):
631 """ 639 """
632 Private slot to start the update timer. 640 Private slot to start the update timer.
633 """ 641 """
634 if self.activeDownloadsCount() and not self.__updateTimer.isActive(): 642 if self.activeDownloadsCount() and not self.__updateTimer.isActive():
635 self.__updateTimer.start(DownloadManager.UpdateTimerTimeout, self) 643 self.__updateTimer.start(DownloadManager.UpdateTimerTimeout, self)
636 644
637 def __stopUpdateTimer(self): 645 def __stopUpdateTimer(self):
638 """ 646 """
639 Private slot to stop the update timer. 647 Private slot to stop the update timer.
640 """ 648 """
641 self.__updateTimer.stop() 649 self.__updateTimer.stop()
642 650
643 ########################################################################### 651 ###########################################################################
644 ## Context menu related methods below 652 ## Context menu related methods below
645 ########################################################################### 653 ###########################################################################
646 654
647 def __currentItem(self): 655 def __currentItem(self):
648 """ 656 """
649 Private method to get a reference to the current item. 657 Private method to get a reference to the current item.
650 658
651 @return reference to the current item (DownloadItem) 659 @return reference to the current item (DownloadItem)
652 """ 660 """
653 index = self.downloadsView.currentIndex() 661 index = self.downloadsView.currentIndex()
654 if index and index.isValid(): 662 if index and index.isValid():
655 row = index.row() 663 row = index.row()
656 return self.__downloads[row] 664 return self.__downloads[row]
657 665
658 return None 666 return None
659 667
660 def __contextMenuOpen(self): 668 def __contextMenuOpen(self):
661 """ 669 """
662 Private method to open the downloaded file. 670 Private method to open the downloaded file.
663 """ 671 """
664 itm = self.__currentItem() 672 itm = self.__currentItem()
665 if itm is not None: 673 if itm is not None:
666 itm.openFile() 674 itm.openFile()
667 675
668 def __contextMenuOpenFolder(self): 676 def __contextMenuOpenFolder(self):
669 """ 677 """
670 Private method to open the folder containing the downloaded file. 678 Private method to open the folder containing the downloaded file.
671 """ 679 """
672 itm = self.__currentItem() 680 itm = self.__currentItem()
673 if itm is not None: 681 if itm is not None:
674 itm.openFolder() 682 itm.openFolder()
675 683
676 def __contextMenuCancel(self): 684 def __contextMenuCancel(self):
677 """ 685 """
678 Private method to cancel the current download. 686 Private method to cancel the current download.
679 """ 687 """
680 itm = self.__currentItem() 688 itm = self.__currentItem()
681 if itm is not None: 689 if itm is not None:
682 itm.cancelDownload() 690 itm.cancelDownload()
683 691
684 def __contextMenuGotoPage(self): 692 def __contextMenuGotoPage(self):
685 """ 693 """
686 Private method to open the download page. 694 Private method to open the download page.
687 """ 695 """
688 itm = self.__currentItem() 696 itm = self.__currentItem()
689 if itm is not None: 697 if itm is not None:
690 url = itm.getPageUrl() 698 url = itm.getPageUrl()
691 WebBrowserWindow.mainWindow().openUrl(url, "") 699 WebBrowserWindow.mainWindow().openUrl(url, "")
692 700
693 def __contextMenuCopyLink(self): 701 def __contextMenuCopyLink(self):
694 """ 702 """
695 Private method to copy the download link to the clipboard. 703 Private method to copy the download link to the clipboard.
696 """ 704 """
697 itm = self.__currentItem() 705 itm = self.__currentItem()
698 if itm is not None: 706 if itm is not None:
699 url = itm.getPageUrl().toDisplayString( 707 url = itm.getPageUrl().toDisplayString(
700 QUrl.ComponentFormattingOption.FullyDecoded) 708 QUrl.ComponentFormattingOption.FullyDecoded
709 )
701 QApplication.clipboard().setText(url) 710 QApplication.clipboard().setText(url)
702 711
703 def __contextMenuSelectAll(self): 712 def __contextMenuSelectAll(self):
704 """ 713 """
705 Private method to select all downloads. 714 Private method to select all downloads.
706 """ 715 """
707 self.downloadsView.selectAll() 716 self.downloadsView.selectAll()
708 717
709 def __contextMenuRemoveSelected(self): 718 def __contextMenuRemoveSelected(self):
710 """ 719 """
711 Private method to remove the selected downloads from the list. 720 Private method to remove the selected downloads from the list.
712 """ 721 """
713 self.downloadsView.removeSelected() 722 self.downloadsView.removeSelected()

eric ide

mercurial