WebBrowser/Download/DownloadManager.py

changeset 6221
35ec993034e1
parent 6153
0b18c86c03a1
child 6222
baffb22b4467
--- a/WebBrowser/Download/DownloadManager.py	Sat Apr 07 16:40:21 2018 +0200
+++ b/WebBrowser/Download/DownloadManager.py	Sun Apr 08 15:54:34 2018 +0200
@@ -9,7 +9,8 @@
 
 from __future__ import unicode_literals
 
-from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QModelIndex, QFileInfo, QUrl
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QModelIndex, QFileInfo, \
+    QUrl, QBasicTimer
 from PyQt5.QtGui import QCursor, QKeySequence
 from PyQt5.QtWidgets import QDialog, QStyle, QFileIconProvider, QMenu, \
     QApplication, QShortcut
@@ -19,12 +20,14 @@
 from .Ui_DownloadManager import Ui_DownloadManager
 
 from .DownloadModel import DownloadModel
+from .DownloadUtilities import speedString, timeString
 
 from WebBrowser.WebBrowserWindow import WebBrowserWindow
 
 from Utilities.AutoSaver import AutoSaver
 import UI.PixmapCache
 import Preferences
+import Globals
 
 
 class DownloadManager(QDialog, Ui_DownloadManager):
@@ -38,6 +41,8 @@
     RemoveExit = 1
     RemoveSuccessFullDownload = 2
     
+    UpdateTimerTimeout = 1000
+    
     downloadsCountChanged = pyqtSignal()
     
     def __init__(self, parent=None):
@@ -50,6 +55,8 @@
         self.setupUi(self)
         self.setWindowFlags(Qt.Window)
         
+        self.__winTaskbarButton = None
+        
         self.__saveTimer = AutoSaver(self, self.save)
         
         self.__model = DownloadModel(self)
@@ -78,6 +85,8 @@
         self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked)
         
         self.__load()
+        
+        self.__updateTimer = QBasicTimer()
     
     def __customContextMenuRequested(self, pos):
         """
@@ -165,6 +174,52 @@
                 return False
         return True
     
+    def __testWebBrowserView(self, view, url):
+        """
+        Private method to test a web browser view against an URL.
+        
+        @param view reference to the web browser view to be tested
+        @type WebBrowserView
+        @param url URL to test against
+        @type QUrl
+        @return flag indicating, that the view is the one for the URL
+        @rtype bool
+        """
+        if view.tabWidget().count() < 2:
+            return False
+        
+        page = view.page()
+        if page.history().count() != 0:
+            return False
+        
+        if not page.url().isEmpty() and \
+           page.url().host() == url.host():
+                return True
+        
+        requestedUrl = page.requestedUrl()
+        if requestedUrl.isEmpty():
+            requestedUrl = QUrl(view.tabWidget().urlBarForView(view).text())
+        return requestedUrl.isEmpty() or requestedUrl.host() == url.host()
+    
+    def __closeDownloadTab(self, url):
+        """
+        Private method to close an empty tab, that was opened only for loading
+        the download URL.
+        
+        @param url download URL
+        @type QUrl
+        """
+        if self.__testWebBrowserView(
+           WebBrowserWindow.getWindow().currentBrowser(), url):
+            WebBrowserWindow.getWindow().closeCurrentBrowser()
+            return
+        
+        for window in WebBrowserWindow.mainWindows():
+            for browser in window.browsers():
+                if self.__testWebBrowserView(browser, url):
+                    window.closeBrowser(browser)
+                    return
+    
     def download(self, downloadItem):
         """
         Public method to download a file.
@@ -177,6 +232,8 @@
         if url.isEmpty():
             return
         
+        self.__closeDownloadTab(url)
+        
         # Safe Browsing
         from WebBrowser.SafeBrowsing.SafeBrowsingManager import \
             SafeBrowsingManager
@@ -206,12 +263,15 @@
                            parent=self)
         self.__addItem(itm)
         
-        if itm.canceledFileSelect():
-            return
+        self.__startUpdateTimer()
+    
+    def show(self):
+        """
+        Public slot to show the download manager dialog.
+        """
+        self.__startUpdateTimer()
         
-        if not self.isVisible():
-            self.show()
-        
+        super(DownloadManager, self).show()
         self.activateWindow()
         self.raise_()
     
@@ -247,7 +307,6 @@
         # just in case the download finished before the constructor returned
         self.__updateRow(itm)
         self.changeOccurred()
-        self.__updateActiveItemCount()
         
         self.downloadsCountChanged.emit()
     
@@ -363,7 +422,6 @@
                 (self.downloadsCount() - self.activeDownloadsCount()) > 0)
         
         self.__loaded = True
-        self.__updateActiveItemCount()
         
         self.downloadsCountChanged.emit()
     
@@ -385,7 +443,7 @@
     @pyqtSlot()
     def on_cleanupButton_clicked(self):
         """
-        Private slot cleanup the downloads.
+        Private slot to cleanup the downloads.
         """
         if self.downloadsCount() == 0:
             return
@@ -396,33 +454,13 @@
             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)
         
@@ -468,7 +506,95 @@
         Public method to signal a change.
         """
         self.__saveTimer.changeOccurred()
-        self.__updateItemCount()
+    
+    def __taskbarButton(self):
+        """
+        Private method to get a reference to the task bar button (Windows
+        only).
+        
+        @return reference to the task bar button
+        @rtype QWinTaskbarButton or None
+        """
+        if Globals.isWindowsPlatform():
+            from PyQt5.QtWinExtras import QWinTaskbarButton
+            if self.__winTaskbarButton is None:
+                window = WebBrowserWindow.mainWindow()
+                self.__winTaskbarButton = QWinTaskbarButton(
+                    window.windowHandle())
+                self.__winTaskbarButton.progress().setRange(0, 100)
+        
+        return self.__winTaskbarButton
+    
+    def timerEvent(self, evt):
+        """
+        Protected event handler for timer events.
+        
+        @param evt reference to the timer event
+        @type QTimerEvent
+        """
+        if evt.timerId() == self.__updateTimer.timerId():
+            if self.activeDownloadsCount() == 0:
+                self.__stopUpdateTimer()
+                self.infoLabel.clear()
+                self.setWindowTitle(self.tr("Download Manager"))
+                if Globals.isWindowsPlatform():
+                    self.__taskbarButton.progress().hide()
+            else:
+                progresses = []
+                for itm in self.__downloads:
+                    if itm is None or \
+                       itm.downloadCanceled() or \
+                       not itm.downloading():
+                        continue
+                    
+                    progresses.append((
+                        itm.downloadProgress(),
+                        itm.remainingTime(),
+                        itm.currentSpeed()
+                    ))
+                
+                if not progresses:
+                    return
+                
+                remaining = 0
+                progress = 0
+                speed = 0.0
+                
+                for progressData in progresses:
+                    if progressData[1] > remaining:
+                        remaining = progressData[1]
+                    progress += progressData[0]
+                    speed += progressData[2]
+                progress = progress / len(progresses)
+                
+                if self.isVisible():
+                    self.infoLabel.setText(self.tr(
+                        "{0}% of %n file(s) ({1}) {2}", "",
+                        len(progresses)).format(
+                        progress,
+                        speedString(speed),
+                        timeString(remaining),
+                    ))
+                    self.setWindowTitle(self.tr("{0}% - Download Manager"))
+                
+                if Globals.isWindowsPlatform():
+                    self.taskbarButton().progress().show()
+                    self.taskbarButton().progress().setValue(progress)
+        
+        super(DownloadManager, self).timerEvent(evt)
+    
+    def __startUpdateTimer(self):
+        """
+        Private slot to start the update timer.
+        """
+        if self.activeDownloadsCount() and not self.__updateTimer.isActive():
+            self.__updateTimer.start(DownloadManager.UpdateTimerTimeout, self)
+    
+    def __stopUpdateTimer(self):
+        """
+        Private slot to stop the update timer.
+        """
+        self.__updateTimer.stop()
     
     ###########################################################################
     ## Context menu related methods below

eric ide

mercurial