SF.net SVN: fclient: [77] trunk/fclient/fclient_widgets/download_widget.py
Status: Pre-Alpha
Brought to you by:
jurner
From: <jU...@us...> - 2007-11-14 11:03:08
|
Revision: 77 http://fclient.svn.sourceforge.net/fclient/?rev=77&view=rev Author: jUrner Date: 2007-11-14 03:03:13 -0800 (Wed, 14 Nov 2007) Log Message: ----------- continued working on menu actions 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 21:00:35 UTC (rev 76) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-14 11:03:13 UTC (rev 77) @@ -80,9 +80,9 @@ #TODO: implement or not ??? IndexLastProgress = 6 IndexDuration = 7 - - - StatusNone = 0x0 + + #TODO: order as appears in groups context menu... needs adjustment + StatusQueued = 0x0 StatusPending = 0x1 StatusRequestInfo = 0x2 StatusRequestInfoComplete = 0x4 @@ -112,7 +112,7 @@ self._requestName = None self._requestNamePostfix = None self._requestPriority = None - self._requestStatus = self.StatusNone + self._requestStatus = self.StatusQueued self._requestInitTime = time.time() self._uri = uri @@ -292,7 +292,8 @@ ] self.headerSections.sort() - self.itemStatus = { + self.itemStatus = { # DownloadItem.Status* --> shortName + DownloadItem.StatusQueued: self.trUtf8('Queued'), DownloadItem.StatusPending: self.trUtf8('Pending'), DownloadItem.StatusRequestInfo: self.trUtf8('Requesting'), DownloadItem.StatusRequestInfoComplete: self.trUtf8('Found'), @@ -303,14 +304,14 @@ DownloadItem.StatusError: self.trUtf8('Error'), } - 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.requestPriorities = { # Priority* --> (shortText, longText) + downloadWidget.PriorityHighest: (self.trUtf8('1'), self.trUtf8('1 Highest')), + downloadWidget.PriorityHigher: (self.trUtf8('2'), self.trUtf8('2 High')), + downloadWidget.PriorityHigh: (self.trUtf8('3'), self.trUtf8('3 Higher')), + downloadWidget.PriorityNormal: (self.trUtf8('4'), self.trUtf8('4 Normal')), + downloadWidget.PriorityLow: (self.trUtf8('5'), self.trUtf8('5 Low')), + downloadWidget.PriorityLower: (self.trUtf8('6'), self.trUtf8('6 Lower')), + downloadWidget.PriorityLowest: (self.trUtf8('7'), self.trUtf8('7 Lowest')), } self.unknown = self.trUtf8('???') @@ -323,16 +324,16 @@ # track by other means. Uh.. how? class DownloadWidget(QtGui.QTreeWidget): - PriorityHighest = 0 - PriorityHigher = 1 - PriorityHigh = 2 - PriorityNormal = 3 - PriorityLow = 4 - PriorityLower = 5 - PriorityLowest = 6 + PriorityHighest = 1 + PriorityHigher = 2 + PriorityHigh = 3 + PriorityNormal = 4 + PriorityLow = 5 + PriorityLower = 6 + PriorityLowest = 7 - # menu action names - ActAdjustPrioritiy = 'DownloadWidgetActAdjustPriority' + # menu and action names for priorities + MenuNameAdjustPrioritiy = 'DownloadWidgetMenuAdjustPriority' ActNamePriorityHighest = 'DownloadWidgetActPriorityHighest' ActNamePriorityHigher = 'DownloadWidgetActPriorityHigh' @@ -342,7 +343,7 @@ ActNamePriorityLower = 'DownloadWidgetActPriorityLower' ActNamePriorityLowest = 'DownloadWidgetActPriorityLowest' - ActNameMapping = { + ActNamePriorityMapping = { PriorityHighest: ActNamePriorityHighest, PriorityHigher: ActNamePriorityHigher, PriorityHigh: ActNamePriorityHigh, @@ -352,7 +353,33 @@ PriorityLowest: ActNamePriorityLowest, } + # menu and action names for groups + MenuNameClearGroup = 'DownloadWidgetMenuClearGroup' + ActNameStatusQueued = 'DownloadWidgetActStatusQueued' + ActNameStatusPending = 'DownloadWidgetActStatusPending' + ActNameStatusRequestInfo = 'DownloadWidgetActStatusRequestInfo' + ActNameStatusRequestInfoComplete = 'DownloadWidgetActStatusRequestInfoComplete' + ActNameStatusDownloading = 'DownloadWidgetActStatusDownloading' + ActNameStatusDownloadComplete = 'DownloadWidgetActStatusDownloadComplete' + + #ActNameStatusStopped = 'DownloadWidgetActStatusStopped' + ActNameStatusError = 'DownloadWidgetActStatusError' + + ActNameStatusMapping = { + DownloadItem.StatusQueued: ActNameStatusQueued, + DownloadItem.StatusPending: ActNameStatusPending, + DownloadItem.StatusRequestInfo: ActNameStatusRequestInfo, + DownloadItem.StatusRequestInfoComplete: ActNameStatusRequestInfoComplete, + DownloadItem.StatusDownloading: ActNameStatusDownloading, + DownloadItem.StatusDownloadComplete: ActNameStatusDownloadComplete, + + #DownloadItem.StatusStopped: ActNameStatusStopped, + DownloadItem.StatusError: ActNameStatusError, + } + + + def __init__(self, parent, connectionName='', @@ -365,6 +392,10 @@ @param directory: (str) directory to sownload items to or None to use default directory @param connectionName: name of the connection to the node @param cfg: (configConfig) instance or None + + @signal: 'hasCurrentItem(bool)' emitted when selection or current item changes. Param is true if there is a + current item or a selection + """ QtGui.QTreeWidget.__init__(self, parent) @@ -395,12 +426,27 @@ self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.connect( - self, - QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), - self.handleCustomContextMenu - ) + self, + QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), + self.handleCustomContextMenu + ) + # used to inform acts about has current item / selection available + self.connect( + self, + QtCore.SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)'), + self.handleCurrentItemChanged + ) + self.connect( + self, + QtCore.SIGNAL('itemSelectionChanged()'), + self.handleCurrentItemChanged + ) + + + + self.retranslate() @@ -420,7 +466,7 @@ if not self._isCreated: self._isCreated = True self._fcpClient = self._cfg.fcpClientManager.newClient(self._connectionName, self.handleFcpClientConnected) - self._fcpClient.setVerbosity(self._fcpClient.Verbosity.Debug) + #self._fcpClient.setVerbosity(self._fcpClient.Verbosity.Debug) # map Fcp priorities to our priorities, in case priority defines change # if self._fcpClient.Version == blah: (...) @@ -441,31 +487,16 @@ ############################################################# def handleCustomContextMenu(self, pt): m = QtGui.QMenu(self) + m.setTearOffEnabled(True) 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) - + + def handleCurrentItemChanged(self, *params): + self.emit(QtCore.SIGNAL('hasCurrentItem(bool)'), self.hasCurrentItem()) + ############################################################# ## ## handlers for Fcp events @@ -573,12 +604,24 @@ #raise self._fcpClient.FetchError(params) - #TODO: not yet handled + #TODO: not tested!! def handleFcpClientIdentifierCollision(self, event, params): - pass - - - #TODO: handle priorityClass in clientGetFile() and clientRequestInfo() and sortf() + identifier = params['Identifier'] + item = self._requests['Downloads'].get(identifier, None) + if item is not None: + del self._requests['Downloads'][identifier] + + if self._fcpClient.isClientGetFile(identifier): + self._startDownload(item) + + elif isClientRequestInfo(identifier): + self._startRequestInfo(item) + + else: + #TODO: more here + pass + + def handleFcpClientIdle(self, event, params): # check if there are downloads queued @@ -622,7 +665,10 @@ def sortf(item1, item2): """Sort function to sort items in queue by priority / time""" - return cmp(item1.requestInitTime(), item2.requestInitTime()) + result = cmp(item1.requestPriority(), item2.requestPriority()) + if not result: + result = cmp(item1.requestInitTime(), item2.requestInitTime()) + return result # start downloads with info requested complete if downloadsToBeStarted > 0: @@ -632,21 +678,10 @@ downloadsToBeStarted -= 1 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 + # tell node to remove info complete self._fcpClient.removePersistentRequest(item.requestIdentifier()) - - item.setRequestIdentifier(identifier) - self._adjustItemStatus(item, DownloadItem.StatusDownloading) - self._fcpClient.clientGetFile(uri, filename, identifier=identifier, priorityClass=priorityClass) - - self._requests['Downloads'][identifier] = item - + self._startDownload(item) + # start pending downloads if downloadsToBeStarted > 0: downloadsPending = items[DownloadItem.StatusPending] @@ -654,33 +689,18 @@ while downloadsPending and downloadsToBeStarted > 0: downloadsToBeStarted -= 1 - 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 - parent = item.parent() - if parent is None: - clientToken = None - else: - clientToken = parent.requestIdentifier() - - item.setRequestIdentifier(identifier) - self._adjustItemStatus(item, DownloadItem.StatusRequestInfo) - self._fcpClient.clientRequestInfo(uri, identifier=identifier, clientToken=clientToken, priorityClass=priorityClass) - - self._requests['Downloads'][identifier] = item - + self._startRequestInfo(item) + def handleFcpClientPersistentGet(self, event, params): identifier = params['Identifier'] item = self._requests['Downloads'].get(identifier, None) if item is None: - #self._fcpClient.removePersistentRequest(identifier) - #return + # uncommment for testing... removes all requests the node passes on connect + ##self._fcpClient.removePersistentRequest(identifier) + ##return #TODO: no idea if the node passes PersistentGet messages in insertion order # ..if not, we will have to cache items and wait for parent item to arrive @@ -812,46 +832,110 @@ ## methods ## ####################################################### - def adjustPriority(self, identifier, priority): - """Adjusts the priority of a request - @param identifier: identifier of the request + def adjustPriority(self, priority): + """Adjusts the priority of all currently selected items or if no items are selected the current item @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) + 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._fcpClient.modifyPersistentRequest( + item.requestIdentifier(), + self._prorityToFcpPriority(priority) + ) + + + + #TODO: not yet implemented + #TODO: check if we don't run into race conditions when called from actions + def clearGroup(self, status): + """Removes all items with the specified status + @param status: DownloadItem.Status* + """ + + def download(self, uri, parent=None): self._requests['DownloadQueue'].append( (parent, uri) ) + + def hasCurrentItem(self): + """Checks if there is a current item or a selection + @return: (bool) + """ + hasCurrentItem = True + if self.currentItem() is None: + if not self.selectedItems(): + hasCurrentItem = False + return hasCurrentItem - #TODO: how do we get to know when to adjust actions (enable / disable)? + def populateMenu(self, menu): + """Populates a menu with all available actions + @return: (list) containing actions and emidiate submenus added to the menu + """ + acts = [] # add priorities submenu # - m = QtGui.QMenu(self.trUtf8('Adjust Priority'), menu) + hasCurrentItem = self.hasCurrentItem() + m = QtGui.QMenu(self.trUtf8('Adjust priority'), menu) + m.setObjectName(self.MenuNameAdjustPrioritiy) + m.setTearOffEnabled(True) + m.setEnabled(hasCurrentItem) + menu.addMenu(m) acts.append(m) + m.connect(self, QtCore.SIGNAL('hasCurrentItem(bool)'), m.setEnabled) priorities = self._priorityMapping.values() priorities.sort() for priority in priorities: - actName = self.ActNameMapping[priority] + actName = self.ActNamePriorityMapping[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 - + act.setObjectName(actName) + act.setEnabled(hasCurrentItem) + cb = lambda method=self.adjustPriority, arg=priority: method(arg) + act.connect(act, QtCore.SIGNAL('triggered()'), cb) + act._downloadWidgetCb = cb + act.connect(self, QtCore.SIGNAL('hasCurrentItem(bool)'), act.setEnabled) m.addAction(act) + # add clear groups menu + # + m = QtGui.QMenu(self.trUtf8('Clear group'), menu) + m.setObjectName(self.MenuNameClearGroup) + m.setTearOffEnabled(True) + + menu.addMenu(m) + acts.append(m) + + groups = self.ActNameStatusMapping.items() + groups.sort() + for status, actName in groups: + actText = self._strings.itemStatus[status] + act = QtGui.QAction(actText, menu) + cb = lambda method=self.clearGroup, arg=status: method(arg) + act.connect(act, QtCore.SIGNAL('triggered()'), cb) + act._downloadWidgetCb = cb + m.addAction(act) + + return acts @@ -945,6 +1029,55 @@ return item + def _prorityToFcpPriority(self, priority): + keys, values = self._priorityMapping.keys(), self._priorityMapping.values() + n = values.index(priority) + return keys[n] + + + def _startDownload(self, item): + """Starts downloading an item + @param item: (DownloadItem) + @note: no check is done to enshure the item is not already being downloaded + """ + 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() + + item.setRequestIdentifier(identifier) + self._adjustItemStatus(item, DownloadItem.StatusDownloading) + self._fcpClient.clientGetFile(uri, filename, identifier=identifier, priorityClass=priorityClass) + + self._requests['Downloads'][identifier] = item + + + def _startRequestInfo(self, item): + """Starts requesting info for an item + @param item: (DownloadItem) + @note: no check is done to enshure the item is not already being requested + """ + identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientRequestInfo) + 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 + parent = item.parent() + if parent is None: + clientToken = None + else: + clientToken = parent.requestIdentifier() + + item.setRequestIdentifier(identifier) + self._adjustItemStatus(item, DownloadItem.StatusRequestInfo) + self._fcpClient.clientRequestInfo(uri, identifier=identifier, clientToken=clientToken, priorityClass=priorityClass) + + self._requests['Downloads'][identifier] = item + + + def _uniqueDownloadsIdentifier(self, identifierPrefix): """Creates a new identifier that is unique to the internal sownloads dict @param identifierPrefix: FcpClient.IdentifierPrefix.* This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |