Tue, 13 Feb 2018 19:59:44 +0100
Fixed a few code style issues.
# -*- coding: utf-8 -*- # Copyright (c) 2010 - 2018 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the download manager class. """ from __future__ import unicode_literals from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QModelIndex, QFileInfo, QUrl from PyQt5.QtGui import QCursor, QKeySequence from PyQt5.QtWidgets import QDialog, QStyle, QFileIconProvider, QMenu, \ QApplication, QShortcut from E5Gui import E5MessageBox from .Ui_DownloadManager import Ui_DownloadManager from .DownloadModel import DownloadModel from WebBrowser.WebBrowserWindow import WebBrowserWindow from Utilities.AutoSaver import AutoSaver import UI.PixmapCache import Preferences class DownloadManager(QDialog, Ui_DownloadManager): """ Class implementing the download manager. @signal downloadsCountChanged() emitted to indicate a change of the count of download items """ RemoveNever = 0 RemoveExit = 1 RemoveSuccessFullDownload = 2 downloadsCountChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = WebBrowserWindow.networkManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.__rowHeightMultiplier = 1.1 self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self) self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked) self.__load() def __customContextMenuRequested(self, pos): """ Private slot to handle the context menu request for the bookmarks tree. @param pos position the context menu was requested (QPoint) """ menu = QMenu() selectedRowsCount = len( self.downloadsView.selectionModel().selectedRows()) if selectedRowsCount == 1: row = self.downloadsView.selectionModel().selectedRows()[0].row() itm = self.__downloads[row] if itm.downloadedSuccessfully(): menu.addAction( UI.PixmapCache.getIcon("open.png"), self.tr("Open"), self.__contextMenuOpen) elif itm.downloading(): menu.addAction( UI.PixmapCache.getIcon("stopLoading.png"), self.tr("Cancel"), self.__contextMenuCancel) menu.addSeparator() menu.addAction( self.tr("Open Containing Folder"), self.__contextMenuOpenFolder) menu.addSeparator() menu.addAction( self.tr("Go to Download Page"), self.__contextMenuGotoPage) menu.addAction( self.tr("Copy Download Link"), self.__contextMenuCopyLink) menu.addSeparator() menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) if selectedRowsCount > 1 or \ (selectedRowsCount == 1 and not self.__downloads[ self.downloadsView.selectionModel().selectedRows()[0].row()] .downloading()): menu.addSeparator() menu.addAction( self.tr("Remove From List"), self.__contextMenuRemoveSelected) menu.exec_(QCursor.pos()) def shutdown(self): """ Public method to stop the download manager. """ self.save() self.close() def activeDownloadsCount(self): """ Public method to get the number of active downloads. @return number of active downloads (integer) """ count = 0 for download in self.__downloads: if download.downloading(): count += 1 return count def allowQuit(self): """ Public method to check, if it is ok to quit. @return flag indicating allowance to quit (boolean) """ if self.activeDownloadsCount() > 0: res = E5MessageBox.yesNo( self, self.tr(""), self.tr("""There are %n downloads in progress.\n""" """Do you want to quit anyway?""", "", self.activeDownloadsCount()), icon=E5MessageBox.Warning) if not res: self.show() return False return True def download(self, downloadItem): """ Public method to download a file. @param downloadItem reference to the download object containing the download data. @type QWebEngineDownloadItem """ url = downloadItem.url() if url.isEmpty(): return # Safe Browsing from WebBrowser.SafeBrowsing.SafeBrowsingManager import \ SafeBrowsingManager if SafeBrowsingManager.isEnabled(): threatLists = WebBrowserWindow.safeBrowsingManager().lookupUrl(url) if threatLists: threatMessages = WebBrowserWindow.safeBrowsingManager()\ .getThreatMessages(threatLists) res = E5MessageBox.warning( WebBrowserWindow.getWindow(), self.tr("Suspicuous URL detected"), self.tr("<p>The URL <b>{0}</b> was found in the Safe" " Browsing database.</p>{1}").format( url.toString(), "".join(threatMessages)), E5MessageBox.StandardButtons( E5MessageBox.Abort | E5MessageBox.Ignore), E5MessageBox.Abort) if res == E5MessageBox.Abort: downloadItem.cancel() return pageUrl = \ WebBrowserWindow.mainWindow().getWindow().currentBrowser().url() from .DownloadItem import DownloadItem itm = DownloadItem(downloadItem=downloadItem, pageUrl=pageUrl, parent=self) self.__addItem(itm) if itm.canceledFileSelect(): return if not self.isVisible(): self.show() self.activateWindow() self.raise_() def __addItem(self, itm, append=False): """ Private method to add a download to the list of downloads. @param itm reference to the download item @type DownloadItem @param append flag indicating to append the item @type bool """ itm.statusChanged.connect(lambda: self.__updateRow(itm)) itm.downloadFinished.connect(self.__finished) # insert at top of window if append: row = self.downloadsCount() else: row = 0 self.__model.beginInsertRows(QModelIndex(), row, row) if append: self.__downloads.append(itm) else: self.__downloads.insert(0, itm) self.__model.endInsertRows() self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) self.downloadsView.setRowHeight( row, itm.sizeHint().height() * self.__rowHeightMultiplier) # just in case the download finished before the constructor returned self.__updateRow(itm) self.changeOccurred() self.__updateActiveItemCount() self.downloadsCountChanged.emit() def __updateRow(self, itm): """ Private slot to update a download item. @param itm reference to the download item @type DownloadItem """ if itm not in self.__downloads: return row = self.__downloads.index(itm) if self.__iconProvider is None: self.__iconProvider = QFileIconProvider() icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) if icon.isNull(): icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) self.downloadsView.setRowHeight( row, itm.minimumSizeHint().height() * self.__rowHeightMultiplier) remove = False if itm.downloadedSuccessfully() and \ self.removePolicy() == DownloadManager.RemoveSuccessFullDownload: remove = True if remove: self.__model.removeRow(row) self.cleanupButton.setEnabled( (self.downloadsCount() - self.activeDownloadsCount()) > 0) # record the change self.changeOccurred() def removePolicy(self): """ Public method to get the remove policy. @return remove policy (integer) """ return Preferences.getWebBrowser("DownloadManagerRemovePolicy") def setRemovePolicy(self, policy): """ Public method to set the remove policy. @param policy policy to be set (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) """ assert policy in (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) if policy == self.removePolicy(): return Preferences.setWebBrowser("DownloadManagerRemovePolicy", self.policy) def save(self): """ Public method to save the download settings. """ if not self.__loaded: return Preferences.setWebBrowser("DownloadManagerSize", self.size()) Preferences.setWebBrowser("DownloadManagerPosition", self.pos()) if self.removePolicy() == DownloadManager.RemoveExit: return from WebBrowser.WebBrowserWindow import WebBrowserWindow if WebBrowserWindow.isPrivate(): return downloads = [] for download in self.__downloads: downloads.append(download.getData()) Preferences.setWebBrowser("DownloadManagerDownloads", downloads) def __load(self): """ Private method to load the download settings. """ if self.__loaded: return size = Preferences.getWebBrowser("DownloadManagerSize") if size.isValid(): self.resize(size) pos = Preferences.getWebBrowser("DownloadManagerPosition") self.move(pos) from WebBrowser.WebBrowserWindow import WebBrowserWindow if not WebBrowserWindow.isPrivate(): downloads = Preferences.getWebBrowser("DownloadManagerDownloads") for download in downloads: if not download["URL"].isEmpty() and \ bool(download["Location"]): from .DownloadItem import DownloadItem itm = DownloadItem(parent=self) itm.setData(download) self.__addItem(itm, append=True) self.cleanupButton.setEnabled( (self.downloadsCount() - self.activeDownloadsCount()) > 0) self.__loaded = True self.__updateActiveItemCount() self.downloadsCountChanged.emit() def closeEvent(self, evt): """ Protected event handler for the close event. @param evt reference to the close event @type QCloseEvent """ self.save() def cleanup(self): """ Public slot to cleanup the downloads. """ self.on_cleanupButton_clicked() @pyqtSlot() def on_cleanupButton_clicked(self): """ Private slot cleanup the downloads. """ if self.downloadsCount() == 0: return self.__model.removeRows(0, self.downloadsCount()) if self.downloadsCount() == 0 and \ self.__iconProvider is not None: self.__iconProvider = None self.changeOccurred() self.__updateActiveItemCount() self.downloadsCountChanged.emit() def __updateItemCount(self): """ Private method to update the count label. """ count = self.downloadsCount() self.countLabel.setText(self.tr("%n Download(s)", "", count)) def __updateActiveItemCount(self): """ Private method to update the window title. """ count = self.activeDownloadsCount() if count > 0: self.setWindowTitle( self.tr("Downloading %n file(s)", "", count)) else: self.setWindowTitle(self.tr("Downloads")) def __finished(self): """ Private slot to handle a finished download. """ self.__updateActiveItemCount() if self.isVisible(): QApplication.alert(self) self.downloadsCountChanged.emit() def setDownloadDirectory(self, directory): """ Public method to set the current download directory. @param directory current download directory (string) """ self.__downloadDirectory = directory if self.__downloadDirectory != "": self.__downloadDirectory += "/" def downloadDirectory(self): """ Public method to get the current download directory. @return current download directory (string) """ return self.__downloadDirectory def downloadsCount(self): """ Public method to get the number of downloads. @return number of downloads @type int """ return len(self.__downloads) def downloads(self): """ Public method to get a reference to the downloads. @return reference to the downloads (list of DownloadItem) """ return self.__downloads def changeOccurred(self): """ Public method to signal a change. """ self.__saveTimer.changeOccurred() self.__updateItemCount() ########################################################################### ## Context menu related methods below ########################################################################### def __currentItem(self): """ Private method to get a reference to the current item. @return reference to the current item (DownloadItem) """ index = self.downloadsView.currentIndex() if index and index.isValid(): row = index.row() return self.__downloads[row] return None def __contextMenuOpen(self): """ Private method to open the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFile() def __contextMenuOpenFolder(self): """ Private method to open the folder containing the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFolder() def __contextMenuCancel(self): """ Private method to cancel the current download. """ itm = self.__currentItem() if itm is not None: itm.cancelDownload() def __contextMenuGotoPage(self): """ Private method to open the download page. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl() WebBrowserWindow.mainWindow().openUrl(url, "") def __contextMenuCopyLink(self): """ Private method to copy the download link to the clipboard. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl().toDisplayString(QUrl.FullyDecoded) QApplication.clipboard().setText(url) def __contextMenuSelectAll(self): """ Private method to select all downloads. """ self.downloadsView.selectAll() def __contextMenuRemoveSelected(self): """ Private method to remove the selected downloads from the list. """ self.downloadsView.removeSelected()