WebBrowser/Download/DownloadManager.py

branch
QtWebEngine
changeset 4768
57da9217196b
parent 4631
5c1a96925da4
child 4769
2b6f7e026cdc
equal deleted inserted replaced
4767:0bace7c5ebc9 4768:57da9217196b
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the download manager class.
8 """
9
10 from __future__ import unicode_literals
11
12 from PyQt5.QtCore import pyqtSlot, Qt, QModelIndex, QFileInfo
13 from PyQt5.QtGui import QCursor
14 from PyQt5.QtWidgets import QDialog, QStyle, QFileIconProvider, QMenu, \
15 QApplication
16 from PyQt5.QtNetwork import QNetworkRequest
17 from PyQt5.QtWebKit import QWebSettings
18
19 from E5Gui import E5MessageBox
20
21 from .Ui_DownloadManager import Ui_DownloadManager
22
23 from .DownloadModel import DownloadModel
24
25 from WebBrowser.WebBrowserWindow import WebBrowserWindow
26
27 from Utilities.AutoSaver import AutoSaver
28 import UI.PixmapCache
29 import Preferences
30
31
32 class DownloadManager(QDialog, Ui_DownloadManager):
33 """
34 Class implementing the download manager.
35 """
36 RemoveNever = 0
37 RemoveExit = 1
38 RemoveSuccessFullDownload = 2
39
40 def __init__(self, parent=None):
41 """
42 Constructor
43
44 @param parent reference to the parent widget (QWidget)
45 """
46 super(DownloadManager, self).__init__(parent)
47 self.setupUi(self)
48 self.setWindowFlags(Qt.Window)
49
50 self.__saveTimer = AutoSaver(self, self.save)
51
52 self.__model = DownloadModel(self)
53 self.__manager = Helpviewer.HelpWindow.HelpWindow\
54 .networkAccessManager()
55
56 self.__iconProvider = None
57 self.__downloads = []
58 self.__downloadDirectory = ""
59 self.__loaded = False
60
61 self.setDownloadDirectory(Preferences.getUI("DownloadPath"))
62
63 self.downloadsView.setShowGrid(False)
64 self.downloadsView.verticalHeader().hide()
65 self.downloadsView.horizontalHeader().hide()
66 self.downloadsView.setAlternatingRowColors(True)
67 self.downloadsView.horizontalHeader().setStretchLastSection(True)
68 self.downloadsView.setModel(self.__model)
69 self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
70 self.downloadsView.customContextMenuRequested.connect(
71 self.__customContextMenuRequested)
72
73 self.__load()
74
75 def __customContextMenuRequested(self, pos):
76 """
77 Private slot to handle the context menu request for the bookmarks tree.
78
79 @param pos position the context menu was requested (QPoint)
80 """
81 menu = QMenu()
82
83 selectedRowsCount = len(
84 self.downloadsView.selectionModel().selectedRows())
85
86 if selectedRowsCount == 1:
87 row = self.downloadsView.selectionModel().selectedRows()[0].row()
88 itm = self.__downloads[row]
89 if itm.downloadCanceled():
90 menu.addAction(
91 UI.PixmapCache.getIcon("restart.png"),
92 self.tr("Retry"), self.__contextMenuRetry)
93 else:
94 if itm.downloadedSuccessfully():
95 menu.addAction(
96 UI.PixmapCache.getIcon("open.png"),
97 self.tr("Open"), self.__contextMenuOpen)
98 elif itm.downloading():
99 menu.addAction(
100 UI.PixmapCache.getIcon("stopLoading.png"),
101 self.tr("Cancel"), self.__contextMenuCancel)
102 menu.addSeparator()
103 menu.addAction(
104 self.tr("Open Containing Folder"),
105 self.__contextMenuOpenFolder)
106 menu.addSeparator()
107 menu.addAction(
108 self.tr("Go to Download Page"),
109 self.__contextMenuGotoPage)
110 menu.addAction(
111 self.tr("Copy Download Link"),
112 self.__contextMenuCopyLink)
113 menu.addSeparator()
114 menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll)
115 if selectedRowsCount > 1 or \
116 (selectedRowsCount == 1 and
117 not self.__downloads[
118 self.downloadsView.selectionModel().selectedRows()[0].row()]
119 .downloading()):
120 menu.addSeparator()
121 menu.addAction(
122 self.tr("Remove From List"),
123 self.__contextMenuRemoveSelected)
124
125 menu.exec_(QCursor.pos())
126
127 def shutdown(self):
128 """
129 Public method to stop the download manager.
130 """
131 self.__saveTimer.changeOccurred()
132 self.__saveTimer.saveIfNeccessary()
133 self.close()
134
135 def activeDownloads(self):
136 """
137 Public method to get the number of active downloads.
138
139 @return number of active downloads (integer)
140 """
141 count = 0
142
143 for download in self.__downloads:
144 if download.downloading():
145 count += 1
146 return count
147
148 def allowQuit(self):
149 """
150 Public method to check, if it is ok to quit.
151
152 @return flag indicating allowance to quit (boolean)
153 """
154 if self.activeDownloads() > 0:
155 res = E5MessageBox.yesNo(
156 self,
157 self.tr(""),
158 self.tr("""There are %n downloads in progress.\n"""
159 """Do you want to quit anyway?""", "",
160 self.activeDownloads()),
161 icon=E5MessageBox.Warning)
162 if not res:
163 self.show()
164 return False
165 return True
166
167 def download(self, requestOrUrl, requestFileName=False, mainWindow=None):
168 """
169 Public method to download a file.
170
171 @param requestOrUrl reference to a request object (QNetworkRequest)
172 or a URL to be downloaded (QUrl)
173 @keyparam requestFileName flag indicating to ask for the
174 download file name (boolean)
175 @keyparam mainWindow reference to the main window (HelpWindow)
176 """
177 request = QNetworkRequest(requestOrUrl)
178 if request.url().isEmpty():
179 return
180 self.handleUnsupportedContent(
181 self.__manager.get(request),
182 requestFileName=requestFileName,
183 download=True,
184 mainWindow=mainWindow)
185
186 def handleUnsupportedContent(self, reply, requestFileName=False,
187 webPage=None, download=False,
188 mainWindow=None):
189 """
190 Public method to handle unsupported content by downloading the
191 referenced resource.
192
193 @param reply reference to the reply object (QNetworkReply)
194 @keyparam requestFileName indicating to ask for a filename
195 (boolean)
196 @keyparam webPage reference to the web page (HelpWebPage)
197 @keyparam download flag indicating a download request (boolean)
198 @keyparam mainWindow reference to the main window (HelpWindow)
199 """
200 if reply is None or reply.url().isEmpty():
201 return
202
203 size = reply.header(QNetworkRequest.ContentLengthHeader)
204 if size == 0:
205 return
206
207 from .DownloadItem import DownloadItem
208 itm = DownloadItem(
209 reply=reply, requestFilename=requestFileName,
210 webPage=webPage, download=download, parent=self,
211 mainWindow=mainWindow)
212 self.__addItem(itm)
213
214 if itm.canceledFileSelect():
215 return
216
217 if not self.isVisible():
218 self.show()
219
220 self.activateWindow()
221 self.raise_()
222
223 def __addItem(self, itm):
224 """
225 Private method to add a download to the list of downloads.
226
227 @param itm reference to the download item (DownloadItem)
228 """
229 itm.statusChanged.connect(self.__updateRow)
230 itm.downloadFinished.connect(self.__finished)
231
232 row = len(self.__downloads)
233 self.__model.beginInsertRows(QModelIndex(), row, row)
234 self.__downloads.append(itm)
235 self.__model.endInsertRows()
236
237 self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm)
238 icon = self.style().standardIcon(QStyle.SP_FileIcon)
239 itm.setIcon(icon)
240 self.downloadsView.setRowHeight(row, itm.sizeHint().height() * 1.5)
241 # just in case the download finished before the constructor returned
242 self.__updateRow(itm)
243 self.changeOccurred()
244 self.__updateActiveItemCount()
245
246 def __updateRow(self, itm=None):
247 """
248 Private slot to update a download item.
249
250 @param itm reference to the download item (DownloadItem)
251 """
252 if itm is None:
253 itm = self.sender()
254
255 if itm not in self.__downloads:
256 return
257
258 row = self.__downloads.index(itm)
259
260 if self.__iconProvider is None:
261 self.__iconProvider = QFileIconProvider()
262
263 icon = self.__iconProvider.icon(QFileInfo(itm.fileName()))
264 if icon.isNull():
265 icon = self.style().standardIcon(QStyle.SP_FileIcon)
266 itm.setIcon(icon)
267
268 oldHeight = self.downloadsView.rowHeight(row)
269 self.downloadsView.setRowHeight(
270 row,
271 max(oldHeight, itm.minimumSizeHint().height() * 1.5))
272
273 remove = False
274 globalSettings = QWebSettings.globalSettings()
275 if not itm.downloading() and \
276 globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
277 remove = True
278
279 if itm.downloadedSuccessfully() and \
280 self.removePolicy() == DownloadManager.RemoveSuccessFullDownload:
281 remove = True
282
283 if remove:
284 self.__model.removeRow(row)
285
286 self.cleanupButton.setEnabled(
287 (len(self.__downloads) - self.activeDownloads()) > 0)
288
289 # record the change
290 self.changeOccurred()
291
292 def removePolicy(self):
293 """
294 Public method to get the remove policy.
295
296 @return remove policy (integer)
297 """
298 return Preferences.getWebBrowser("DownloadManagerRemovePolicy")
299
300 def setRemovePolicy(self, policy):
301 """
302 Public method to set the remove policy.
303
304 @param policy policy to be set
305 (DownloadManager.RemoveExit, DownloadManager.RemoveNever,
306 DownloadManager.RemoveSuccessFullDownload)
307 """
308 assert policy in (DownloadManager.RemoveExit,
309 DownloadManager.RemoveNever,
310 DownloadManager.RemoveSuccessFullDownload)
311
312 if policy == self.removePolicy():
313 return
314
315 Preferences.getWebBrowser("DownloadManagerRemovePolicy", self.policy)
316
317 def save(self):
318 """
319 Public method to save the download settings.
320 """
321 if not self.__loaded:
322 return
323
324 Preferences.setWebBrowser("DownloadManagerSize", self.size())
325 Preferences.setWebBrowser("DownloadManagerPosition", self.pos())
326 if self.removePolicy() == DownloadManager.RemoveExit:
327 return
328
329 downloads = []
330 for download in self.__downloads:
331 downloads.append(download.getData())
332 Preferences.setWebBrowser("DownloadManagerDownloads", downloads)
333
334 def __load(self):
335 """
336 Private method to load the download settings.
337 """
338 if self.__loaded:
339 return
340
341 size = Preferences.getWebBrowser("DownloadManagerSize")
342 if size.isValid():
343 self.resize(size)
344 pos = Preferences.getWebBrowser("DownloadManagerPosition")
345 self.move(pos)
346
347 downloads = Preferences.getWebBrowser("DownloadManagerDownloads")
348 for download in downloads:
349 if not download[0].isEmpty() and \
350 download[1] != "":
351 from .DownloadItem import DownloadItem
352 itm = DownloadItem(parent=self)
353 itm.setData(download)
354 self.__addItem(itm)
355 self.cleanupButton.setEnabled(
356 (len(self.__downloads) - self.activeDownloads()) > 0)
357
358 self.__loaded = True
359 self.__updateActiveItemCount()
360
361 def cleanup(self):
362 """
363 Public slot to cleanup the downloads.
364 """
365 self.on_cleanupButton_clicked()
366
367 @pyqtSlot()
368 def on_cleanupButton_clicked(self):
369 """
370 Private slot cleanup the downloads.
371 """
372 if len(self.__downloads) == 0:
373 return
374
375 self.__model.removeRows(0, len(self.__downloads))
376 if len(self.__downloads) == 0 and \
377 self.__iconProvider is not None:
378 self.__iconProvider = None
379
380 self.changeOccurred()
381 self.__updateActiveItemCount()
382
383 def __updateItemCount(self):
384 """
385 Private method to update the count label.
386 """
387 count = len(self.__downloads)
388 self.countLabel.setText(self.tr("%n Download(s)", "", count))
389
390 def __updateActiveItemCount(self):
391 """
392 Private method to update the window title.
393 """
394 count = self.activeDownloads()
395 if count > 0:
396 self.setWindowTitle(
397 self.tr("Downloading %n file(s)", "", count))
398 else:
399 self.setWindowTitle(self.tr("Downloads"))
400
401 def __finished(self):
402 """
403 Private slot to handle a finished download.
404 """
405 self.__updateActiveItemCount()
406 if self.isVisible():
407 QApplication.alert(self)
408
409 def setDownloadDirectory(self, directory):
410 """
411 Public method to set the current download directory.
412
413 @param directory current download directory (string)
414 """
415 self.__downloadDirectory = directory
416 if self.__downloadDirectory != "":
417 self.__downloadDirectory += "/"
418
419 def downloadDirectory(self):
420 """
421 Public method to get the current download directory.
422
423 @return current download directory (string)
424 """
425 return self.__downloadDirectory
426
427 def count(self):
428 """
429 Public method to get the number of downloads.
430
431 @return number of downloads (integer)
432 """
433 return len(self.__downloads)
434
435 def downloads(self):
436 """
437 Public method to get a reference to the downloads.
438
439 @return reference to the downloads (list of DownloadItem)
440 """
441 return self.__downloads
442
443 def changeOccurred(self):
444 """
445 Public method to signal a change.
446 """
447 self.__saveTimer.changeOccurred()
448 self.__updateItemCount()
449
450 ###########################################################################
451 ## Context menu related methods below
452 ###########################################################################
453
454 def __currentItem(self):
455 """
456 Private method to get a reference to the current item.
457
458 @return reference to the current item (DownloadItem)
459 """
460 index = self.downloadsView.currentIndex()
461 if index and index.isValid():
462 row = index.row()
463 return self.__downloads[row]
464
465 return None
466
467 def __contextMenuRetry(self):
468 """
469 Private method to retry of the download.
470 """
471 itm = self.__currentItem()
472 if itm is not None:
473 itm.retry()
474
475 def __contextMenuOpen(self):
476 """
477 Private method to open the downloaded file.
478 """
479 itm = self.__currentItem()
480 if itm is not None:
481 itm.openFile()
482
483 def __contextMenuOpenFolder(self):
484 """
485 Private method to open the folder containing the downloaded file.
486 """
487 itm = self.__currentItem()
488 if itm is not None:
489 itm.openFolder()
490
491 def __contextMenuCancel(self):
492 """
493 Private method to cancel the current download.
494 """
495 itm = self.__currentItem()
496 if itm is not None:
497 itm.cancelDownload()
498
499 def __contextMenuGotoPage(self):
500 """
501 Private method to open the download page.
502 """
503 itm = self.__currentItem()
504 if itm is not None:
505 url = itm.getPageUrl()
506 WebBrowserWindow.mainWindow().openUrl(url, "")
507
508 def __contextMenuCopyLink(self):
509 """
510 Private method to copy the download link to the clipboard.
511 """
512 itm = self.__currentItem()
513 if itm is not None:
514 url = itm.getPageUrl().toString()
515 QApplication.clipboard().setText(url)
516
517 def __contextMenuSelectAll(self):
518 """
519 Private method to select all downloads.
520 """
521 self.downloadsView.selectAll()
522
523 def __contextMenuRemoveSelected(self):
524 """
525 Private method to remove the selected downloads from the list.
526 """
527 self.downloadsView.removeSelected()

eric ide

mercurial