SF.net SVN: fclient: [75] trunk/fclient/fclient_widgets/download_widget.py
Status: Pre-Alpha
Brought to you by:
jurner
From: <jU...@us...> - 2007-11-13 20:59:42
|
Revision: 75 http://fclient.svn.sourceforge.net/fclient/?rev=75&view=rev Author: jUrner Date: 2007-11-13 12:59:47 -0800 (Tue, 13 Nov 2007) Log Message: ----------- started implementing priority handling + first glimpse of a context menu Modified Paths: -------------- trunk/fclient/fclient_widgets/download_widget.py Modified: trunk/fclient/fclient_widgets/download_widget.py =================================================================== --- trunk/fclient/fclient_widgets/download_widget.py 2007-11-13 20:57:40 UTC (rev 74) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-13 20:59:47 UTC (rev 75) @@ -27,7 +27,6 @@ from fclient_lib.qt4ex.ctrls import treewidgetwrap - sys.path.pop(0) del parentdir #<-- rel import hack @@ -58,7 +57,7 @@ ('MaxSimultaniousDownloads', ['toInt', 2]), ('MaxNewDownloadsPerHop', ['toInt', 100]), - + ('ClearCompletedDownloads', ['toBool', False]), ('ItemTipsShow', ['toBool', True]), ('ItemTipsDelay', ['toInt', areatips.DEFAULT_SHOW_DELAY]) @@ -90,12 +89,15 @@ StatusDownloading = 0x8 StatusDownloadComplete = 0x10 - StatusStopped = 0x1000 + #StatusStopped = 0x1000 # ??? StatusError = 0x20000 + # currently busy loading StatusMaskBusy = StatusRequestInfo | StatusDownloading + # the node knows about our request + StatusMaskInNodeQueue = StatusMaskBusy | StatusRequestInfoComplete | StatusDownloadComplete + - def __init__(self, parent, requestIdentifier, uri): """ @param parent: parent of the item @@ -109,6 +111,7 @@ self._requestIdentifier = requestIdentifier self._requestName = None self._requestNamePostfix = None + self._requestPriority = None self._requestStatus = self.StatusNone self._requestInitTime = time.time() self._uri = uri @@ -195,17 +198,19 @@ return self._requestName - def setRequestPriority(self, priority): + def setRequestPriority(self, priority, text): """Sets the request priority of the item - @param prority: (FcpClient.Priority) + @param prority: (DownloadWidget.Priority*) + @param text: text to display """ - self.setText(self.IndexPriority, priority) + self._requestPriority = priority + self.setText(self.IndexPriority, text) def requestPriority(self): """Returns the request priority of the item - @return: (unicode) request priority + @return: (DownloadWidget.Priority*) request priority """ - return unicode(self.text(self.IndexPriority)) + return self._requestPriority def setProgressWidget(self, tree, widget): """Associates a progress widget to the item @@ -254,11 +259,16 @@ """Returns the request uri of the item""" return self._uri - def setDDATested(self, flag): + """Sets wether the item is dda tested r not + @param flag: True if already tested, False otherwise + """ self._ddaTested = flag def ddaTested(self): + """Checks if the item was already dda tested + @return: (bool) + """ return self._ddaTested #***************************************************************************** @@ -267,10 +277,9 @@ class DownloadWidgetStrings(QtCore.QObject): """Strings for the download widget""" - def __init__(self, parent): - QtCore.QObject.__init__(self, parent) - - + def __init__(self, downloadWidget): + QtCore.QObject.__init__(self, downloadWidget) + self.headerSections = [ (DownloadItem.IndexName, self.trUtf8('Name')), (DownloadItem.IndexStatus, self.trUtf8('Status')), @@ -290,20 +299,60 @@ DownloadItem.StatusDownloading: self.trUtf8('Loading'), DownloadItem.StatusDownloadComplete: self.trUtf8('Complete'), - DownloadItem.StatusStopped: self.trUtf8('Stopped'), + #DownloadItem.StatusStopped: self.trUtf8('Stopped'), DownloadItem.StatusError: self.trUtf8('Error'), } - - self.unknown = self.trUtf8('Unknown') + self.requestPriorities = { # Priority* --> (shortName, longName) + downloadWidget.PriorityHighest: (self.trUtf8('0'), self.trUtf8('0 Highest')), + downloadWidget.PriorityHigher: (self.trUtf8('1'), self.trUtf8('1 High')), + downloadWidget.PriorityHigh: (self.trUtf8('2'), self.trUtf8('2 Higher')), + downloadWidget.PriorityNormal: (self.trUtf8('3'), self.trUtf8('3 Normal')), + downloadWidget.PriorityLow: (self.trUtf8('4'), self.trUtf8('4 Low')), + downloadWidget.PriorityLower: (self.trUtf8('5'), self.trUtf8('5 Lower')), + downloadWidget.PriorityLowest: (self.trUtf8('6'), self.trUtf8('6 Lowest')), + } - + self.unknown = self.trUtf8('???') + #**************************************************************************** # #**************************************************************************** -#TODO: when closing we have to empty queues by sending ClientGet requests +#TODO: when closing we have to send ClientGet for all pending requests by, so they don't get lost. +#HINT: might take a while, so take care to handle gracefully (user panic). Alternative is to keep +# track by other means. Uh.. how? class DownloadWidget(QtGui.QTreeWidget): + + PriorityHighest = 0 + PriorityHigher = 1 + PriorityHigh = 2 + PriorityNormal = 3 + PriorityLow = 4 + PriorityLower = 5 + PriorityLowest = 6 + + # menu action names + ActAdjustPrioritiy = 'DownloadWidgetActAdjustPriority' + ActNamePriorityHighest = 'DownloadWidgetActPriorityHighest' + ActNamePriorityHigher = 'DownloadWidgetActPriorityHigh' + ActNamePriorityHigh = 'DownloadWidgetActPriorityHigh' + ActNamePriorityNormal = 'DownloadWidgetActPriorityNormal' + ActNamePriorityLow = 'DownloadWidgetActPriorityLow' + ActNamePriorityLower = 'DownloadWidgetActPriorityLower' + ActNamePriorityLowest = 'DownloadWidgetActPriorityLowest' + + ActNameMapping = { + PriorityHighest: ActNamePriorityHighest, + PriorityHigher: ActNamePriorityHigher, + PriorityHigh: ActNamePriorityHigh, + PriorityNormal: ActNamePriorityNormal, + PriorityLow: ActNamePriorityLow, + PriorityLower: ActNamePriorityLower, + PriorityLowest: ActNamePriorityLowest, + } + + def __init__(self, parent, connectionName='', @@ -317,7 +366,6 @@ @param connectionName: name of the connection to the node @param cfg: (configConfig) instance or None """ - QtGui.QTreeWidget.__init__(self, parent) self._connectionName = connectionName @@ -331,10 +379,10 @@ self._fcpClient = None self._isCreated = False self._itemTips = areatips.ItemTips(self) + self._priorityMapping = {} self._strings = None self._userSettings = UserSettings() - - + # setup item tips self._itemTips.setEnabled(self._userSettings['ItemTipsShow']) self._itemTips.setShowDelay(self._userSettings['ItemTipsDelay']) @@ -364,23 +412,60 @@ #self._requests['DownloadDirectories'][self._directory] = (self, uris) + + #TODO: "PriorityLowest" might be a bit misleading in priorityMapping + # ... according to docs it equals "will never complete". If true, implement + # DownloadItem.StatusStopped and leave out FcpClient.Priority.Minimum def showEvent(self, event): if not self._isCreated: self._isCreated = True self._fcpClient = self._cfg.fcpClientManager.newClient(self._connectionName, self.handleFcpClientConnected) self._fcpClient.setVerbosity(self._fcpClient.Verbosity.Debug) - - + + # map Fcp priorities to our priorities, in case priority defines change + # if self._fcpClient.Version == blah: (...) + self._priorityMapping = { + self._fcpClient.Priority.Maximum: self.PriorityHighest, + self._fcpClient.Priority.Interactive: self.PriorityHigher, + self._fcpClient.Priority.SemiInteractive: self.PriorityHigh, + self._fcpClient.Priority.Updatable: self.PriorityNormal, + self._fcpClient.Priority.Bulk: self.PriorityLow, + self._fcpClient.Priority.Prefetch: self.PriorityLower, + self._fcpClient.Priority.Minimum: self.PriorityLowest, + } + ############################################################# ## ## handlers for Qt events ## ############################################################# - def handleCustomContextMenu(self): - pass + def handleCustomContextMenu(self, pt): + m = QtGui.QMenu(self) + self.populateMenu(m) + pt = self.viewport().mapToGlobal(pt) + act = m.exec_(pt) + return - - + + def handleAdjustPriority(self, priority): + """Slot called when a priority change of all currently selected items is triggered + @param priority: one of the Priority* consts + """ + items = self.selectedItems() + if not items: + item = self.currentItem() + if item is not None: + items = (item, ) + + priorityName = self._strings.requestPriorities[priority][0] + for item in items: + item.setRequestPriority(priority, priorityName) + + #TODO: more or less a guess that the item is still in the queue. We don't know + # exactly on context menu for example. Check if the node complains + if item.requestStatus() & DownloadItem.StatusMaskInNodeQueue: + self.adjustPriority(item.requestIdentifier(), priority) + ############################################################# ## ## handlers for Fcp events @@ -398,6 +483,7 @@ (self._fcpClient.events.SimpleProgress, self.handleFcpClientSimpleProgress), (self._fcpClient.events.DataFound, self.handleFcpClientDataFound), (self._fcpClient.events.PersistentGet, self.handleFcpClientPersistentGet), + (self._fcpClient.events.PersistentRequestModified, self.handleFcpClientPersistentRequestModified), (self._fcpClient.events.GetFailed, self.handleFcpClientGetFailed), (self._fcpClient.events.IdentifierCollision, self.handleFcpClientIdentifierCollision), @@ -492,9 +578,10 @@ pass + #TODO: handle priorityClass in clientGetFile() and clientRequestInfo() and sortf() def handleFcpClientIdle(self, event, params): - # check if there are sownloads queued + # check if there are downloads queued n = 0 maxNewDls = self._userSettings['MaxNewDownloadsPerHop'] while self._requests['DownloadQueue'] and n < maxNewDls: @@ -533,9 +620,8 @@ downloadsToBeStarted = self._userSettings['MaxSimultaniousDownloads'] - itemsBusy - - #TODO: sort by priority def sortf(item1, item2): + """Sort function to sort items in queue by priority / time""" return cmp(item1.requestInitTime(), item2.requestInitTime()) # start downloads with info requested complete @@ -548,6 +634,8 @@ item = downloadsRequestInfoComplete.pop(0) filename = os.path.join(self.downloadDirectoryFromItem(item), item.requestName()) identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile) + priorityClass = item.requestPriority() + priorityClass = priorityClass if priorityClass else None # play it save uri = item.requestUri() # tell node to remove completed RequestInfo @@ -555,7 +643,7 @@ item.setRequestIdentifier(identifier) self._adjustItemStatus(item, DownloadItem.StatusDownloading) - self._fcpClient.clientGetFile(uri, filename, identifier=identifier) + self._fcpClient.clientGetFile(uri, filename, identifier=identifier, priorityClass=priorityClass) self._requests['Downloads'][identifier] = item @@ -568,6 +656,8 @@ identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientRequestInfo) item = downloadsPending.pop(0) + priorityClass = item.requestPriority() + priorityClass = priorityClass if priorityClass else None # play it save + pendings may not have a priority uri = item.requestUri() # ClientToken will be set to item parent identifier @@ -579,7 +669,7 @@ item.setRequestIdentifier(identifier) self._adjustItemStatus(item, DownloadItem.StatusRequestInfo) - self._fcpClient.clientRequestInfo(uri, identifier=identifier, clientToken=clientToken) + self._fcpClient.clientRequestInfo(uri, identifier=identifier, clientToken=clientToken, priorityClass=priorityClass) self._requests['Downloads'][identifier] = item @@ -614,7 +704,26 @@ status = DownloadItem.StatusRequestInfo self._adjustItemStatus(item, status) - + + # + fcpPriority = params['PriorityClass'] + priority = self._priorityMapping[fcpPriority] + priorityText = self._strings.requestPriorities[priority][0] + item.setRequestPriority(priority, priorityText) + + + def handleFcpClientPersistentRequestModified(self, event, params): + identifier = params['Identifier'] + item = self._requests['Downloads'].get(identifier, None) + if item is not None: + + fcpPriority = params.get('PriorityClass', None) + if fcpPriority is not None: + priority = self._priorityMapping[fcpPriority] + priorityText = self._strings.requestPriorities[priority][0] + tem.setRequestPriority(priority, priorityText) + + def handleFcpClientProtocolError(self, event, params): identifier = params.get('Identifier', None) @@ -703,10 +812,48 @@ ## methods ## ####################################################### + def adjustPriority(self, identifier, priority): + """Adjusts the priority of a request + @param identifier: identifier of the request + @param priority: one of the Priority* consts + @return: always None + """ + keys, values = self._priorityMapping.keys(), self._priorityMapping.values() + n = values.index(priority) + fcpPriority = keys[n] + self._fcpClient.modifyPersistantRequest(identifier, priorityClass=fcpPriority) + + def download(self, uri, parent=None): self._requests['DownloadQueue'].append( (parent, uri) ) - #TODO: adjust priority? + + + #TODO: how do we get to know when to adjust actions (enable / disable)? + def populateMenu(self, menu): + acts = [] + + # add priorities submenu + # + m = QtGui.QMenu(self.trUtf8('Adjust Priority'), menu) + menu.addMenu(m) + acts.append(m) + + priorities = self._priorityMapping.values() + priorities.sort() + for priority in priorities: + actName = self.ActNameMapping[priority] + actText = self._strings.requestPriorities[priority][1] + act = QtGui.QAction(actText, menu) + cb = lambda method=self.handleAdjustPriority, arg=priority: method(arg) + print act.connect(act, QtCore.SIGNAL('triggered()'), cb) + act._cb = cb + + m.addAction(act) + + + return acts + def retranslate(self): self._strings = DownloadWidgetStrings(self) @@ -808,6 +955,7 @@ if identifier not in self._requests['Downloads']: return identifier + #*************************************************************************************************** # #*************************************************************************************************** @@ -838,7 +986,7 @@ w.setCentralWidget(peers) #m = w.menuBar() - #m1 = m.addMenu('Peers') + #m1 = m.addMenu('Requests') #peers.populateMenu(m1) w.show() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |