WebBrowser/Download/DownloadManager.py

branch
QtWebEngine
changeset 4768
57da9217196b
parent 4631
5c1a96925da4
child 4769
2b6f7e026cdc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadManager.py	Tue Feb 23 20:00:40 2016 +0100
@@ -0,0 +1,527 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the download manager class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QModelIndex, QFileInfo
+from PyQt5.QtGui import QCursor
+from PyQt5.QtWidgets import QDialog, QStyle, QFileIconProvider, QMenu, \
+    QApplication
+from PyQt5.QtNetwork import QNetworkRequest
+from PyQt5.QtWebKit import QWebSettings
+
+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.
+    """
+    RemoveNever = 0
+    RemoveExit = 1
+    RemoveSuccessFullDownload = 2
+    
+    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 = Helpviewer.HelpWindow.HelpWindow\
+            .networkAccessManager()
+        
+        self.__iconProvider = None
+        self.__downloads = []
+        self.__downloadDirectory = ""
+        self.__loaded = False
+        
+        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.__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.downloadCanceled():
+                menu.addAction(
+                    UI.PixmapCache.getIcon("restart.png"),
+                    self.tr("Retry"), self.__contextMenuRetry)
+            else:
+                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.__saveTimer.changeOccurred()
+        self.__saveTimer.saveIfNeccessary()
+        self.close()
+    
+    def activeDownloads(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.activeDownloads() > 0:
+            res = E5MessageBox.yesNo(
+                self,
+                self.tr(""),
+                self.tr("""There are %n downloads in progress.\n"""
+                        """Do you want to quit anyway?""", "",
+                        self.activeDownloads()),
+                icon=E5MessageBox.Warning)
+            if not res:
+                self.show()
+                return False
+        return True
+    
+    def download(self, requestOrUrl, requestFileName=False, mainWindow=None):
+        """
+        Public method to download a file.
+        
+        @param requestOrUrl reference to a request object (QNetworkRequest)
+            or a URL to be downloaded (QUrl)
+        @keyparam requestFileName flag indicating to ask for the
+            download file name (boolean)
+        @keyparam mainWindow reference to the main window (HelpWindow)
+        """
+        request = QNetworkRequest(requestOrUrl)
+        if request.url().isEmpty():
+            return
+        self.handleUnsupportedContent(
+            self.__manager.get(request),
+            requestFileName=requestFileName,
+            download=True,
+            mainWindow=mainWindow)
+    
+    def handleUnsupportedContent(self, reply, requestFileName=False,
+                                 webPage=None, download=False,
+                                 mainWindow=None):
+        """
+        Public method to handle unsupported content by downloading the
+        referenced resource.
+        
+        @param reply reference to the reply object (QNetworkReply)
+        @keyparam requestFileName indicating to ask for a filename
+            (boolean)
+        @keyparam webPage reference to the web page (HelpWebPage)
+        @keyparam download flag indicating a download request (boolean)
+        @keyparam mainWindow reference to the main window (HelpWindow)
+        """
+        if reply is None or reply.url().isEmpty():
+            return
+        
+        size = reply.header(QNetworkRequest.ContentLengthHeader)
+        if size == 0:
+            return
+        
+        from .DownloadItem import DownloadItem
+        itm = DownloadItem(
+            reply=reply, requestFilename=requestFileName,
+            webPage=webPage, download=download, parent=self,
+            mainWindow=mainWindow)
+        self.__addItem(itm)
+        
+        if itm.canceledFileSelect():
+            return
+        
+        if not self.isVisible():
+            self.show()
+        
+        self.activateWindow()
+        self.raise_()
+    
+    def __addItem(self, itm):
+        """
+        Private method to add a download to the list of downloads.
+        
+        @param itm reference to the download item (DownloadItem)
+        """
+        itm.statusChanged.connect(self.__updateRow)
+        itm.downloadFinished.connect(self.__finished)
+        
+        row = len(self.__downloads)
+        self.__model.beginInsertRows(QModelIndex(), row, row)
+        self.__downloads.append(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() * 1.5)
+        # just in case the download finished before the constructor returned
+        self.__updateRow(itm)
+        self.changeOccurred()
+        self.__updateActiveItemCount()
+    
+    def __updateRow(self, itm=None):
+        """
+        Private slot to update a download item.
+        
+        @param itm reference to the download item (DownloadItem)
+        """
+        if itm is None:
+            itm = self.sender()
+        
+        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)
+        
+        oldHeight = self.downloadsView.rowHeight(row)
+        self.downloadsView.setRowHeight(
+            row,
+            max(oldHeight, itm.minimumSizeHint().height() * 1.5))
+        
+        remove = False
+        globalSettings = QWebSettings.globalSettings()
+        if not itm.downloading() and \
+           globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
+            remove = True
+        
+        if itm.downloadedSuccessfully() and \
+           self.removePolicy() == DownloadManager.RemoveSuccessFullDownload:
+            remove = True
+        
+        if remove:
+            self.__model.removeRow(row)
+        
+        self.cleanupButton.setEnabled(
+            (len(self.__downloads) - self.activeDownloads()) > 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.getWebBrowser("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
+        
+        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)
+        
+        downloads = Preferences.getWebBrowser("DownloadManagerDownloads")
+        for download in downloads:
+            if not download[0].isEmpty() and \
+               download[1] != "":
+                from .DownloadItem import DownloadItem
+                itm = DownloadItem(parent=self)
+                itm.setData(download)
+                self.__addItem(itm)
+        self.cleanupButton.setEnabled(
+            (len(self.__downloads) - self.activeDownloads()) > 0)
+        
+        self.__loaded = True
+        self.__updateActiveItemCount()
+    
+    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 len(self.__downloads) == 0:
+            return
+        
+        self.__model.removeRows(0, len(self.__downloads))
+        if len(self.__downloads) == 0 and \
+           self.__iconProvider is not None:
+            self.__iconProvider = None
+        
+        self.changeOccurred()
+        self.__updateActiveItemCount()
+    
+    def __updateItemCount(self):
+        """
+        Private method to update the count label.
+        """
+        count = len(self.__downloads)
+        self.countLabel.setText(self.tr("%n Download(s)", "", count))
+    
+    def __updateActiveItemCount(self):
+        """
+        Private method to update the window title.
+        """
+        count = self.activeDownloads()
+        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)
+    
+    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 count(self):
+        """
+        Public method to get the number of downloads.
+        
+        @return number of downloads (integer)
+        """
+        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 __contextMenuRetry(self):
+        """
+        Private method to retry of the download.
+        """
+        itm = self.__currentItem()
+        if itm is not None:
+            itm.retry()
+    
+    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().toString()
+            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()

eric ide

mercurial