Thread: SF.net SVN: fclient: [51] trunk/fclient/fclient_widgets/download_widget.py
Status: Pre-Alpha
Brought to you by:
jurner
From: <jU...@us...> - 2007-11-06 12:39:15
|
Revision: 51 http://fclient.svn.sourceforge.net/fclient/?rev=51&view=rev Author: jUrner Date: 2007-11-06 04:39:13 -0800 (Tue, 06 Nov 2007) Log Message: ----------- started implementing a download widget Added Paths: ----------- trunk/fclient/fclient_widgets/download_widget.py Added: trunk/fclient/fclient_widgets/download_widget.py =================================================================== --- trunk/fclient/fclient_widgets/download_widget.py (rev 0) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-06 12:39:13 UTC (rev 51) @@ -0,0 +1,358 @@ +"""Sketch for a download widget + +""" + + +import os, sys + +#--> rel import hack +def parentdir(n, fpath): + fpath = os.path.abspath(fpath) + for i in xrange(n): + fpath = os.path.dirname(fpath) + return fpath +sys.path.insert(0, parentdir(2, __file__)) + +import config +from fclient_lib import fcp +#from fclient_dlgs import frdx_viewer +from fclient_lib.pyex import namespace, numbers +#from fclient_lib.qt4ex import settingsbase +#from fclient_lib.qt4ex.ctrls import progressbarwrap + +sys.path.pop(0) +del parentdir +#<-- rel import hack + + +import thread +from PyQt4 import QtCore, QtGui +#***************************************************************************** +# +#***************************************************************************** +class DownloadItem(QtGui.QTreeWidgetItem): + + # default indices + IndexName = 0 + IndexStatus = 1 + IndexProgress = 2 + IndexSize = 3 + IndexMimeType = 4 + IndexPriority = 5 + + ###################### + IndexLastProgress = 6 + IndexDuration = 7 + + + def __init__(self, parent, requestIdentifier, uri): + QtGui.QTreeWidgetItem.__init__(self, parent) + + self._requestIdentifier = requestIdentifier + self._uri = uri + + + def setRequestIdentifier(self, identifier): + self._requestIdentifier = identifier + + def requestIdentifier(self): + return self._requestIdentifier + + def setRequestMimeType(self, mimeType): + self.setText(self.IndexMimeType, mimeType) + + def requestMimeType(self): + return self.text(self.IndexMimeType) + + def setRequestName(self, name): + self.setText(self.IndexName, name) + + def requestName(self): + return self.text(self.IndexName) + + def setRequestPriority(self, priority): + self.setText(self.IndexPriority, priority) + + def requestPriority(self): + return self.text(self.IndexPriority) + + def setProgressWidget(self, tree, widget): + tree.setItemWidget(self, self.IndexProgress, widget) + + def progressWidget(self, tree): + return tree.itemWidget(self, self.IndexProgress) + + def setRequestStatus(self, status): + self.setText(self.IndexStatus, status) + + def requestStatus(self): + return self.text(self.IndexStatus) + + def setRequestSize(self, size): + self.setText(self.IndexSize, size) + + def requestSize(self): + return self.text(self.IndexSize) + + def setRequestUri(self, uri): + self._uri = uri + + def requestUri(self): + return self._uri + +#***************************************************************************** +# +#***************************************************************************** +class DownloadWidgetStrings(QtCore.QObject): + + + def __init__(self, parent): + QtCore.QObject.__init__(self, parent) + + + self.headerSections = [ + (DownloadItem.IndexName, self.trUtf8('Name')), + (DownloadItem.IndexStatus, self.trUtf8('Status')), + (DownloadItem.IndexProgress, self.trUtf8('Progress')), + (DownloadItem.IndexSize, self.trUtf8('Size')), + (DownloadItem.IndexMimeType, self.trUtf8('Content')), + (DownloadItem.IndexPriority, self.trUtf8('Priority')), + (DownloadItem.IndexLastProgress, self.trUtf8('Last Progress')), + (DownloadItem.IndexDuration, self.trUtf8('Duration')), + ] + self.headerSections.sort() + + +#**************************************************************************** +# +#**************************************************************************** +class DownloadWidget(QtGui.QTreeWidget): + + def __init__(self, + parent, + connectionName='', + directory=None, + uris=None, + maxQueuedItemsPerHop=200, + maxSimultaniousDownloads=2, + cfg=None + ): + """ + @param parent: (QWidget) parent or None + @param directory: (str) directory to sownload items to or None to use default directory + @param connectionName: name of the connection to the node + @param maxQueuedItemsPerHop: maximum number of items that can be e added in one hop + @param maxSimultaniousDownloads: maximim number of simultaneous downloads + @param cfg: (configConfig) instance or None + """ + + QtGui.QTreeWidget.__init__(self, parent) + + self._connectionName = connectionName + self._cfg = cfg if cfg is not None else config.Config(self) + self._directory = directory if directory is not None else self._cfg.defaultDownloadDir + self._downloads = { + 'Downloads': {}, # identifier --> item / items currently downloading + 'DownloadQueue': [], # (parent-item, uri) / items scheduled for download + 'DownloadDirectories': {}, # directory --> (parentItem, uris) / directories scheduled for testDDA + } + self._fcpClient = None + self._isCreated = False + self._lock = thread.allocate_lock() + self._maxQueuedItemsPerHop = maxQueuedItemsPerHop #TODO: move to settings + self._maxSimultaniousDownloads = maxSimultaniousDownloads #TODO: move to settings + self._strings = DownloadWidgetStrings(self) + + # timer to add scheduled downloads (add maxQueuedItemsPerHop / hop to avoid flooding of the tree) + self._timer = QtCore.QTimer(self) + self._timer.setInterval(300) # arbitrary delay + self.connect( + self._timer, + QtCore.SIGNAL('timeout()'), + self.addQueuedItems + ) + + # setup tree + self.setHeaderLabels([i[1] for i in self._strings.headerSections]) + #self.setColumnCount(len(self._strings.HeaderSections)) + self.setSelectionMode(self.ContiguousSelection) + + header = self.header() + header.setResizeMode(header.ResizeToContents) + header.setStretchLastSection(True) + + self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.connect( + self, + QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), + self.handleContextMenu + ) + + # schedule initial uris + if uris is not None: + self._downloads['DownloadDirectories'][self._directory] = (self, uris) + + + def showEvent(self, event): + if not self._isCreated: + self._isCreated = True + self._fcpClient = self._cfg.fcpClientManager.newClient(self._connectionName, self.handleClientConnected) + + ############################################################# + ## + ## handlers for Qt events + ## + ############################################################# + def handleContextMenu(self): + pass + + + + ############################################################# + ## + ## handlers for Fcp events + ## + ############################################################# + def handleClientConnected(self, event, params): + """ + """ + self._fcpEvents = ( + (self._fcpClient.events.ClientDisconnected, self.handleClientDisconnected), + + (self._fcpClient.events.TestDDAComplete, self.handleTestDDAComplete), + ) + # take care not to connect twice + for event, observer in self._fcpEvents: + if not observer in event: + event += observer + + # register directories + for directory in self._downloads['DownloadDirectories']: + self._fcpClient.testDDA(directory, wantWriteDirectory=True) + + + def handleClientDisconnected(self, event, params): + """ + """ + + def handleTestDDAComplete(self, event, params): + """ + """ + directory = params['Directory'] + readAllowed = params.get('WriteDirectoryAllowed') + writeAllowed = params.get('WriteDirectoryAllowed') + + # check if there are items to be donloaded for the directory + downloads = None + self._lock.acquire(True) + try: + downloads = self._downloads['DownloadDirectories'].get(directory, None) + if downloads is not None: + del self._downloads['DownloadDirectories'][directory] # ??? + finally: + self._lock.release() + + if downloads: + if writeAllowed == self._fcpClient.FcpTrue: + parent, uris = downloads + for uri in uris: + self.download(uri, parent=parent) + + else: + pass + #TODO: write access denied, error + + ####################################################### + ## + ## methods + ## + ####################################################### + def addQueuedItems(self): + + self._timer.stop() + + self._lock.acquire(True) + try: + n = 0 + while self._downloads['DownloadQueue'] and n < self._maxQueuedItemsPerHop: + n += 1 + + parent, uri = self._downloads['DownloadQueue'].pop(0) + item = DownloadItem(parent, '', uri) + fcpUri = self._fcpClient.FcpUri(uri) + fileName = fcpUri.fileName() + fileName = namespace.unquote_uri(fileName) + + item.setRequestName(fileName) + + #... more here + + if self._downloads['DownloadQueue']: + self._timer.start() + + finally: + self._lock.release() + + + def download(self, uri, parent=None): + self._lock.acquire(True) + try: + self._downloads['DownloadQueue'].append( (parent, uri) ) + finally: + self._lock.release() + + if not self._timer.isActive(): + self._timer.start() + + + def retranslate(self): + pass + +#*************************************************************************************************** +# +#*************************************************************************************************** +if __name__ == '__main__': + import sys + + TestUris = '''CHK@sdNenKGj5mupxaSwo44jcW8dsX7vYTLww~BsRPtur0k,ZNRm9reMjtKEl9e-xFByKXbW6q4f6OQyfg~l9GRSAes,AAIC--8/snow_002%20%281%29.jpg +CHK@q4~2soHTd9SOINIoXmg~dn7LNUAOYzN1tHNHT3j4c9E,gcVRtoglEhgqN-DJolXPqJ4yX1f~1gBGh89HNWlFMWQ,AAIC--8/snow_002%20%2810%29.jpg +CHK@6tw-vrINDoNgSY9pTq-88n2sOAhREv1iFc3T0E18xHk,NxJxo9d7spLHCqGsMyY26XtXpUACPKTJq9v~WxVp3ic,AAIC--8/snow_002%20%2811%29.jpg +CHK@nOb-ZM4h1SNpoG3E0xcOPnNDydSf~y2DON~R80exAtU,2o6smJYijaftLzMz4iva24T1geEAnDFn~bIMKFkzU30,AAIC--8/snow_002%20%2812%29.jpg +CHK@XWyYvxoirEKiLWhnSVdKffSHT~nnKWsVZ5tJRKiFxBk,mK9-g5AeCvcNue1wYHRhc1Hdj3-yn77Vyu9Tq7XXJTM,AAIC--8/snow_002%20%2813%29.jpg +CHK@AbQOZRSJcgXnKOI16D2hF8qkjxp3cl1u72bkL2KS29E,jjQlvojCkJJgzI2WgWj5rMsmUd935zjGChiV-j937X0,AAIC--8/snow_002%20%2814%29.jpg +CHK@1ZW0fOpB7RmUFqSO2TtAOyfFm1uJ6~JcWuPESiYwtc4,aCDkI~JCr3Mb8s7rPBgmalPqzQEFdovlF~DgDRkGWuE,AAIC--8/snow_002%20%2815%29.jpg +CHK@a8jHCl0TkwVAcTdYOezd-FtIWTLIFhjrWeHXVcSjRPk,4rIwDZE~-Gp~B5tjDSAcSpn~tLgMmnMUFeikXP4f7y0,AAIC--8/snow_002%20%2816%29.jpg +CHK@Jg~F~~WbKu8AlBgmwkonWGQNzZFUholE2CkGnyt23kk,Gj7JtnR97EIRvabG8m6Xp1dUAqiP6qD4J5FJRN9waMQ,AAIC--8/snow_002%20%2836%29.jpg''' + TestUris = TestUris.split('\n') + + + + app = QtGui.QApplication(sys.argv) + w = QtGui.QMainWindow() + peers = DownloadWidget(None, + connectionName='TestDownloadWidget', + uris=TestUris + + ) + w.setCentralWidget(peers) + + #m = w.menuBar() + #m1 = m.addMenu('Peers') + #peers.populateMenu(m1) + + w.show() + res = app.exec_() + sys.exit(res) + + + + + + + + + + + + + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-07 17:35:29
|
Revision: 57 http://fclient.svn.sourceforge.net/fclient/?rev=57&view=rev Author: jUrner Date: 2007-11-07 09:35:34 -0800 (Wed, 07 Nov 2007) Log Message: ----------- continued working on downloads widget 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-07 17:35:00 UTC (rev 56) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-07 17:35:34 UTC (rev 57) @@ -17,15 +17,21 @@ from fclient_lib import fcp #from fclient_dlgs import frdx_viewer from fclient_lib.pyex import namespace, numbers + + #from fclient_lib.qt4ex import settingsbase -#from fclient_lib.qt4ex.ctrls import progressbarwrap +from fclient_lib.qt4ex.ctrls import progressbarwrap +from fclient_lib.qt4ex.ctrls import treewidgetwrap + + sys.path.pop(0) del parentdir #<-- rel import hack import thread +import time from PyQt4 import QtCore, QtGui #***************************************************************************** # @@ -40,15 +46,31 @@ IndexMimeType = 4 IndexPriority = 5 - ###################### + #TODO: implement or not ??? IndexLastProgress = 6 IndexDuration = 7 + StatusNone = 0x0 + StatusPending = 0x1 + StatusRequestInfo = 0x2 + StatusRequestInfoComplete = 0x4 + StatusDownloading = 0x8 + StatusDownloadComplete = 0x10 + + StatusError = 0x10000 + + StatusMaskBusy = StatusRequestInfo | StatusDownloading + + + + def __init__(self, parent, requestIdentifier, uri): QtGui.QTreeWidgetItem.__init__(self, parent) self._requestIdentifier = requestIdentifier + self._requestStatus = self.StatusNone + self._requestInitTime = time.time() self._uri = uri @@ -58,6 +80,9 @@ def requestIdentifier(self): return self._requestIdentifier + def requestInitTime(self): + return self._requestInitTime + def setRequestMimeType(self, mimeType): self.setText(self.IndexMimeType, mimeType) @@ -82,12 +107,13 @@ def progressWidget(self, tree): return tree.itemWidget(self, self.IndexProgress) - def setRequestStatus(self, status): - self.setText(self.IndexStatus, status) + def setRequestStatus(self, status, text=''): + self._requestStatus = status + self.setText(self.IndexStatus, text) def requestStatus(self): - return self.text(self.IndexStatus) - + return self._requestStatus + def setRequestSize(self, size): self.setText(self.IndexSize, size) @@ -122,6 +148,19 @@ ] self.headerSections.sort() + self.itemStatus = { + DownloadItem.StatusPending: self.trUtf8('Pending'), + DownloadItem.StatusRequestInfo: self.trUtf8('Requesting'), + DownloadItem.StatusRequestInfoComplete: self.trUtf8('Found'), + DownloadItem.StatusDownloading: self.trUtf8('Loading'), + DownloadItem.StatusDownloadComplete: self.trUtf8('Complete'), + + DownloadItem.StatusError: self.trUtf8('Error'), + } + + + self.unknown = self.trUtf8('Unknown') + #**************************************************************************** # @@ -161,24 +200,12 @@ self._lock = thread.allocate_lock() self._maxQueuedItemsPerHop = maxQueuedItemsPerHop #TODO: move to settings self._maxSimultaniousDownloads = maxSimultaniousDownloads #TODO: move to settings - self._strings = DownloadWidgetStrings(self) - - # timer to add scheduled downloads (add maxQueuedItemsPerHop / hop to avoid flooding of the tree) - self._timer = QtCore.QTimer(self) - self._timer.setInterval(300) # arbitrary delay - self.connect( - self._timer, - QtCore.SIGNAL('timeout()'), - self.addQueuedItems - ) - + self._strings = None + # setup tree - self.setHeaderLabels([i[1] for i in self._strings.headerSections]) - #self.setColumnCount(len(self._strings.HeaderSections)) self.setSelectionMode(self.ContiguousSelection) - header = self.header() - header.setResizeMode(header.ResizeToContents) + #header.setResizeMode(header.ResizeToContents) header.setStretchLastSection(True) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) @@ -188,7 +215,11 @@ self.handleContextMenu ) - # schedule initial uris + + self.retranslate() + + + # schedule initial uris for download if uris is not None: self._downloads['DownloadDirectories'][self._directory] = (self, uris) @@ -217,9 +248,12 @@ """ """ self._fcpEvents = ( + (self._fcpClient.events.Idle, self.handleClientIdle), (self._fcpClient.events.ClientDisconnected, self.handleClientDisconnected), (self._fcpClient.events.TestDDAComplete, self.handleTestDDAComplete), + (self._fcpClient.events.ClientGetInfo, self.handleClientGetInfo), + (self._fcpClient.events.ClientGetInfoProgress, self.handleClientGetInfoProgress), ) # take care not to connect twice for event, observer in self._fcpEvents: @@ -230,7 +264,82 @@ for directory in self._downloads['DownloadDirectories']: self._fcpClient.testDDA(directory, wantWriteDirectory=True) + + + def handleClientIdle(self, event, msg): + + # check if there are sownloads queued + n = 0 + while self._downloads['DownloadQueue'] and n < self._maxQueuedItemsPerHop: + n += 1 + + parent, uri = self._downloads['DownloadQueue'].pop(0) + item = DownloadItem(parent, '', uri) + fcpUri = self._fcpClient.FcpUri(uri) + fileName = fcpUri.fileName() + fileName = namespace.unquote_uri(fileName) + status = DownloadItem.StatusPending + + item.setRequestName(fileName) + item.setRequestStatus(status, self._strings.itemStatus[status]) + + #... more here + + # check how many downloads are currently running + items = { + DownloadItem.StatusPending: [], + DownloadItem.StatusRequestInfoComplete: [], + } + itemsBusy = 0 + enum = treewidgetwrap.TreeWidgetIterator(self) + for item in enum: + status = item.requestStatus() + if status & item.StatusMaskBusy: + itemsBusy += 1 + elif status & item.StatusPending: + items[status].append(item) + elif status & item.StatusRequestInfoComplete: + items[status].append(item) + + # start items with info requested + itemsToBeStarted = self._maxSimultaniousDownloads - itemsBusy + if itemsToBeStarted > 0: + def sortf(item1, item2): + return cmp(item1.requestInitTime(), item2.requestInitTime()) + + infoCompleteItems = items[DownloadItem.StatusRequestInfoComplete] + infoCompleteItems.sort(cmp=sortf) + while infoCompleteItems: + + #TODO: not yet implemented + break + itemsToBeStarted -= 1 + + + # start pending items + if itemsToBeStarted > 0: + pendingItems = items[DownloadItem.StatusPending] + pendingItems.sort(cmp=sortf) + while pendingItems: + itemsToBeStarted -= 1 + if itemsToBeStarted < 0: + break + + item = pendingItems.pop(0) + progress = progressbarwrap.ProgressBarEx(self) + uri = item.requestUri() + + + progress.setRange(0, 0) + progress.setValue(0) + item.setProgressWidget(self, progress) + + identifier = self._fcpClient.clientGetInfo(uri) + self._downloads['Downloads'][identifier] = item + + + def handleClientDisconnected(self, event, params): """ """ @@ -243,14 +352,9 @@ writeAllowed = params.get('WriteDirectoryAllowed') # check if there are items to be donloaded for the directory - downloads = None - self._lock.acquire(True) - try: - downloads = self._downloads['DownloadDirectories'].get(directory, None) - if downloads is not None: - del self._downloads['DownloadDirectories'][directory] # ??? - finally: - self._lock.release() + downloads = self._downloads['DownloadDirectories'].get(directory, None) + if downloads is not None: + del self._downloads['DownloadDirectories'][directory] # ??? if downloads: if writeAllowed == self._fcpClient.FcpTrue: @@ -261,52 +365,60 @@ else: pass #TODO: write access denied, error - + + + def handleClientGetInfo(self, event, params): + + identifier = params['Identifier'] + item = self._downloads['Downloads'].get(identifier, None) + if item is not None: + del self._downloads['Downloads'][identifier] + + status = DownloadItem.StatusRequestInfoComplete + mimeType = params.get('Metadata.ContentType', self._strings.unknown) + dataLength = params.get('DataLength', '') + try: + dataLength = int(dataLength) + except ValueError: + dataLength = self._strings.unknown + else: + dataLength = numbers.format_num_bytes(dataLength) + + item.setRequestStatus(status, self._strings.itemStatus[status]) + item.setRequestMimeType(mimeType) + item.setRequestSize(dataLength) + + + + def handleClientGetInfoProgress(self, event, params): + + identifier = params['Identifier'] + item = self._downloads['Downloads'].get(identifier, None) + if item is not None: + progress = item.progressWidget(self) + required=int(params['Required']) + succeeded=int(params['Succeeded']) + + if required != progress.maximum(): + progress.setRange(0, required) + progress.setValue(succeeded) + ####################################################### ## ## methods ## ####################################################### - def addQueuedItems(self): - - self._timer.stop() - - self._lock.acquire(True) - try: - n = 0 - while self._downloads['DownloadQueue'] and n < self._maxQueuedItemsPerHop: - n += 1 - - parent, uri = self._downloads['DownloadQueue'].pop(0) - item = DownloadItem(parent, '', uri) - fcpUri = self._fcpClient.FcpUri(uri) - fileName = fcpUri.fileName() - fileName = namespace.unquote_uri(fileName) - - item.setRequestName(fileName) - - #... more here - - if self._downloads['DownloadQueue']: - self._timer.start() - - finally: - self._lock.release() - - def download(self, uri, parent=None): - self._lock.acquire(True) - try: - self._downloads['DownloadQueue'].append( (parent, uri) ) - finally: - self._lock.release() + self._downloads['DownloadQueue'].append( (parent, uri) ) + #TODO: adjust priority? - if not self._timer.isActive(): - self._timer.start() - - + def retranslate(self): - pass + self._strings = DownloadWidgetStrings(self) + self.setHeaderLabels([i[1] for i in self._strings.headerSections]) + + #TODO: retranslate item status + #*************************************************************************************************** # This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-08 11:13:49
|
Revision: 59 http://fclient.svn.sourceforge.net/fclient/?rev=59&view=rev Author: jUrner Date: 2007-11-08 03:13:54 -0800 (Thu, 08 Nov 2007) Log Message: ----------- continued working on downloads widget 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-08 11:12:56 UTC (rev 58) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-08 11:13:54 UTC (rev 59) @@ -19,7 +19,7 @@ from fclient_lib.pyex import namespace, numbers -#from fclient_lib.qt4ex import settingsbase +from fclient_lib.qt4ex import settingsbase from fclient_lib.qt4ex.ctrls import progressbarwrap from fclient_lib.qt4ex.ctrls import treewidgetwrap @@ -36,7 +36,33 @@ #***************************************************************************** # #***************************************************************************** +class UserSettings(settingsbase.SettingsBase): + """Settings for the download widget""" + + #TODO: impl dynamic setting of key for multiple widgets per app + KEY_SETTINGS = 'DownloadsWidgetUser' + SETTINGS = ( + ('ColorProgressBarRequestInfo', ['toString', 'cyan']), + ('ColorProgressBgRequestInfo', ['toString', 'white']), + ('ColorProgressBarRequestInfoComplete', ['toString', 'darkCyan']), + ('ColorProgressBgRequestInfoComplete', ['toString', 'white']), + ('ColorProgressBarDownloading', ['toString', 'blue']), + ('ColorProgressBgDownloading', ['toString', 'white']), + ('ColorProgressBarDownloadComplete', ['toString', 'green']), + ('ColorProgressBgDownloadComplete', ['toString', 'white']), + ('ColorProgressBarError', ['toString', 'red']), + ('ColorProgressBgError', ['toString', 'white']), + ('ColorProgressBarStopped', ['toString', 'gray']), + ('ColorProgressBarStopped', ['toString', 'white']), + + ('ClearCompletedDownloads', ['toBool', False]), + ) + +#***************************************************************************** +# +#***************************************************************************** class DownloadItem(QtGui.QTreeWidgetItem): + """Tree item""" # default indices IndexName = 0 @@ -57,81 +83,138 @@ StatusRequestInfoComplete = 0x4 StatusDownloading = 0x8 StatusDownloadComplete = 0x10 + + StatusStopped = 0x1000 + StatusError = 0x20000 - StatusError = 0x10000 - StatusMaskBusy = StatusRequestInfo | StatusDownloading + - - - def __init__(self, parent, requestIdentifier, uri): + """ + @param parent: parent of the item + @param requestIdentifier: (str) request identifier + @param uri: (str) uri to download + """ + QtGui.QTreeWidgetItem.__init__(self, parent) + self._requestError = None self._requestIdentifier = requestIdentifier self._requestStatus = self.StatusNone self._requestInitTime = time.time() self._uri = uri - + + def setRequestError(self, event, params): + """If an error occures, sets info about the error + @param event: event that triggered the error + @param params: params passed along with the event + """ + self._requestError = (event, params) + + def requestError(self): + """Return information about the last error encountered or None""" + return self._requestError + def setRequestIdentifier(self, identifier): + """Sets the request identifier of the item + @param requestIdentifier: (str) request identifier + """ self._requestIdentifier = identifier def requestIdentifier(self): + """Returns the request identifier of the item""" return self._requestIdentifier def requestInitTime(self): + """Returns the time the request was initialized + @return: (float) time + """ return self._requestInitTime def setRequestMimeType(self, mimeType): + """Sets the mime type of the item + @param mimeType: (str) mime type + """ self.setText(self.IndexMimeType, mimeType) def requestMimeType(self): + """Returns the mime type of the item""" return self.text(self.IndexMimeType) def setRequestName(self, name): + """Sets the request name of the item + @param name: (str) name + """ self.setText(self.IndexName, name) def requestName(self): + """Returns the request name of the item + """ return self.text(self.IndexName) def setRequestPriority(self, priority): + """Sets the request priority of the item + @param prority: (FcpClient.Priority) + """ self.setText(self.IndexPriority, priority) def requestPriority(self): + """Returns the request priority of the item""" return self.text(self.IndexPriority) def setProgressWidget(self, tree, widget): + """Associates a progress widget to the item + @param tree: (QTreeWidget) + @param widget: (QProgressBar) + """ tree.setItemWidget(self, self.IndexProgress, widget) def progressWidget(self, tree): + """Returns the progress widget associated to the item""" return tree.itemWidget(self, self.IndexProgress) def setRequestStatus(self, status, text=''): + """Sets the request status of the item + @param status: one of the Status* consts + @param text: status text to display + """ self._requestStatus = status self.setText(self.IndexStatus, text) def requestStatus(self): + """Returns the request status of the item + @return: (int) one of the Status* consts + """ return self._requestStatus def setRequestSize(self, size): + """Sets the request size of the item + @param size: (str) size + """ self.setText(self.IndexSize, size) def requestSize(self): + """Returns the request size of the item""" return self.text(self.IndexSize) def setRequestUri(self, uri): + """Sets the request uri of the item + @param uri: (str) uri + """ self._uri = uri def requestUri(self): + """Returns the request uri of the item""" return self._uri #***************************************************************************** # #***************************************************************************** class DownloadWidgetStrings(QtCore.QObject): - - + """Strings for the download widget""" + def __init__(self, parent): QtCore.QObject.__init__(self, parent) @@ -155,6 +238,7 @@ DownloadItem.StatusDownloading: self.trUtf8('Loading'), DownloadItem.StatusDownloadComplete: self.trUtf8('Complete'), + DownloadItem.StatusStopped: self.trUtf8('Stopped'), DownloadItem.StatusError: self.trUtf8('Error'), } @@ -202,6 +286,8 @@ self._maxSimultaniousDownloads = maxSimultaniousDownloads #TODO: move to settings self._strings = None + self._userSettings = UserSettings() + # setup tree self.setSelectionMode(self.ContiguousSelection) header = self.header() @@ -212,7 +298,7 @@ self.connect( self, QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), - self.handleContextMenu + self.handleCustomContextMenu ) @@ -227,14 +313,14 @@ def showEvent(self, event): if not self._isCreated: self._isCreated = True - self._fcpClient = self._cfg.fcpClientManager.newClient(self._connectionName, self.handleClientConnected) + self._fcpClient = self._cfg.fcpClientManager.newClient(self._connectionName, self.handleFcpClientConnected) ############################################################# ## ## handlers for Qt events ## ############################################################# - def handleContextMenu(self): + def handleCustomContextMenu(self): pass @@ -244,16 +330,19 @@ ## handlers for Fcp events ## ############################################################# - def handleClientConnected(self, event, params): + def handleFcpClientConnected(self, event, params): """ """ self._fcpEvents = ( - (self._fcpClient.events.Idle, self.handleClientIdle), - (self._fcpClient.events.ClientDisconnected, self.handleClientDisconnected), + (self._fcpClient.events.Idle, self.handleFcpClientIdle), + (self._fcpClient.events.ClientDisconnected, self.handleFcpClientDisconnected), - (self._fcpClient.events.TestDDAComplete, self.handleTestDDAComplete), - (self._fcpClient.events.ClientGetInfo, self.handleClientGetInfo), - (self._fcpClient.events.ClientGetInfoProgress, self.handleClientGetInfoProgress), + (self._fcpClient.events.TestDDAComplete, self.handleFcpClientTestDDAComplete), + (self._fcpClient.events.ClientRequestInfo, self.handleFcpClientRequestInfo), + (self._fcpClient.events.ClientRequestInfoProgress, self.handleFcpClientRequestInfoProgress), + + (self._fcpClient.events.GetFailed, self.handleFcpClientGetFailed), + (self._fcpClient.events.ProtocolError, self.handleFcpClientProtocolError), ) # take care not to connect twice for event, observer in self._fcpEvents: @@ -265,9 +354,32 @@ self._fcpClient.testDDA(directory, wantWriteDirectory=True) - - def handleClientIdle(self, event, msg): + def handleFcpClientDisconnected(self, event, params): + """ + """ + + #TODO: how to provide extra information to the user? + def handleFcpClientGetFailed(self, event, params): + identifier = params['Identifier'] + item = self._downloads['Downloads'].get(identifier, None) + if item is not None: + del self._downloads['Downloads'][identifier] + progress = item.progressWidget(self) + status = DownloadItem.StatusError + + progress.setRange(0, 1) + progress.setValue(1) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarError'], + colorBg=self._userSettings['ColorProgressBgError'] + ) + item.setRequestStatus(status, self._strings.itemStatus[status]) + item.setRequestError(event, params) + + + def handleFcpClientIdle(self, event, params): + # check if there are sownloads queued n = 0 while self._downloads['DownloadQueue'] and n < self._maxQueuedItemsPerHop: @@ -287,6 +399,9 @@ # check how many downloads are currently running + + #TODO: bit sloppy coding here. We know how max downloads to start, + # so should be not too hard to keep the lists short items = { DownloadItem.StatusPending: [], DownloadItem.StatusRequestInfoComplete: [], @@ -329,46 +444,33 @@ item = pendingItems.pop(0) progress = progressbarwrap.ProgressBarEx(self) uri = item.requestUri() - - + progress.setRange(0, 0) progress.setValue(0) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarRequestInfo'], + colorBg=self._userSettings['ColorProgressBgRequestInfo'] + ) item.setProgressWidget(self, progress) - identifier = self._fcpClient.clientGetInfo(uri) + identifier = self._fcpClient.clientRequestInfo(uri) self._downloads['Downloads'][identifier] = item - - def handleClientDisconnected(self, event, params): - """ - """ - def handleTestDDAComplete(self, event, params): - """ - """ - directory = params['Directory'] - readAllowed = params.get('WriteDirectoryAllowed') - writeAllowed = params.get('WriteDirectoryAllowed') + def handleFcpClientProtocolError(self, event, params): + identifier = params.get('Identifier', None) + if identifier is None: + #TDO: handle error + pass + else: + item = self._downloads['Downloads'].get(identifier, None) + if item is not None: + self.handleFcpClientGetFailed(event, params) + - # check if there are items to be donloaded for the directory - downloads = self._downloads['DownloadDirectories'].get(directory, None) - if downloads is not None: - del self._downloads['DownloadDirectories'][directory] # ??? - - if downloads: - if writeAllowed == self._fcpClient.FcpTrue: - parent, uris = downloads - for uri in uris: - self.download(uri, parent=parent) - - else: - pass - #TODO: write access denied, error - + def handleFcpClientRequestInfo(self, event, params): - def handleClientGetInfo(self, event, params): - identifier = params['Identifier'] item = self._downloads['Downloads'].get(identifier, None) if item is not None: @@ -384,13 +486,18 @@ else: dataLength = numbers.format_num_bytes(dataLength) + progress = item.progressWidget(self) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarRequestInfoComplete'], + colorBg=self._userSettings['ColorProgressBgRequestInfoComplete'] + ) + item.setRequestStatus(status, self._strings.itemStatus[status]) item.setRequestMimeType(mimeType) item.setRequestSize(dataLength) - - - def handleClientGetInfoProgress(self, event, params): + + def handleFcpClientRequestInfoProgress(self, event, params): identifier = params['Identifier'] item = self._downloads['Downloads'].get(identifier, None) @@ -403,6 +510,29 @@ progress.setRange(0, required) progress.setValue(succeeded) + + def handleFcpClientTestDDAComplete(self, event, params): + """ + """ + directory = params['Directory'] + readAllowed = params.get('WriteDirectoryAllowed') + writeAllowed = params.get('WriteDirectoryAllowed') + + # check if there are items to be donloaded for the directory + downloads = self._downloads['DownloadDirectories'].get(directory, None) + if downloads is not None: + del self._downloads['DownloadDirectories'][directory] # ??? + + if downloads: + if writeAllowed == self._fcpClient.FcpTrue: + parent, uris = downloads + for uri in uris: + self.download(uri, parent=parent) + + else: + pass + #TODO: write access denied, error + ####################################################### ## ## methods @@ -434,7 +564,7 @@ CHK@AbQOZRSJcgXnKOI16D2hF8qkjxp3cl1u72bkL2KS29E,jjQlvojCkJJgzI2WgWj5rMsmUd935zjGChiV-j937X0,AAIC--8/snow_002%20%2814%29.jpg CHK@1ZW0fOpB7RmUFqSO2TtAOyfFm1uJ6~JcWuPESiYwtc4,aCDkI~JCr3Mb8s7rPBgmalPqzQEFdovlF~DgDRkGWuE,AAIC--8/snow_002%20%2815%29.jpg CHK@a8jHCl0TkwVAcTdYOezd-FtIWTLIFhjrWeHXVcSjRPk,4rIwDZE~-Gp~B5tjDSAcSpn~tLgMmnMUFeikXP4f7y0,AAIC--8/snow_002%20%2816%29.jpg -CHK@Jg~F~~WbKu8AlBgmwkonWGQNzZFUholE2CkGnyt23kk,Gj7JtnR97EIRvabG8m6Xp1dUAqiP6qD4J5FJRN9waMQ,AAIC--8/snow_002%20%2836%29.jpg''' +CHK@[I AM INVALID] Jg~F~~WbKu8AlBgmwkonWGQNzZFUholE2CkGnyt23kk,Gj7JtnR97EIRvabG8m6Xp1dUAqiP6qD4J5FJRN9waMQ,AAIC--8/should-error.test''' TestUris = TestUris.split('\n') This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-08 13:21:13
|
Revision: 61 http://fclient.svn.sourceforge.net/fclient/?rev=61&view=rev Author: jUrner Date: 2007-11-08 05:21:17 -0800 (Thu, 08 Nov 2007) Log Message: ----------- some more user options ++ item tips ++ fixed status for RequestInfo 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-08 13:18:59 UTC (rev 60) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-08 13:21:17 UTC (rev 61) @@ -20,6 +20,7 @@ from fclient_lib.qt4ex import settingsbase +from fclient_lib.qt4ex.ctrls import areatips from fclient_lib.qt4ex.ctrls import progressbarwrap from fclient_lib.qt4ex.ctrls import treewidgetwrap @@ -29,8 +30,6 @@ del parentdir #<-- rel import hack - -import thread import time from PyQt4 import QtCore, QtGui #***************************************************************************** @@ -55,7 +54,12 @@ ('ColorProgressBarStopped', ['toString', 'gray']), ('ColorProgressBarStopped', ['toString', 'white']), + ('MaxSimultaniousDownloads', ['toInt', 2]), + ('MaxNewDownloadsPerHop', ['toInt', 100]), + ('ClearCompletedDownloads', ['toBool', False]), + ('ItemTipsShow', ['toBool', True]), + ('ItemTipsDelay', ['toInt', areatips.DEFAULT_SHOW_DELAY]) ) #***************************************************************************** @@ -256,16 +260,12 @@ connectionName='', directory=None, uris=None, - maxQueuedItemsPerHop=200, - maxSimultaniousDownloads=2, cfg=None ): """ @param parent: (QWidget) parent or None @param directory: (str) directory to sownload items to or None to use default directory @param connectionName: name of the connection to the node - @param maxQueuedItemsPerHop: maximum number of items that can be e added in one hop - @param maxSimultaniousDownloads: maximim number of simultaneous downloads @param cfg: (configConfig) instance or None """ @@ -281,13 +281,15 @@ } self._fcpClient = None self._isCreated = False - self._lock = thread.allocate_lock() - self._maxQueuedItemsPerHop = maxQueuedItemsPerHop #TODO: move to settings - self._maxSimultaniousDownloads = maxSimultaniousDownloads #TODO: move to settings + self._itemTips = areatips.ItemTips(self) self._strings = None - self._userSettings = UserSettings() + + # setup item tips + self._itemTips.setEnabled(self._userSettings['ItemTipsShow']) + self._itemTips.setShowDelay(self._userSettings['ItemTipsDelay']) + # setup tree self.setSelectionMode(self.ContiguousSelection) header = self.header() @@ -367,22 +369,26 @@ progress = item.progressWidget(self) status = DownloadItem.StatusError + + item.setRequestStatus(status, self._strings.itemStatus[status]) + item.setRequestError(event, params) - progress.setRange(0, 1) - progress.setValue(1) + # get rid of progress pending indicator if necessary + if progress.maximum() == progress.minimum() == 0: + progress.setRange(0, 1) + progress.setValue(0) progress.setColors( colorBar=self._userSettings['ColorProgressBarError'], colorBg=self._userSettings['ColorProgressBgError'] ) - item.setRequestStatus(status, self._strings.itemStatus[status]) - item.setRequestError(event, params) - + def handleFcpClientIdle(self, event, params): # check if there are sownloads queued n = 0 - while self._downloads['DownloadQueue'] and n < self._maxQueuedItemsPerHop: + maxNewDls = self._userSettings['MaxNewDownloadsPerHop'] + while self._downloads['DownloadQueue'] and n < maxNewDls: n += 1 parent, uri = self._downloads['DownloadQueue'].pop(0) @@ -418,7 +424,7 @@ items[status].append(item) # start items with info requested - itemsToBeStarted = self._maxSimultaniousDownloads - itemsBusy + itemsToBeStarted = self._userSettings['MaxSimultaniousDownloads'] - itemsBusy if itemsToBeStarted > 0: def sortf(item1, item2): return cmp(item1.requestInitTime(), item2.requestInitTime()) @@ -443,6 +449,7 @@ item = pendingItems.pop(0) progress = progressbarwrap.ProgressBarEx(self) + status = DownloadItem.StatusRequestInfo uri = item.requestUri() progress.setRange(0, 0) @@ -452,6 +459,7 @@ colorBg=self._userSettings['ColorProgressBgRequestInfo'] ) item.setProgressWidget(self, progress) + item.setRequestStatus(status, self._strings.itemStatus[status]) identifier = self._fcpClient.clientRequestInfo(uri) self._downloads['Downloads'][identifier] = item This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-10 11:30:23
|
Revision: 66 http://fclient.svn.sourceforge.net/fclient/?rev=66&view=rev Author: jUrner Date: 2007-11-10 03:30:27 -0800 (Sat, 10 Nov 2007) Log Message: ----------- implemented actual download of uris 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-10 11:28:52 UTC (rev 65) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-10 11:30:27 UTC (rev 66) @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + """Sketch for a download widget """ @@ -105,6 +107,8 @@ self._requestError = None self._requestIdentifier = requestIdentifier + self._requestName = None + self._requestNamePostfix = None self._requestStatus = self.StatusNone self._requestInitTime = time.time() self._uri = uri @@ -144,20 +148,50 @@ self.setText(self.IndexMimeType, mimeType) def requestMimeType(self): - """Returns the mime type of the item""" - return self.text(self.IndexMimeType) + """Returns the mime type of the item + @return: (unicode) request mime type + """ + return unicode(self.text(self.IndexMimeType)) + def setRequestName(self, name): """Sets the request name of the item @param name: (str) name + @return: (unicode) name (including filename collision handling postfix) + @note: the name should not include the filename collision handling postfix """ + self._requestName = name + if self._requestNamePostfix is not None: + name, exts = namespace.split_extensions(name) + name = '%s (%s)%s' % (name, self._requestNamePostfix, ''.join(exts)) self.setText(self.IndexName, name) + return name - def requestName(self): + + def increaseRequestNamePostfix(self): + """Increases the filename collision handling postfix by one + @return: (unicode) new request name + @note: use this to compose one of these 'filename (1).txt' filenames in case of + filename collisons + """ + if self._requestNamePostfix is None: + self._requestNamePostfix = 1 + else: + self._requestNamePostfix += 1 + return self.setRequestName(self.requestName(includePostfix=False)) + + + def requestName(self, includePostfix=True): """Returns the request name of the item + @param includePostfix: if True, returns the request name including file collision handling postfi, + if False the request name is returned without postfix + @return: (unicode) request name """ - return self.text(self.IndexName) + if includePostfix: + return unicode(self.text(self.IndexName)) + return self._requestName + def setRequestPriority(self, priority): """Sets the request priority of the item @param prority: (FcpClient.Priority) @@ -165,8 +199,10 @@ self.setText(self.IndexPriority, priority) def requestPriority(self): - """Returns the request priority of the item""" - return self.text(self.IndexPriority) + """Returns the request priority of the item + @return: (unicode) request priority + """ + return unicode(self.text(self.IndexPriority)) def setProgressWidget(self, tree, widget): """Associates a progress widget to the item @@ -200,8 +236,10 @@ self.setText(self.IndexSize, size) def requestSize(self): - """Returns the request size of the item""" - return self.text(self.IndexSize) + """Returns the request size of the item + @return: (unicode) request size + """ + return unicode(self.text(self.IndexSize)) def setRequestUri(self, uri): """Sets the request uri of the item @@ -253,6 +291,9 @@ #**************************************************************************** # #**************************************************************************** + +#TODO: no idea if the node removes requests when complete or on error or +# if RemovePersistentRequest has to be send explicitely class DownloadWidget(QtGui.QTreeWidget): def __init__(self, @@ -341,9 +382,11 @@ (self._fcpClient.events.TestDDAComplete, self.handleFcpClientTestDDAComplete), (self._fcpClient.events.ClientRequestInfo, self.handleFcpClientRequestInfo), - (self._fcpClient.events.ClientRequestInfoProgress, self.handleFcpClientRequestInfoProgress), + (self._fcpClient.events.SimpleProgress, self.handleFcpClientSimpleProgress), + (self._fcpClient.events.DataFound, self.handleFcpClientDataFound), (self._fcpClient.events.GetFailed, self.handleFcpClientGetFailed), + (self._fcpClient.events.IdentifierCollision, self.handleFcpClientIdentifierCollision), (self._fcpClient.events.ProtocolError, self.handleFcpClientProtocolError), ) # take care not to connect twice @@ -356,17 +399,52 @@ self._fcpClient.testDDA(directory, wantWriteDirectory=True) + #TODO: not yet handled def handleFcpClientDisconnected(self, event, params): """ """ + def handleFcpClientDataFound(self, event, params): + """ + """ + identifier = params['Identifier'] + item = self._downloads['Downloads'].get(identifier, None) + if item is not None: + del self._downloads['Downloads'][identifier] + + progress = item.progressWidget(self) + status = DownloadItem.StatusDownloadComplete + + item.setRequestStatus(status, self._strings.itemStatus[status]) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarDownloadComplete'], + colorBg=self._userSettings['ColorProgressBgDownloadComplete'] + ) + + #TODO: how to provide extra information to the user? def handleFcpClientGetFailed(self, event, params): identifier = params['Identifier'] item = self._downloads['Downloads'].get(identifier, None) if item is not None: del self._downloads['Downloads'][identifier] + code = params['Code'] + # handle file name collision + if code == self._fcpClient.ProtocolError.DiskTargetExists: + if item.requestStatus() == DownloadItem.StatusDownloading: + filename = os.path.join( + self.downloadDirectoryFromItem(item), + item.increaseRequestNamePostfix() + ) + identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile) + uri = item.requestUri() + + self._fcpClient.clientGetFile(uri, filename, identifier=identifier) + self._downloads['Downloads'][identifier] = item + return + + # handle error progress = item.progressWidget(self) status = DownloadItem.StatusError @@ -381,8 +459,14 @@ colorBar=self._userSettings['ColorProgressBarError'], colorBg=self._userSettings['ColorProgressBgError'] ) + #raise self._fcpClient.FetchError(params) + #TODO: not yet handled + def handleFcpClientIdentifierCollision(self, vent, params): + pass + + def handleFcpClientIdle(self, event, params): # check if there are sownloads queued @@ -423,31 +507,49 @@ elif status & item.StatusRequestInfoComplete: items[status].append(item) - # start items with info requested - itemsToBeStarted = self._userSettings['MaxSimultaniousDownloads'] - itemsBusy - if itemsToBeStarted > 0: - def sortf(item1, item2): - return cmp(item1.requestInitTime(), item2.requestInitTime()) - - infoCompleteItems = items[DownloadItem.StatusRequestInfoComplete] - infoCompleteItems.sort(cmp=sortf) - while infoCompleteItems: - - #TODO: not yet implemented - break - itemsToBeStarted -= 1 - + downloadsToBeStarted = self._userSettings['MaxSimultaniousDownloads'] - itemsBusy - # start pending items - if itemsToBeStarted > 0: - pendingItems = items[DownloadItem.StatusPending] - pendingItems.sort(cmp=sortf) - while pendingItems: - itemsToBeStarted -= 1 - if itemsToBeStarted < 0: + #TODO: sort by priority + def sortf(item1, item2): + return cmp(item1.requestInitTime(), item2.requestInitTime()) + + # start downloads with info requested complete + if downloadsToBeStarted > 0: + downloadsRequestInfoComplete = items[DownloadItem.StatusRequestInfoComplete] + downloadsRequestInfoComplete.sort(cmp=sortf) + while downloadsRequestInfoComplete: + downloadsToBeStarted -= 1 + if downloadsToBeStarted < 0: break - item = pendingItems.pop(0) + item = downloadsRequestInfoComplete.pop(0) + filename = os.path.join( self.downloadDirectoryFromItem(item), item.requestName()) + identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile) + progress = item.progressWidget(self) + status = DownloadItem.StatusDownloading + uri = item.requestUri() + + progress.setRange(0, 0) + progress.setValue(0) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarDownloading'], + colorBg=self._userSettings['ColorProgressBgDownloading'] + ) + item.setRequestStatus(status, self._strings.itemStatus[status]) + self._fcpClient.clientGetFile(uri, filename, identifier=identifier) + self._downloads['Downloads'][identifier] = item + + # start pending downloads + if downloadsToBeStarted > 0: + downloadsPending = items[DownloadItem.StatusPending] + downloadsPending.sort(cmp=sortf) + while downloadsPending: + downloadsToBeStarted -= 1 + if downloadsToBeStarted < 0: + break + + identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientRequestInfo) + item = downloadsPending.pop(0) progress = progressbarwrap.ProgressBarEx(self) status = DownloadItem.StatusRequestInfo uri = item.requestUri() @@ -460,11 +562,9 @@ ) item.setProgressWidget(self, progress) item.setRequestStatus(status, self._strings.itemStatus[status]) - - identifier = self._fcpClient.clientRequestInfo(uri) + self._fcpClient.clientRequestInfo(uri, identifier=identifier) self._downloads['Downloads'][identifier] = item - - + def handleFcpClientProtocolError(self, event, params): identifier = params.get('Identifier', None) @@ -505,7 +605,7 @@ item.setRequestSize(dataLength) - def handleFcpClientRequestInfoProgress(self, event, params): + def handleFcpClientSimpleProgress(self, event, params): identifier = params['Identifier'] item = self._downloads['Downloads'].get(identifier, None) @@ -558,6 +658,33 @@ #TODO: retranslate item status + def downloadDirectoryFromItem(self, item): + """Returns the dirctory an item should be downloaded to + @param item: (QTreeWidgetItem) + @return: (str) directory + """ + out = [] + parent = item.parent() + while parent is not None: + out.append(parent.requestName()) + parent = parent.parent() + + out.append(self._directory) + out.reverse() + return os.path.join(*out) + + + def uniqueDownloadsIdentifier(self, identifierPrefix): + """Creates a new identifier that is unique to the internal sownloads dict + @param identifierPrefix: FcpClient.IdentifierPrefix.* + @return: (str) identifier + """ + while True: + identifier = identifierPrefix + self._fcpClient.newIdentifier() + if identifier not in self._downloads['Downloads']: + return identifier + + #*************************************************************************************************** # #*************************************************************************************************** This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-12 10:37:49
|
Revision: 70 http://fclient.svn.sourceforge.net/fclient/?rev=70&view=rev Author: jUrner Date: 2007-11-12 02:37:52 -0800 (Mon, 12 Nov 2007) Log Message: ----------- too many a changes.. reworked testDDA, PersistentGet is now handled and God knows whatelse 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-12 10:36:01 UTC (rev 69) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-12 10:37:52 UTC (rev 70) @@ -112,11 +112,14 @@ self._requestStatus = self.StatusNone self._requestInitTime = time.time() self._uri = uri + + self._ddaTested = False def setRequestError(self, event, params): """If an error occures, sets info about the error - @param event: event that triggered the error + @param event: event that triggered the error (EventFetchError, EventProtocolError, + EventTestDDAComplete) @param params: params passed along with the event """ self._requestError = (event, params) @@ -250,6 +253,13 @@ def requestUri(self): """Returns the request uri of the item""" return self._uri + + + def setDDATested(self, flag): + self._ddaTested = flag + + def ddaTested(self): + return self._ddaTested #***************************************************************************** # @@ -291,9 +301,7 @@ #**************************************************************************** # #**************************************************************************** - -#TODO: no idea if the node removes requests when complete or on error or -# if RemovePersistentRequest has to be send explicitely +#TODO: when closing we have to empty queues by sending ClientGet requests class DownloadWidget(QtGui.QTreeWidget): def __init__(self, @@ -315,7 +323,7 @@ self._connectionName = connectionName self._cfg = cfg if cfg is not None else config.Config(self) self._directory = directory if directory is not None else self._cfg.defaultDownloadDir - self._downloads = { + self._requests = { 'Downloads': {}, # identifier --> item / items currently downloading 'DownloadQueue': [], # (parent-item, uri) / items scheduled for download 'DownloadDirectories': {}, # directory --> (parentItem, uris) / directories scheduled for testDDA @@ -350,14 +358,19 @@ # schedule initial uris for download if uris is not None: - self._downloads['DownloadDirectories'][self._directory] = (self, uris) + for uri in uris: + self.download(uri) + + #self._requests['DownloadDirectories'][self._directory] = (self, uris) 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) + + ############################################################# ## ## handlers for Qt events @@ -384,6 +397,7 @@ (self._fcpClient.events.ClientRequestInfo, self.handleFcpClientRequestInfo), (self._fcpClient.events.SimpleProgress, self.handleFcpClientSimpleProgress), (self._fcpClient.events.DataFound, self.handleFcpClientDataFound), + (self._fcpClient.events.PersistentGet, self.handleFcpClientPersistentGet), (self._fcpClient.events.GetFailed, self.handleFcpClientGetFailed), (self._fcpClient.events.IdentifierCollision, self.handleFcpClientIdentifierCollision), @@ -395,7 +409,7 @@ event += observer # register directories - for directory in self._downloads['DownloadDirectories']: + for directory in self._requests['DownloadDirectories']: self._fcpClient.testDDA(directory, wantWriteDirectory=True) @@ -408,62 +422,72 @@ """ """ identifier = params['Identifier'] - item = self._downloads['Downloads'].get(identifier, None) + item = self._requests['Downloads'].get(identifier, None) if item is not None: - del self._downloads['Downloads'][identifier] + del self._requests['Downloads'][identifier] - progress = item.progressWidget(self) - status = DownloadItem.StatusDownloadComplete + mimeType = params.get('Metadata.ContentType', self._strings.unknown) + dataLength = params.get('DataLength', '') + try: + dataLength = int(dataLength) + except ValueError: + dataLength = self._strings.unknown + else: + dataLength = numbers.format_num_bytes(dataLength) + + self._adjustItemStatus(item, DownloadItem.StatusDownloadComplete) + item.setRequestMimeType(mimeType) + item.setRequestSize(dataLength) - item.setRequestStatus(status, self._strings.itemStatus[status]) - progress.setColors( - colorBar=self._userSettings['ColorProgressBarDownloadComplete'], - colorBg=self._userSettings['ColorProgressBgDownloadComplete'] - ) - #TODO: how to provide extra information to the user? + #TODO: how to provide extra error information to the user? def handleFcpClientGetFailed(self, event, params): identifier = params['Identifier'] - item = self._downloads['Downloads'].get(identifier, None) + item = self._requests['Downloads'].get(identifier, None) if item is not None: - del self._downloads['Downloads'][identifier] + del self._requests['Downloads'][identifier] code = params['Code'] # handle file name collision + # ...always wait for this error and adjust filename if necessary if code == self._fcpClient.ProtocolError.DiskTargetExists: if item.requestStatus() == DownloadItem.StatusDownloading: filename = os.path.join( self.downloadDirectoryFromItem(item), item.increaseRequestNamePostfix() ) - identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile) + identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile) uri = item.requestUri() self._fcpClient.clientGetFile(uri, filename, identifier=identifier) - self._downloads['Downloads'][identifier] = item + self._requests['Downloads'][identifier] = item return + # handle testDDA + # ...always wait for this error and reinsert download on TestDDAComplete + elif code == self._fcpClient.ProtocolError.DDADenied: + if not item.ddaTested(): + directory = self.downloadDirectoryFromItem(item) + if directory in self._requests['DownloadDirectories']: + self._requests['DownloadDirectories'][directory].append(item) + else: + self._requests['DownloadDirectories'][directory] = [item, ] + #NOTE: have to take care to not send neted TestDDAs + self._fcpClient.testDDA(directory, wantWriteDirectory=True) + + self._requests['Downloads'][item.requestIdentifier()] = item + return + # handle error - progress = item.progressWidget(self) - status = DownloadItem.StatusError - - item.setRequestStatus(status, self._strings.itemStatus[status]) + self._adjustItemStatus(item, DownloadItem.StatusError) item.setRequestError(event, params) - # get rid of progress pending indicator if necessary - if progress.maximum() == progress.minimum() == 0: - progress.setRange(0, 1) - progress.setValue(0) - progress.setColors( - colorBar=self._userSettings['ColorProgressBarError'], - colorBg=self._userSettings['ColorProgressBgError'] - ) #raise self._fcpClient.FetchError(params) #TODO: not yet handled - def handleFcpClientIdentifierCollision(self, vent, params): + def handleFcpClientIdentifierCollision(self, event, params): pass @@ -472,22 +496,21 @@ # check if there are sownloads queued n = 0 maxNewDls = self._userSettings['MaxNewDownloadsPerHop'] - while self._downloads['DownloadQueue'] and n < maxNewDls: + while self._requests['DownloadQueue'] and n < maxNewDls: n += 1 - parent, uri = self._downloads['DownloadQueue'].pop(0) + parent, uri = self._requests['DownloadQueue'].pop(0) + if parent is None: + parent = self item = DownloadItem(parent, '', uri) fcpUri = self._fcpClient.FcpUri(uri) fileName = fcpUri.fileName() fileName = namespace.unquote_uri(fileName) - status = DownloadItem.StatusPending - + item.setRequestName(fileName) - item.setRequestStatus(status, self._strings.itemStatus[status]) + self._adjustItemStatus(item, DownloadItem.StatusPending) - #... more here - - + # check how many downloads are currently running #TODO: bit sloppy coding here. We know how max downloads to start, @@ -508,6 +531,7 @@ items[status].append(item) downloadsToBeStarted = self._userSettings['MaxSimultaniousDownloads'] - itemsBusy + #TODO: sort by priority def sortf(item1, item2): @@ -517,54 +541,79 @@ if downloadsToBeStarted > 0: downloadsRequestInfoComplete = items[DownloadItem.StatusRequestInfoComplete] downloadsRequestInfoComplete.sort(cmp=sortf) - while downloadsRequestInfoComplete: + while downloadsRequestInfoComplete and downloadsToBeStarted > 0: downloadsToBeStarted -= 1 - if downloadsToBeStarted < 0: - break - + item = downloadsRequestInfoComplete.pop(0) - filename = os.path.join( self.downloadDirectoryFromItem(item), item.requestName()) - identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile) - progress = item.progressWidget(self) - status = DownloadItem.StatusDownloading + filename = os.path.join(self.downloadDirectoryFromItem(item), item.requestName()) + identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile) uri = item.requestUri() - progress.setRange(0, 0) - progress.setValue(0) - progress.setColors( - colorBar=self._userSettings['ColorProgressBarDownloading'], - colorBg=self._userSettings['ColorProgressBgDownloading'] - ) - item.setRequestStatus(status, self._strings.itemStatus[status]) + # tell node to remove completed RequestInfo + self._fcpClient.removePersistentRequest(item.requestIdentifier()) + + item.setRequestIdentifier(identifier) + self._adjustItemStatus(item, DownloadItem.StatusDownloading) self._fcpClient.clientGetFile(uri, filename, identifier=identifier) - self._downloads['Downloads'][identifier] = item + + self._requests['Downloads'][identifier] = item # start pending downloads if downloadsToBeStarted > 0: downloadsPending = items[DownloadItem.StatusPending] downloadsPending.sort(cmp=sortf) - while downloadsPending: + while downloadsPending and downloadsToBeStarted > 0: downloadsToBeStarted -= 1 - if downloadsToBeStarted < 0: - break - - identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientRequestInfo) + + identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientRequestInfo) item = downloadsPending.pop(0) - progress = progressbarwrap.ProgressBarEx(self) - status = DownloadItem.StatusRequestInfo uri = item.requestUri() - progress.setRange(0, 0) - progress.setValue(0) - progress.setColors( - colorBar=self._userSettings['ColorProgressBarRequestInfo'], - colorBg=self._userSettings['ColorProgressBgRequestInfo'] - ) - item.setProgressWidget(self, progress) - item.setRequestStatus(status, self._strings.itemStatus[status]) - self._fcpClient.clientRequestInfo(uri, identifier=identifier) - self._downloads['Downloads'][identifier] = item + # 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) + + self._requests['Downloads'][identifier] = 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 + + #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 + # + clientToken = params.get('ClientToken', None) + parent = self + if clientToken is not None: + parent = self._requests['Downloads'].get(clientToken, None) + uri = params['URI'] + item = DownloadItem(parent, identifier, uri) + fcpUri = self._fcpClient.FcpUri(uri) + fileName = fcpUri.fileName() + fileName = namespace.unquote_uri(fileName) + item.setRequestName(fileName) + + self._requests['Downloads'][identifier] = item + + if self._fcpClient.isClientGetFile(identifier): + status = DownloadItem.StatusDownloading + elif self._fcpClient.isClientRequestInfo(identifier): + status = DownloadItem.StatusRequestInfo + + self._adjustItemStatus(item, status) + def handleFcpClientProtocolError(self, event, params): identifier = params.get('Identifier', None) @@ -572,17 +621,20 @@ #TDO: handle error pass else: - item = self._downloads['Downloads'].get(identifier, None) + item = self._requests['Downloads'].get(identifier, None) if item is not None: self.handleFcpClientGetFailed(event, params) + + #NOTE: ProtocolError seems to jump in before a request is inserted in + # the nodes queue. So we will not get informed about the request on next reconnect. def handleFcpClientRequestInfo(self, event, params): identifier = params['Identifier'] - item = self._downloads['Downloads'].get(identifier, None) + item = self._requests['Downloads'].get(identifier, None) if item is not None: - del self._downloads['Downloads'][identifier] + del self._requests['Downloads'][identifier] status = DownloadItem.StatusRequestInfoComplete mimeType = params.get('Metadata.ContentType', self._strings.unknown) @@ -608,7 +660,7 @@ def handleFcpClientSimpleProgress(self, event, params): identifier = params['Identifier'] - item = self._downloads['Downloads'].get(identifier, None) + item = self._requests['Downloads'].get(identifier, None) if item is not None: progress = item.progressWidget(self) required=int(params['Required']) @@ -627,19 +679,23 @@ writeAllowed = params.get('WriteDirectoryAllowed') # check if there are items to be donloaded for the directory - downloads = self._downloads['DownloadDirectories'].get(directory, None) + downloads = self._requests['DownloadDirectories'].get(directory, None) if downloads is not None: - del self._downloads['DownloadDirectories'][directory] # ??? + del self._requests['DownloadDirectories'][directory] if downloads: if writeAllowed == self._fcpClient.FcpTrue: - parent, uris = downloads - for uri in uris: - self.download(uri, parent=parent) - + for item in downloads: + item.setDDATested(True) + filename = os.path.join(self.downloadDirectoryFromItem(item), item.requestName()) + self._fcpClient.clientGetFile(item.requestUri(), filename, identifier=item.requestIdentifier()) + + #TODO: not tested else: - pass - #TODO: write access denied, error + for item in downloads: + self._adjustItemStatus(item, DownloadItem.StatusError) + item.setRequestError(event, params) + ####################################################### ## @@ -647,7 +703,7 @@ ## ####################################################### def download(self, uri, parent=None): - self._downloads['DownloadQueue'].append( (parent, uri) ) + self._requests['DownloadQueue'].append( (parent, uri) ) #TODO: adjust priority? @@ -658,6 +714,8 @@ #TODO: retranslate item status + #TODO: is not correct for items that are not downloaded to self._downloads. + # self._downloads may change and / or items from global queue may jump in def downloadDirectoryFromItem(self, item): """Returns the dirctory an item should be downloaded to @param item: (QTreeWidgetItem) @@ -674,17 +732,81 @@ return os.path.join(*out) - def uniqueDownloadsIdentifier(self, identifierPrefix): + ####################################################### + ## + ## private methods + ## + ####################################################### + def _adjustItemStatus(self, item, status): + """Adjusts an item to a specifed status + @param item: (L{DownloadItem}) to adjust + @param status: L{DownloadItem}.Status* to adjust the item to + @return: item + """ + if status == DownloadItem.StatusPending: + pass + + elif status == DownloadItem.StatusRequestInfo: + progress = progressbarwrap.ProgressBarEx(self) + item.setProgressWidget(self, progress) + progress.setRange(0, 0) + progress.setValue(0) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarRequestInfo'], + colorBg=self._userSettings['ColorProgressBgRequestInfo'] + ) + + elif status == DownloadItem.StatusDownloading: + progress = item.progressWidget(self) + #NOTE: when we connect to the node PersistenGet may pass GetFile requests, + # ...so make shure progessbar is available + if progress is None: + progress = progressbarwrap.ProgressBarEx(self) + item.setProgressWidget(self, progress) + progress.setRange(0, 0) + progress.setValue(0) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarDownloading'], + colorBg=self._userSettings['ColorProgressBgDownloading'] + ) + + elif status == DownloadItem.StatusDownloadComplete: + progress = item.progressWidget(self) + progress.setRange(0, 1) + progress.setValue(1) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarDownloadComplete'], + colorBg=self._userSettings['ColorProgressBgDownloadComplete'] + ) + + elif status == DownloadItem.StatusError: + progress = item.progressWidget(self) + # get rid of pending progress indicator if necessary + if progress.maximum() == progress.minimum() == 0: + progress.setRange(0, 1) + progress.setValue(0) + progress.setColors( + colorBar=self._userSettings['ColorProgressBarError'], + colorBg=self._userSettings['ColorProgressBgError'] + ) + + else: + raise ValueError('Status not handled') + + item.setRequestStatus(status, self._strings.itemStatus[status]) + return item + + + def _uniqueDownloadsIdentifier(self, identifierPrefix): """Creates a new identifier that is unique to the internal sownloads dict @param identifierPrefix: FcpClient.IdentifierPrefix.* @return: (str) identifier """ while True: identifier = identifierPrefix + self._fcpClient.newIdentifier() - if identifier not in self._downloads['Downloads']: + if identifier not in self._requests['Downloads']: return identifier - #*************************************************************************************************** # #*************************************************************************************************** @@ -708,8 +830,9 @@ w = QtGui.QMainWindow() peers = DownloadWidget(None, connectionName='TestDownloadWidget', - uris=TestUris - + #uris=None, + #uris=TestUris, + #uris=[TestUris[1], ] ) w.setCentralWidget(peers) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-12 10:41:54
|
Revision: 71 http://fclient.svn.sourceforge.net/fclient/?rev=71&view=rev Author: jUrner Date: 2007-11-12 02:41:59 -0800 (Mon, 12 Nov 2007) Log Message: ----------- ... 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-12 10:37:52 UTC (rev 70) +++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-12 10:41:59 UTC (rev 71) @@ -473,7 +473,8 @@ self._requests['DownloadDirectories'][directory].append(item) else: self._requests['DownloadDirectories'][directory] = [item, ] - #NOTE: have to take care to not send neted TestDDAs + #NOTE: have to take care to not send nested TestDDAs, + # if so, node will (may) throw a ProtocolError self._fcpClient.testDDA(directory, wantWriteDirectory=True) self._requests['Downloads'][item.requestIdentifier()] = item This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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. |
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. |