fclient-commit Mailing List for fclient (Page 37)
Status: Pre-Alpha
Brought to you by:
jurner
You can subscribe to this list here.
2007 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(23) |
Nov
(54) |
Dec
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
2008 |
Jan
(17) |
Feb
(209) |
Mar
(63) |
Apr
(31) |
May
(7) |
Jun
(39) |
Jul
(390) |
Aug
(122) |
Sep
(6) |
Oct
|
Nov
|
Dec
|
From: <jU...@us...> - 2007-11-10 11:25:36
|
Revision: 62 http://fclient.svn.sourceforge.net/fclient/?rev=62&view=rev Author: jUrner Date: 2007-11-10 03:25:39 -0800 (Sat, 10 Nov 2007) Log Message: ----------- for testing.. will be removed later Added Paths: ----------- trunk/fclient/download/ 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-08 13:18:54
|
Revision: 60 http://fclient.svn.sourceforge.net/fclient/?rev=60&view=rev Author: jUrner Date: 2007-11-08 05:18:59 -0800 (Thu, 08 Nov 2007) Log Message: ----------- fixed typo Modified Paths: -------------- trunk/fclient/fclient_lib/qt4ex/ctrls/areatips.py Modified: trunk/fclient/fclient_lib/qt4ex/ctrls/areatips.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/ctrls/areatips.py 2007-11-08 11:13:54 UTC (rev 59) +++ trunk/fclient/fclient_lib/qt4ex/ctrls/areatips.py 2007-11-08 13:18:59 UTC (rev 60) @@ -300,7 +300,7 @@ """adjusts the show delay @param n milliseconds to delay the popup of the tooltip """ - return self.areaTip.setShowDelay(delay) + return self.areaTip.setShowDelay(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 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 11:12:52
|
Revision: 58 http://fclient.svn.sourceforge.net/fclient/?rev=58&view=rev Author: jUrner Date: 2007-11-08 03:12:56 -0800 (Thu, 08 Nov 2007) Log Message: ----------- protocol errors in relation to requests are now passed to the caller ++ some renames Modified Paths: -------------- trunk/fclient/fclient_lib/fcp/fcp2_0.py Modified: trunk/fclient/fclient_lib/fcp/fcp2_0.py =================================================================== --- trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-07 17:35:34 UTC (rev 57) +++ trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-08 11:12:56 UTC (rev 58) @@ -146,6 +146,8 @@ 'ClientConnected', 'ClientDisconnected', + 'ProtocolError', + # config related events 'ConfigData', 'NodeData', @@ -164,8 +166,8 @@ 'TestDDAComplete', 'IdentifierCollision', - 'ClientGetInfo', - 'ClientGetInfoProgress', + 'ClientRequestInfo', + 'ClientRequestInfoProgress', 'DataFound', 'GetFailed', @@ -319,7 +321,7 @@ class IdentifierPrefix: """Special purpose identifier prefixes""" - ClientGetInfo = 'ClientGetInfo::' + ClientRequestInfo = 'ClientRequestInfo::' class InsertError(Exception): """All insert errors supported by the client""" @@ -797,8 +799,14 @@ self.events.ClientDisconnect({'DisconnectReason': DisconnectReason.Shutdown}) return True - raise self.ProtocolError(msg) + identifier = msg.get('Identifier', None) + if identifier is None: + #TODO: check how to handle this + raise self.ProtocolError(msg) + else: + self.events.ProtocolError(msg.params) + #################################################### ## ## TestDDA @@ -888,8 +896,8 @@ ## #################################################### elif msg.name == self.Message.DataFound: - if msg['Identifier'].startswith(self.IdentifierPrefix.ClientGetInfo): - self.events.ClientGetInfo(msg.params) + if msg['Identifier'].startswith(self.IdentifierPrefix.ClientRequestInfo): + self.events.ClientRequestInfo(msg.params) return True self.events.DataFound(msg.params) @@ -898,21 +906,21 @@ elif msg.name == self.Message.GetFailed: code = msg['Code'] if code == self.FetchError.TooBig: - if msg['Identifier'].startswith(self.IdentifierPrefix.ClientGetInfo): + if msg['Identifier'].startswith(self.IdentifierPrefix.ClientRequestInfo): params = { 'Identifier': msg['Identifier'], 'Metadata.ContentType': msg.get('ExpectedMetadata.ContentType', ''), 'DataLength': msg.get('ExpectedDataLength', '') } - self.events.ClientGetInfo(params) + self.events.ClientRequestInfo(params) return True self.events.GetFailed(msg.params) return True elif msg.name == self.Message.SimpleProgress: - if msg['Identifier'].startswith(self.IdentifierPrefix.ClientGetInfo): - self.events.ClientGetInfoProgress(msg.params) + if msg['Identifier'].startswith(self.IdentifierPrefix.ClientRequestInfo): + self.events.ClientRequestInfoProgress(msg.params) else: self.events.SimpleProgress(msg.params) return True @@ -1211,7 +1219,7 @@ return identifier - def clientGetInfo(self, uri, **params): + def clientRequestInfo(self, uri, **params): """Requests info about a file @param uri: uri of the file to request info about @event: clientGetInfo(event, params). If success, params will contain a key 'Metadata.ContentType' @@ -1220,7 +1228,7 @@ @note: for other events see: L{clientGet} @return: (str) request identifier """ - identifier = self.IdentifierPrefix.ClientGetInfo + self.newIdentifier() + identifier = self.IdentifierPrefix.ClientRequestInfo + self.newIdentifier() self.sendMessage( self.Message.ClientGet, Identifier=identifier, @@ -1394,13 +1402,13 @@ #testListPeerNotes() - def testClientGetInfo(): + def testClientRequestInfo(): def cb(event, params): print params c.events.ClientGetInfo += cb - identifier = c.clientGetInfo('CHK@sdNenKGj5mupxaSwo44jcW8dsX7vYTLww~BsRPtur0k,ZNRm9reMjtKEl9e-xFByKXbW6q4f6OQyfg~l9GRSAes,AAIC--8/snow_002%20%281%29.jpg') + identifier = c.clientRequestInfo('CHK@sdNenKGj5mupxaSwo44jcW8dsX7vYTLww~BsRPtur0k,ZNRm9reMjtKEl9e-xFByKXbW6q4f6OQyfg~l9GRSAes,AAIC--8/snow_002%20%281%29.jpg') for i in xrange(20): c.next() - #testClientGetInfo() + #testClientRequestInfo() 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-07 17:35:03
|
Revision: 56 http://fclient.svn.sourceforge.net/fclient/?rev=56&view=rev Author: jUrner Date: 2007-11-07 09:35:00 -0800 (Wed, 07 Nov 2007) Log Message: ----------- config was broken - fixed Modified Paths: -------------- trunk/fclient/fclient_widgets/peer_widget.py Modified: trunk/fclient/fclient_widgets/peer_widget.py =================================================================== --- trunk/fclient/fclient_widgets/peer_widget.py 2007-11-07 17:34:24 UTC (rev 55) +++ trunk/fclient/fclient_widgets/peer_widget.py 2007-11-07 17:35:00 UTC (rev 56) @@ -67,13 +67,15 @@ def __init__(self, parent, - cfg=None + cfg=None, connectionName='', ): """ """ + QtGui.QWidget.__init__(self, parent) + self._connectionName = connectionName - self._cfg = cfg if cfg is None else config.Config(self) + self._cfg = cfg if cfg is not None else config.Config(self) self._fcpClient = None self._fcpEvents = None self._isCreated = False @@ -81,8 +83,7 @@ self._requestsPending = 0 self._showPeersFlags = self.ShowPeersAll - QtGui.QWidget.__init__(self, parent) - + # setup tree self.setUniformRowHeights(True) headerLabels = [ 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:34:22
|
Revision: 55 http://fclient.svn.sourceforge.net/fclient/?rev=55&view=rev Author: jUrner Date: 2007-11-07 09:34:24 -0800 (Wed, 07 Nov 2007) Log Message: ----------- poll timer is single shot now to avoid troubles with reentrant timer Modified Paths: -------------- trunk/fclient/fclient_ui/fcp_client_manager.py Modified: trunk/fclient/fclient_ui/fcp_client_manager.py =================================================================== --- trunk/fclient/fclient_ui/fcp_client_manager.py 2007-11-07 17:33:26 UTC (rev 54) +++ trunk/fclient/fclient_ui/fcp_client_manager.py 2007-11-07 17:34:24 UTC (rev 55) @@ -242,10 +242,11 @@ ) self.pollTimer = QtCore.QTimer() + self.pollTimer.setSingleShot(True) QtCore.QObject.connect( self.pollTimer, QtCore.SIGNAL('timeout()'), - self.fcpClient.next + self.handleFcpClientNext ) @@ -306,7 +307,14 @@ except StopIteration: return self.connectTimer.start(int(self.connectPriority)) + + def handleFcpClientNext(self): + """Handles reading next message from FcpClient""" + self.fcpClient.next() + self.pollTimer.start(int(self.pollPriority)) + + #****************************************************************************************** # #****************************************************************************************** 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:33:38
|
Revision: 54 http://fclient.svn.sourceforge.net/fclient/?rev=54&view=rev Author: jUrner Date: 2007-11-07 09:33:26 -0800 (Wed, 07 Nov 2007) Log Message: ----------- added an idle event and fixed a missing identifier in EventClientGetInfo Modified Paths: -------------- trunk/fclient/fclient_lib/fcp/fcp2_0.py Modified: trunk/fclient/fclient_lib/fcp/fcp2_0.py =================================================================== --- trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-07 17:32:18 UTC (rev 53) +++ trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-07 17:33:26 UTC (rev 54) @@ -141,6 +141,8 @@ """All events the client supports""" _events_ = ( + 'Idle', + 'ClientConnected', 'ClientDisconnected', @@ -898,6 +900,7 @@ if code == self.FetchError.TooBig: if msg['Identifier'].startswith(self.IdentifierPrefix.ClientGetInfo): params = { + 'Identifier': msg['Identifier'], 'Metadata.ContentType': msg.get('ExpectedMetadata.ContentType', ''), 'DataLength': msg.get('ExpectedDataLength', '') } @@ -984,7 +987,12 @@ } self.events.ClientDisconnected(params) raise self.SocketError(msg['Details']) - self.handleMessage(msg) + + elif msg.name == self.Message.ClientSocketTimeout: + self.events.Idle(msg) + + else: + self.handleMessage(msg) return msg 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:33:38
|
Revision: 53 http://fclient.svn.sourceforge.net/fclient/?rev=53&view=rev Author: jUrner Date: 2007-11-07 09:32:18 -0800 (Wed, 07 Nov 2007) Log Message: ----------- added a todo Modified Paths: -------------- trunk/fclient/config.py Modified: trunk/fclient/config.py =================================================================== --- trunk/fclient/config.py 2007-11-06 12:40:00 UTC (rev 52) +++ trunk/fclient/config.py 2007-11-07 17:32:18 UTC (rev 53) @@ -30,7 +30,6 @@ "DlgPreferences": _(DocDir, 'dialogs', 'preferences.html'), } - #***************************************************************************** # #***************************************************************************** @@ -57,6 +56,7 @@ #TODO: assistant can not handle locale dependend docs yet self.assistant = assistant.Assistant(parent, profile=AssistantProfile, pages=DocPages) + #TODO: no good idea to have a default directory. May lead to troubles when making fclient executable self.defaultDownloadDir = os.path.join(BaseDir, 'download') self.fcpClientManager = FcpClientManager(parent) self.language = language.Language([LanguageDir, ], translationPrefixes=TranslationPrefixes) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-06 12:40:00
|
Revision: 52 http://fclient.svn.sourceforge.net/fclient/?rev=52&view=rev Author: jUrner Date: 2007-11-06 04:40:00 -0800 (Tue, 06 Nov 2007) Log Message: ----------- added a few methods Modified Paths: -------------- trunk/fclient/fclient_ui/fcp_client_manager.py Modified: trunk/fclient/fclient_ui/fcp_client_manager.py =================================================================== --- trunk/fclient/fclient_ui/fcp_client_manager.py 2007-11-06 12:39:13 UTC (rev 51) +++ trunk/fclient/fclient_ui/fcp_client_manager.py 2007-11-06 12:40:00 UTC (rev 52) @@ -109,15 +109,16 @@ self.priorities = Priorities(self) - def stopClient(self, name): #TODO: name of client or client??? - """Stops a FcpClient - @name: (str) connection name of the client to stop - @note: the client is not closed in the call. To close the connection call FcpClient.close() + def closeClient(self, name): + """Closes a FcpClient + @name: (str) connection name of the client to close + @note: the client is unregisatered and closed in the call. """ handler = self.fcpClients.pop(name) handler.stop() + handler.fcpClient.close() - + def newClient(self, name, eventConnectedHandler, @@ -168,8 +169,24 @@ handler.setPollPriority(pollPriority) + def startClient(self, name): + """Starts a FcpClient + @name: (str) connection name of the client to start + """ + handler = self.fcpClients[name] + handler.start() + + + def stopClient(self, name): + """Stops a FcpClient + @name: (str) connection name of the client to stop + """ + handler = self.fcpClients[name] + handler.stop() + + def handleFcpClientDisconnected(self, handler, params): - """Handles FcpClient.EventDisconnect""" + """Global handler for all clients to handle FcpClient.EventDisconnect""" disconnectReason = params['DisconnectReason'] fcpClient = handler.fcpClient This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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-06 12:38:46
|
Revision: 50 http://fclient.svn.sourceforge.net/fclient/?rev=50&view=rev Author: jUrner Date: 2007-11-06 04:38:50 -0800 (Tue, 06 Nov 2007) Log Message: ----------- added connectionName param Modified Paths: -------------- trunk/fclient/fclient_widgets/peer_widget.py Modified: trunk/fclient/fclient_widgets/peer_widget.py =================================================================== --- trunk/fclient/fclient_widgets/peer_widget.py 2007-11-06 12:35:18 UTC (rev 49) +++ trunk/fclient/fclient_widgets/peer_widget.py 2007-11-06 12:38:50 UTC (rev 50) @@ -34,7 +34,6 @@ """ """ QtGui.QTreeWidgetItem.__init__(self, *args) - self._peerData = None def peerData(self): @@ -66,10 +65,15 @@ ShowPeersAll = ShowPeersOpennet | ShowPeersDarknet - def __init__(self, parent, cfg=None): + def __init__(self, + parent, + cfg=None + connectionName='', + ): """ """ - self._cfg = cfg + self._connectionName = connectionName + self._cfg = cfg if cfg is None else config.Config(self) self._fcpClient = None self._fcpEvents = None self._isCreated = False @@ -79,10 +83,6 @@ QtGui.QWidget.__init__(self, parent) - # setup config - if self._cfg is None: - self._cfg = config.Config(self) - # setup tree self.setUniformRowHeights(True) headerLabels = [ @@ -112,7 +112,7 @@ """ if not self._isCreated: self._isCreated = True - self._fcpClient = self._cfg.fcpClientManager.newClient('MeName', self.handleClientConnected) + self._fcpClient = self._cfg.fcpClientManager.newClient(self._connectionName, self.handleClientConnected) ############################################################# ## @@ -147,7 +147,7 @@ (self._fcpClient.events.EndListPeerNotes, self.handleEndListPeerNotes), ) - # take care to not connect twice + # take care not to connect twice for event, observer in self._fcpEvents: if not observer in event: event += observer @@ -155,9 +155,7 @@ ## self.listPeers() - - - + def handleClientDisconnected(self, event, params): """ """ @@ -210,8 +208,7 @@ pollPriority=self._cfg.fcpClientManager.priorities['Lowest'] ) - - + def handlePeerRemoved(self, peer): """ """ @@ -258,7 +255,7 @@ """ # disconnect Fcp events for event, observer in self._fcpEvents: - if not observer in event: + if observer in event: event -= observer self._cfg.fcpClientHandler.closeClient(self._fcpClient.connectionName()) @@ -528,7 +525,7 @@ app = QtGui.QApplication(sys.argv) w = QtGui.QMainWindow() - peers = PeerWidget(None) + peers = PeerWidget(None, connectionName='TestPeerWidget') w.setCentralWidget(peers) m = w.menuBar() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-06 12:35:15
|
Revision: 49 http://fclient.svn.sourceforge.net/fclient/?rev=49&view=rev Author: jUrner Date: 2007-11-06 04:35:18 -0800 (Tue, 06 Nov 2007) Log Message: ----------- minor changes Modified Paths: -------------- trunk/fclient/config.py Modified: trunk/fclient/config.py =================================================================== --- trunk/fclient/config.py 2007-11-06 12:34:50 UTC (rev 48) +++ trunk/fclient/config.py 2007-11-06 12:35:18 UTC (rev 49) @@ -57,7 +57,7 @@ #TODO: assistant can not handle locale dependend docs yet self.assistant = assistant.Assistant(parent, profile=AssistantProfile, pages=DocPages) - self.defaultDownloadsDir = os.path.join(BaseDir, 'downloads') + self.defaultDownloadDir = os.path.join(BaseDir, 'download') self.fcpClientManager = FcpClientManager(parent) self.language = language.Language([LanguageDir, ], translationPrefixes=TranslationPrefixes) self.resources = resources.Resources([ResourceDir, ], ) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-06 12:34:48
|
Revision: 48 http://fclient.svn.sourceforge.net/fclient/?rev=48&view=rev Author: jUrner Date: 2007-11-06 04:34:50 -0800 (Tue, 06 Nov 2007) Log Message: ----------- minor changes Modified Paths: -------------- trunk/fclient/fclient_lib/fcp/fcp2_0.py Modified: trunk/fclient/fclient_lib/fcp/fcp2_0.py =================================================================== --- trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-05 12:50:42 UTC (rev 47) +++ trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-06 12:34:50 UTC (rev 48) @@ -222,6 +222,7 @@ NotAllDataFound = '28' class FcpError(Exception): pass + #TODO: better description of keys (MaxKeyLen ...) so we can use regex(es) to parse arbitrary strings for keys class FcpUri(object): """Wrapper class for freenet uris""" @@ -362,7 +363,7 @@ KeyboardInterrupt = 'Keyboard interrupt' SocketDied = 'Socket died' - #TODO: maybe speed up lookup of message name lookup by implementing integer message names + #TODO: maybe speed up message name lookup by implementing integer message names class Message(object): """Class wrapping a freenet message""" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-05 12:50:37
|
Revision: 47 http://fclient.svn.sourceforge.net/fclient/?rev=47&view=rev Author: jUrner Date: 2007-11-05 04:50:42 -0800 (Mon, 05 Nov 2007) Log Message: ----------- svn fix Added Paths: ----------- trunk/fclient/fclient_lib/qt4ex/ trunk/fclient/fclient_lib/qt4ex/LICENCE.MIT trunk/fclient/fclient_lib/qt4ex/README trunk/fclient/fclient_lib/qt4ex/__init__.py trunk/fclient/fclient_lib/qt4ex/assistant.py trunk/fclient/fclient_lib/qt4ex/ctrls/ trunk/fclient/fclient_lib/qt4ex/ctrls/__init__.py trunk/fclient/fclient_lib/qt4ex/ctrls/areatips.py trunk/fclient/fclient_lib/qt4ex/ctrls/checkarraywrap.py trunk/fclient/fclient_lib/qt4ex/ctrls/colorbutton.py trunk/fclient/fclient_lib/qt4ex/ctrls/compactpatwrap.py trunk/fclient/fclient_lib/qt4ex/ctrls/dragtool.py trunk/fclient/fclient_lib/qt4ex/ctrls/editboxwrap.py trunk/fclient/fclient_lib/qt4ex/ctrls/labelwrap.py trunk/fclient/fclient_lib/qt4ex/ctrls/mrumenu.py trunk/fclient/fclient_lib/qt4ex/ctrls/progressbarwrap.py trunk/fclient/fclient_lib/qt4ex/ctrls/tablewidget.py trunk/fclient/fclient_lib/qt4ex/ctrls/toolbarwrap.py trunk/fclient/fclient_lib/qt4ex/ctrls/treewidgetwrap.py trunk/fclient/fclient_lib/qt4ex/dlgs/ trunk/fclient/fclient_lib/qt4ex/dlgs/__init__.py trunk/fclient/fclient_lib/qt4ex/dlgs/dlgabout/ trunk/fclient/fclient_lib/qt4ex/dlgs/dlgabout/DlgAbout.ui trunk/fclient/fclient_lib/qt4ex/dlgs/dlgabout/Ui_DlgAbout.py trunk/fclient/fclient_lib/qt4ex/dlgs/dlgabout/__init__.py trunk/fclient/fclient_lib/qt4ex/dlgs/dlgfindreplace/ trunk/fclient/fclient_lib/qt4ex/dlgs/dlgfindreplace/DlgFindReplace.ui trunk/fclient/fclient_lib/qt4ex/dlgs/dlgfindreplace/Ui_DlgFindReplace.py trunk/fclient/fclient_lib/qt4ex/dlgs/dlgfindreplace/__init__.py trunk/fclient/fclient_lib/qt4ex/dlgs/dlgpreferences/ trunk/fclient/fclient_lib/qt4ex/dlgs/dlgpreferences/DlgPreferencesTree.ui trunk/fclient/fclient_lib/qt4ex/dlgs/dlgpreferences/Ui_DlgPreferencesTree.py trunk/fclient/fclient_lib/qt4ex/dlgs/dlgpreferences/__init__.py trunk/fclient/fclient_lib/qt4ex/lang/ trunk/fclient/fclient_lib/qt4ex/lang/qt4ex_de.ts trunk/fclient/fclient_lib/qt4ex/lang/qt4ex_en.ts trunk/fclient/fclient_lib/qt4ex/language.py trunk/fclient/fclient_lib/qt4ex/qt4ex.pro trunk/fclient/fclient_lib/qt4ex/qtools.py trunk/fclient/fclient_lib/qt4ex/res/ trunk/fclient/fclient_lib/qt4ex/res/language/ trunk/fclient/fclient_lib/qt4ex/res/language/LangCodes-ISO 639-1.txt trunk/fclient/fclient_lib/qt4ex/resources.py trunk/fclient/fclient_lib/qt4ex/scripts/ trunk/fclient/fclient_lib/qt4ex/scripts/__init__.py trunk/fclient/fclient_lib/qt4ex/scripts/manifest.py trunk/fclient/fclient_lib/qt4ex/scripts/pylupdate.py trunk/fclient/fclient_lib/qt4ex/scripts/qtpro.py trunk/fclient/fclient_lib/qt4ex/settingsbase.py Added: trunk/fclient/fclient_lib/qt4ex/LICENCE.MIT =================================================================== --- trunk/fclient/fclient_lib/qt4ex/LICENCE.MIT (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/LICENCE.MIT 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,21 @@ + +qt4ex -- Qt5 / PyQt4 extensions + +Copyright (c) 2007 J\xFCrgen Urner + + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file Added: trunk/fclient/fclient_lib/qt4ex/README =================================================================== --- trunk/fclient/fclient_lib/qt4ex/README (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/README 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,23 @@ + qt4ex -- Qt5 / PyQt4 extensions + + + +Version history: + +******************************************************************* +0.1.0 +******************************************************************* +news: + x. initial release + +bugfixes: + x. + + + + + + + + + Added: trunk/fclient/fclient_lib/qt4ex/__init__.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/__init__.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/__init__.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,11 @@ +"""Qt4 extensions + +""" + +__version__ = '0.1.0' +__author__ = 'Juergen Urner' +__emeil__ = 'jU...@ar...' +__licence__ = 'Mit' +__copyright__ = '(c) 2007 Juergen Urner' + + Added: trunk/fclient/fclient_lib/qt4ex/assistant.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/assistant.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/assistant.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,130 @@ + +import os +from PyQt4 import QtCore, QtGui + +__version__ = '0.0.1' +#***************************************************************** +# +#***************************************************************** +class Assistant(QtCore.QObject): + """Class to tame QAssistant a bit + + Usage: + + * Feed a profile and a dict of pages to the class + * call showPage() with the widget and the name of a page to show as params + * call close() with the widget as param the page is shown for when the widget is closed + + An internal refcount is kept to enshure QAssistant is not closed any sooner + than as when the last widget refering to it is closed. And as a bonus closing + a page for a widget will switch back to the page opend by the previous widget + + @note: currently only one open page / widget is supported + """ + def __init__(self, parent, profile, pages): + """ + @param profile: (absolute) path of the profile file to dfeed to assistant + @param pages: a dict{name-of-page: fpath-page} of pages + """ + QtCore.QObject.__init__(self, parent) + + self._referers = [[], []] # [widgets, page-names] + self._isInited = False + + self.qtAssistant = None + self.pages = pages + self.parent = parent + self.profile = profile + + if self.pages is None: + self.pages = {} + + + ################################################ + ## slots + ################################################ + def onAssistantClosed(self): + self._referers = [[], []] + + ################################################ + ## methods + ################################################ + def close(self, parent): + """Closes assistant if open and no other widget currently has a page open + """ + if self.qtAssistant is not None: + widgets, pages = self._referers + if parent in widgets: + i = widgets.index(parent) + del widgets[i] + del pages[i] + if widgets: + self.showPage(widgets[-1], pages[-1], register=False) + else: + if self.qtAssistant.isOpen(): + self.qtAssistant.closeAssistant() + return True + return False + + + def error(self, msg): + QtGui.QMessageBox.critical(self.parent, self.trUtf8('Qt assistant error'), msg) + return False + + + def initAssistant(self, parent): + """Inits QAssistant client if not already done""" + if self._isInited: + return + else: + self._isInited = True + + try: + from PyQt4 import QtAssistant + except ImportError: + pass + else: + self.qtAssistant = QtAssistant.QAssistantClient('', parent) + self.qtAssistant.connect( + self.qtAssistant, + QtCore.SIGNAL('error(const QSting&)'), + self.error + ) + self.qtAssistant.connect( + self.qtAssistant, + QtCore.SIGNAL('assistantClosed()'), + self.onAssistantClosed + ) + + L = QtCore.QStringList('-profile' ) << self.profile + self.qtAssistant.setArguments(L) + + + def showPage(self, parent, name, register=True): + """Shows a page + @param widget: widget to show the page for + @param name: name of the page + @param register: if True the widget will be registerd in the stack of open pages + """ + self.initAssistant(parent) + if self.qtAssistant is None: + return self.error(self.trUtf8('Can not display help. Assistant is not installed')) + + fpath = self.pages.get(name, None) + if fpath is None: + return self.error(self.trUtf8('Can not display help. No such page: ') + name) + if not os.path.isfile(fpath): + return self.error(self.trUtf8('Can not display help. Page not found: ') + '\n' + fpath) + if register and parent not in self._referers[0]: + self._referers[0].append(parent) + self._referers[1].append(name) + self.qtAssistant.showPage(fpath) + return True + + + + + + + + \ No newline at end of file Added: trunk/fclient/fclient_lib/qt4ex/ctrls/__init__.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/ctrls/__init__.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/ctrls/__init__.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1 @@ + Added: trunk/fclient/fclient_lib/qt4ex/ctrls/areatips.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/ctrls/areatips.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/ctrls/areatips.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,465 @@ +# -*- coding: utf-8 -*- + +"""Customized tooltips +""" + +# TODO: +# +# x. fix ComboBoxEditTip to act as promised +# x. add classes to handle temTips for editBoxes (...) +# +# x. ComboBox trouble. +# +# XXX jopefuly fixed by adding an event.type().Leave handler to cancel toolTip display XXX +# +# y. The dropdown of a combobox does not seem to eat up events +# An itemView underneath the dropdown still receives mouseMove events +# and may pop up an itemTip. No idea what todo. +# +# y. there is a glitch with comboBoxes. When an itemTip is popped up +# and the user clicks on it, the dropDown is closed, but the itemTip +# not. For some reason the itemView eats the mouse message and +# the itemTip never gets notified. +# +# x. there is a possible glitch aswell in itemTips when an itemView gets +# disabled / hidden while an itemTip is scheduled for display. No way to +# cancel the itemTip currently +# +# +import os +from PyQt4 import QtCore, QtGui + +__version__ = '0.0.1' +#*************************************************************************** +# +#*************************************************************************** +DEFAULT_SHOW_DELAY = 400 + +#*************************************************************************** +# +#*************************************************************************** +#code for the toolTip is taken from QToolTip.cpp + +class AreaTip(QtGui.QLabel): + """ + Customized tooltip class to show tooltips for a specified area + under the mouse cursor. + + """ + + def __init__(self, parent, showDelay=DEFAULT_SHOW_DELAY): + """constructor + + @param parent parent of the tooltip + @showDelay delay in milliseconds before the tootliptp is shown + """ + + + QtGui.QLabel.__init__(self, parent, QtCore.Qt.ToolTip) + self.hide() + + + self._currentAreaTipData = None + self._showTimer = QtCore.QTimer(self) + self._showDelay = showDelay + + self.connect( + self._showTimer, + QtCore.SIGNAL("timeout()"), + self.onShowTip) + + + + self.ensurePolished() + frameW = self.style().pixelMetric( + QtGui.QStyle.PM_ToolTipLabelFrameWidth, + None, + self + ) + self.setMargin( 1 + frameW) + self.setFrameStyle(QtGui.QFrame.NoFrame) + self.setAlignment(QtCore.Qt.AlignLeft) + self.setIndent(1) + + self.installEventFilter(self) + + opacity = self.style().styleHint( + QtGui.QStyle.SH_ToolTipLabel_Opacity, + None, + self + ) + self.setWindowOpacity( opacity / 255.0) + self.setPalette(QtGui.QToolTip.palette()) + + + + def showDelay(self): + """returns the current show delay""" + return self._showDelay + + def setShowDelay(self, n): + """adjusts the show delay + @param n milliseconds to delay the popup of the tooltip + """ + self._showDelay = n + + + def paintEvent(self, event): + """overwritten QWidget.paintEvent""" + p = QtGui.QStylePainter(self) + + opt = QtGui.QStyleOptionFrame() + opt.init(self) + p.drawPrimitive(QtGui.QStyle.PE_PanelTipLabel, opt) + p.end() + + QtGui.QLabel.paintEvent(self, event) + + + + def eventFilter(self, obj, event): + """event filter for the tooltip""" + + type_ = event.type() + if type_ == event.KeyPress or type == event.KeyRelease: + key = event.key() + modifiers = event.modifiers() + if modifiers & QtCore.Qt.KeyboardModifierMask or \ + key == QtCore.Qt.Key_Shift or \ + key == QtCore.Qt.Key_Control or \ + key == QtCore.Qt.Key_Alt or \ + key == QtCore.Qt.Key_Meta: + return False + + # TODO: delegate mouseactions to the window underneath the cursor + elif type_ in ( + event.Leave, + event.WindowActivate, + event.WindowDeactivate, + event.MouseButtonPress, + event.MouseButtonRelease, + event.MouseButtonDblClick, + event.FocusIn, + event.FocusOut, + event.Wheel, + ): + self.hideTip() + + return False + + + def onShowTip(self): + """called when the tooltip is about to be displayed""" + + if self._currentAreaTipData: + pt, text, rc = self._currentAreaTipData + pos = QtGui.QCursor().pos() + if not rc.contains(pos): + return + + desktop = QtGui.QApplication.desktop() + if desktop.isVirtualDesktop(): + scr = desktop.screenNumber(pt) + else: + scr = desktop.screenNumber(self.parent()) + + if os.name == "mac": + screen = desktop.availableGeometry(scr) + else: + screen = desktop.screenGeometry(scr) + + self.setText(text) + fm = QtGui.QFontMetrics(self.font()) + extra = QtCore.QSize(1, 0) + # Make it look good with the default ToolTip font on Mac, + # which has a small descent. + if fm.descent() == 2 and fm.ascent() >= 11: + extra.setHeight(extra.height() + 1) + + self.resize(self.sizeHint() + extra) + + self.setWordWrap(QtCore.Qt.mightBeRichText(text)) + self.move(pt) + self.show() + + + def cancel(self): + if self._showTimer.isActive(): + self._showTimer.stop() + + + def hideTip(self): + """hides the tooltip""" + self.cancel() + self.hide() + + + def showText(self, pt, text, rc): + """shows the tooltip + + @param pt: (global coordinates) point to show the tooltip at (left/top) + @param text: text to show (if emtpty string the tooltip is not shown) + @param rc: (global coordinates) bounding rect the tooltip is assigned to + + """ + self._currentAreaTipData = (pt, text, rc) + self.hideTip() + if not text.isEmpty(): + self._showTimer.start(self._showDelay) + + + def showItemTip(self, itemView, index): + """Shows an item tip for item views (QTreeView, QListView ....) + for the specified index if necessary. + + @param itemView: item view to display the tip for + @param index: QModelIndex of the item to display the tip for + + Note: the tolltip will only be displayed if the text of the item is truncated + or the item is not entirely visible. + """ + rc = self.getTruncatedItemRect(itemView, index) + if rc is not None: + text = index.data().toString() + if not text.isEmpty() and itemView.hasFocus(): + self.showText(rc.topLeft(), text, rc) + + + def getTruncatedItemRect(self, itemView, index): + """Helper method. Returns the rect of a truncted item in an item view + @param itemView: QAbstractItemView + @param index: index of the item + @return: (global coordinates QRect) if the item is truncated, None otherwise + @note: this method is callled by showItemTip() to determine wether to + display a toolTip for an item or not. Overwrite to customize + """ + viewport = itemView.viewport() + rcCli = viewport.contentsRect() + rcActual = itemView.visualRect(index) + rcDesired = QtCore.QRect( + rcActual.topLeft(), + itemView.sizeHintForIndex(index) + ) + if not rcActual.contains(rcDesired, False) or \ + not rcCli.contains(rcDesired, False): + + rcActual.moveTo( viewport.mapToGlobal(rcActual.topLeft()) ) + return rcActual + + +#**************************************************************************** +# +#**************************************************************************** +class ItemTips(QtCore.QObject): + """Equips an itemView (QTreeView, QListView ....) with tooltips for truncated items (item tips) + """ + + def __init__(self, itemView, showDelay=DEFAULT_SHOW_DELAY): + """ + @param itemView: itemView to equip with item tips + param showDelay: delay in (miliseconds) after wich to show a tooltip for an item + """ + QtCore.QObject.__init__(self, itemView) + + self.areaTip = AreaTip(itemView, showDelay=showDelay) + self.itemView = itemView + + itemView.setMouseTracking(True) # enshure we get mouseMove messages + itemView.viewport().installEventFilter(self) + + + def eventFilter(self, obj, event): + """Event filter for the itemTip""" + if event.type() == event.MouseMove: + if self.areaTip.isHidden(): + + # hope this check fixes a glitch with comboBoxes. Sometimes am itemTip + # did pop up when the combos dropdown was closed. + if not self.itemView.isHidden() and self.itemView.isEnabled() and self.itemView.underMouse(): + index = self.itemView.indexAt(event.pos()) + if index.isValid(): + self.areaTip.showItemTip(self.itemView, index) + if event.type() == event.Leave: + self.areaTip.cancel() + + return False + + + def setEnabled(self, flag): + self.areaTip.setEnabled(flag) + pass + + def isEnabled(self): + self.areaTip.isEnabled() + + def getShowDelay(self): + """returns the current show delay""" + return self.areaTip.getShowDelay() + + def setShowDelay(self, n): + """adjusts the show delay + @param n milliseconds to delay the popup of the tooltip + """ + return self.areaTip.setShowDelay(delay) + +#**************************************************************************** +# +#**************************************************************************** +class ComboBoxEditTips(QtCore.QObject): + """Equips a QComboBox with a toolTip popping up when the text in its edit box is truncated + + @note: this class does currently not quite what it is supposed todo. A normal toolTip is + sisplayed instead of a toolTip that covers the editBox. This may change in future versions. + """ + + def __init__(self, comboBox, showDelay=DEFAULT_SHOW_DELAY): + QtCore.QObject.__init__(self, comboBox) + + self.connect( + comboBox, + QtCore.SIGNAL('currentIndexChanged(const QString &)'), + self._adjustToolTip + ) + + comboBox.installEventFilter(self) + self.comboBox = comboBox + + + def eventFilter(self, obj, event): + if event.type() == event.Resize: + self._adjustToolTip(self.comboBox.currentText()) + return False + + def _adjustToolTip(self, text): + fm = self.comboBox.fontMetrics() + style = self.comboBox.style() + rc = self.comboBox.contentsRect() + rc2 = style.subControlRect( + style.CC_ComboBox, + QtGui.QStyleOptionComboBox(), + style.SC_ComboBoxEditField, + ) + + cx = rc.width() + rc2.width() + w = fm.width(text) + if w > cx -2: + self.comboBox.setToolTip(text) + else: + self.comboBox.setToolTip('') + + + + +#******************************************************************************************** +# some test guis +#******************************************************************************************** +def _testItemModel(): + """Tests itemTips for a QTableView""" + + import sys + + + class TestModel(QtCore.QAbstractTableModel): + + + def __init__(self, table): + QtCore.QAbstractTableModel.__init__(self, table) + + self.__table = table + self.__columns = ('foo', "foo") + self.__rows = ['foo'*10 for i in range(10)] + + self.areaTip = AreaTip(table) + + + def hasIndex(self, row, column): + """QAbstractTableModel.hasIndex() implementation""" + if -1 < column <= len(self.__columns): + if -1 < row < len(self.__rows): + return True + return False + + def rowCount(self, parent): + """QAbstractTableModel.rowCount() implementation""" + return len(self.__rows) + + + def columnCount(self, parent): + """QAbstractTableModel.columnCount() implementation""" + return len(self.__columns) + + + def headerData(self, section, orientation, role): + """QAbstractTableModel.headerData() implementation""" + if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal: + return QtCore.QVariant(self.__columns[section]) + return QtCore.QVariant() + + + def data(self, index, role): + """QAbstractTableModel.data() implementation""" + + if self.hasIndex(index.row(), index.column()): + if role == QtCore.Qt.DisplayRole: + return QtCore.QVariant(self.__rows[index.column()]) + + # display tooltip if text does not fit column width + # or item is not entirely visible + elif role == QtCore.Qt.ToolTipRole: + self.areaTip.showItemTip(self.__table, index) + + + return QtCore.QVariant() + + app = QtGui.QApplication(sys.argv) + dlg = QtGui.QWidget() + + grid = QtGui.QHBoxLayout(dlg) + + tv = QtGui.QTableView(dlg) + grid.addWidget(tv) + + tv.setModel(TestModel(tv)) + + dlg.show() + res = app.exec_() + sys.exit(res) + + + +def _testComboBox(): + """Tests itemTips for a QCombobox""" + + + import sys + + app = QtGui.QApplication(sys.argv) + w = QtGui.QWidget() + + b = QtGui.QGridLayout(w) + c = QtGui.QComboBox(w) + c.setMinimumContentsLength(5) + b.addWidget(c) + + itemTips = ItemTips(c.view()) + + + for i in range(10): + c.addItem('loooooooooooooooooooooooooooooong-itemText-%s' % i) + + w.show() + res = app.exec_() + sys.exit(res) + + + +#*********************************************************************** +# +#*********************************************************************** +if __name__ == "__main__": + pass + #_testItemModel() + #_testComboBox() + + + Added: trunk/fclient/fclient_lib/qt4ex/ctrls/checkarraywrap.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/ctrls/checkarraywrap.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/ctrls/checkarraywrap.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,93 @@ +"""wrapper class for handling checkboxes (...) as bit array + + +CONST_1 = 1 +CONST_2 = 2 +CONST_3 = 4 + +checks = { + CONST_1: ckeckbox1, + CONST_2: checkbox2, + CONST_3: cjeckbox3, + + } + + +flags = CheckArray(checks, initvalue=CONST_1 | CONST_2) + +flags |= CONST_3 # check 3rd checkbox +flags.showChecks(CONST_1 | CONST_2) # hide 3rd checkbox + +""" + +from PyQt4 import QtCore +#******************************************************* +# +#******************************************************* +class CheckArrayWrap(QtCore.QObject): + + def __init__(self, parent, checks, value=None): + QtCore.QObject.__init__(self, parent) + self._checks = checks + + if value is not None: + self.setChecks(value) + + ###################################### + ## public methods + ###################################### + def getValue(self): + value = 0 + for const, ck in self._checks.items(): + if ck.isChecked(): + value |= const + return value + + + def setChecks(self, flags): + for const, ck in self._checks.items(): + if flags & const: + ck.setCheckState(QtCore.Qt.Checked) + else: + ck.setCheckState(QtCore.Qt.Unchecked) + flags &= ~const + if not flags: + break + + + def showChecks(self, flags): + for const, ck in self._checks.items(): + if flags & const: + check.control.setEnabled(True) + check.control.show() + else: + check.control.setEnabled(False) + check.control.hide() + flags &= ~const + if not flags: + break + + + def __or__(self, n): + value = self.getValue() | n + self.setChecks(value) + return self + def __ior__(self, n): return self.__or__(n) + def __ror__(self, n): return self.__or__(n) + + def __and__(self, n): + value = self.getValue() & n + self.setChecks(value) + return self + def __iand__(self, n): return self.__and__(n) + def __rand__(self, n): return self.__and__(n) + + def __xor__(self, n): + value = self.getValue() ^ n + self.setChecks(value) + return self + def __ixor__(self, n): return self.__xor__(n) + def __rxor__(self, n): return self.__xor__(n) + + + Added: trunk/fclient/fclient_lib/qt4ex/ctrls/colorbutton.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/ctrls/colorbutton.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/ctrls/colorbutton.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,238 @@ +"""pyQt4 ColorButton with drag and drop support + +mostly taken from +[http://api.kde.org/4.0-api/kdelibs-apidocs/kdeui/html/classKColorButton.html] +with some minor adjustements +""" + +import sys, os + +#--> rel import hack +d = os.path.dirname(os.path.dirname(__file__)) +sys.path.insert(0, d) + +import qtools + +sys.path.pop(0) +del d +#<-- rel import hack + + +from PyQt4 import QtCore, QtGui + +__author__ = 'Juergen Urner' +__email__ = 'jU...@ar...' +__version__ = '0.1.0' +#************************************************************************ +# +#************************************************************************ +class ColorButtonWrap(QtCore.QObject): + """Event filter to transform a QPushButton into a color button + + The button will popup a color selection dialog when hit ++ the button supports + drag / drop of colors. + + @signal colorChanged(const QColor*): emited when the user changes the color of + the button via color dialog or color drop. + """ + + def __init__(self, button, color=None, fitColor=True): + """ + @param button: button to wrap + @param color: initial color. If None, initial color is black + @fitColor: if True the color shown is adjusted to fit the contents of the button + """ + QtCore.QObject.__init__(self, button) + + self.button = button + self._color = color or QtGui.QColor('black') + self.fitColor = fitColor + + button.setAcceptDrops(True) + button.installEventFilter(self) + self.connect(button, QtCore.SIGNAL('clicked()'), self.chooseColor) + + + def _initStyleOption(self, opt): + opt.initFrom(self.button) + opt.text.clear() + opt.icon = QtGui.QIcon() + opt.features = QtGui.QStyleOptionButton.None + + + def eventFilter(self, obj, event): + """Event filter for the button wrapped""" + + eventType = event.type() + + if eventType == event.Paint: + painter = QtGui.QPainter(self.button) + + # draw bevel + opt = QtGui.QStyleOptionButton() + self._initStyleOption(opt) + self.button.style().drawControl(QtGui.QStyle.CE_PushButtonBevel, opt, painter, self.button) + + # draw color box + #First, sort out where it goes + labelRect = self.button.style().subElementRect(QtGui.QStyle.SE_PushButtonContents, opt, self.button) + shift = self.button.style().pixelMetric(QtGui.QStyle.PM_ButtonMargin) + labelRect.adjust(shift, shift, -shift, -shift); + x, y, w, h = labelRect.getRect() + + if self.button.isChecked() or self.button.isDown(): + x += self.button.style().pixelMetric(QtGui.QStyle.PM_ButtonShiftHorizontal) + y += self.button.style().pixelMetric(QtGui.QStyle.PM_ButtonShiftVertical) + if self.button.isEnabled(): + fillCol = self.getColor() + else: + fillCol = self.button.palette().color(self.button.backgroundRole()) + + QtGui.qDrawShadePanel(painter, x, y, w, h, self.button.palette(), True, 1, None) + if fillCol.isValid(): + painter.fillRect( x+1, y+1, w-2, h-2, fillCol) + + if self.button.hasFocus(): + focusRect = self.button.style().subElementRect(QtGui.QStyle.SE_PushButtonFocusRect, opt, self.button) + focusOpt = QtGui.QStyleOptionFocusRect() + focusOpt.initFrom(self.button) + focusOpt.rect = focusRect + focusOpt.backgroundColor = self.button.palette().background().color() + self.button.style().drawPrimitive(QtGui.QStyle.PE_FrameFocusRect, focusOpt, painter, self.button) + return True + + elif eventType == event.MouseMove: + if not self._mouse_pressed: + return False + + if event.buttons() & QtCore.Qt.LeftButton: + if (self._mousepress_pos - event.pos()).manhattanLength() > QtGui.QApplication.startDragDistance(): + self._mouse_pressed = False + self.button.setDown(False) + drag = QtGui.QDrag(self.button) + data = QtCore.QMimeData() + data.setColorData(QtCore.QVariant(self._color)) + drag.setMimeData(data) + drag.start(QtCore.Qt.CopyAction) + return True + + elif eventType == event.MouseButtonPress: + self._mousepress_pos = QtCore.QPoint(event.pos()) + self._mouse_pressed = True + return False + + elif eventType == event.MouseButtonRelease: + self._mouse_pressed = False + return False + + elif eventType == event.DragEnter: + if event.mimeData().hasColor(): + event.accept() + else: + event.ignore() + return True + + elif eventType == event.DragMove: + if event.mimeData().hasColor(): + event.accept() + else: + Event.ignore() + return True + + elif eventType == event.Drop: + if event.mimeData().hasColor(): + v = event.mimeData().colorData() + self.setColor( QtGui.QColor(v.toString() ) ) + else: + event.ignore() + return True + + + # TODO: copy & paste + if eventType == event.KeyPress: + + if qtools.isStandardKeyEvent(event, QtGui.QKeySequence.Copy): + mimeData = QtCore.QMimeData() + mimeData.setColorData(QtCore.QVariant(self.getColor() ) ) + QtGui.QApplication.clipboard().setMimeData(mimeData, QtGui.QClipboard.Clipboard) + elif qtools.isStandardKeyEvent(event, QtGui.QKeySequence.Paste): + mimeData = QtGui.QApplication.clipboard().mimeData(QtGui.QClipboard.Clipboard) + if mimeData.hasColor(): + v = mimeData.colorData() + self.setColor( QtGui.QColor(v.toString() ) ) + + + + + + """ + void KColorButton::keyPressEvent( QKeyEvent *e ) + { + int key = e->key() | e->modifiers(); + + if ( KStandardShortcut::copy().contains( key ) ) { + QMimeData *mime=new QMimeData; + KColorMimeData::populateMimeData(mime,color()); + QApplication::clipboard()->setMimeData( mime, QClipboard::Clipboard ); + } + else if ( KStandardShortcut::paste().contains( key ) ) { + QColor color=KColorMimeData::fromMimeData( QApplication::clipboard()->mimeData( QClipboard::Clipboard )); + setColor( color ); + } + else + QPushButton::keyPressEvent( e ); + } + """ + + return False + + + ############################################# + ## methods + ############################################# + def chooseColor(self): + """Pops up a color selection dialog to adjust the color of the button""" + color = QtGui.QColorDialog.getColor(self._color, self.button) + if color.isValid(): + self.setColor(color) + self.emit(QtCore.SIGNAL('colorChanged(const QColor*)'), color) + + def getColor(self): + """Returns the color of the button + @return: QColor + """ + return self._color + + def setColor(self, color): + """Sets the color of the button + @param color: QColor + """ + self._color = color + self.button.update() + self.emit(QtCore.SIGNAL('colorChanged(const QColor*)'), color) + + +#******************************************************************** +# +#******************************************************************** +if __name__ == '__main__': + import sys + + app = QtGui.QApplication(sys.argv) + w = QtGui.QWidget(None) + box = QtGui.QVBoxLayout(w) + colors = ('yellow', 'red', 'green', 'blue') + for color in colors: + bt = QtGui.QPushButton(w) + wrap = ColorButtonWrap(bt, QtGui.QColor(color)) + box.addWidget(bt) + + + + w.show() + res = app.exec_() + sys.exit(res) + + + + \ No newline at end of file Added: trunk/fclient/fclient_lib/qt4ex/ctrls/compactpatwrap.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/ctrls/compactpatwrap.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/ctrls/compactpatwrap.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,576 @@ + +"""module to handles compacting of filepaths and urls + + @author Juergen Urner + """ + +import operator +import os +import re + +import ntpath +import posixpath +import macpath + +from PyQt4 import QtCore, QtGui + +# TODO: +# +# x. builtin type() is shaddowed in some methods +# x. rename DEFAULT to DEFAULT_TYPE + +#******************************************************************************* +# compacts a filepath to fit into a desired width +# As measuring function any function may be passed that returns True if a path +# is short enough, False otherwise. Default is len() +# +# +# IDEA: +# +# weight path components by priority, 0 is lowest. +# +# 2 0 1 3 +# mypath = "aaa/bb/ccc/ddd" +# +# genearte a sort matrix [[nPriority0, indexComponent0, component0], [...]] +# so we only have to flip the matrix to either get the priority order of the +# components and eat components from martx[0] to matrix[N] or the restore +# the original component order +# +# matrix = [[2, 0, "aaa"], [0, 1, "bbb"], [1, 2, "ccc"], [3, 3, "ddd"]] +# matrix.sort() +# matrix = [[0, 1, "bbb"], [1, 2, "ccc"], [2, 0, "aaa"], [3, 3, "ddd"]] +# +# I am shure that so. with better algo skills than me can come up with a nicer +# solution to the problem. I would like to see that. +# +#********************************************************************************** + +#************************************************************************************** +# helpers +#************************************************************************************** +_drive_pat = re.compile(r'([a-zA-Z]\:)(?=/|\\|\Z)') +def is_drive(fpath): + return bool(_drive_pat.match(fpath)) + +_host_pat = re.compile(r'([^:/?#]+:[^/]*///?)') +def is_host(fpath): + return bool(_host_pat.match(fpath)) + +def is_root(fpath): + return fpath.startswith("/") + +def is_mac_root(fpath): + if fpath: + return ':' in fpath and fpath[0] != ':' + return False + +if os.path.__name__.startswith("nt"): + DEFAULT = "nt" +elif os.path.__name__.startswith("mac"): + DEFAULT = "mac" +elif os.path.__name__.startswith("posix"): + DEFAULT = "posix" +else: + DEFAULT = os.path.__name__ + +PATH_MODULES = { # modules to handle path actions for path types + 'nt': ntpath, + 'posix': posixpath, + 'mac': macpath, + 'url': posixpath, + DEFAULT: os.path, + } + +#************************************************************************************** +# +#************************************************************************************** +class PathCompacter(object): + """ + implementation of the compact path algorithm. + + The class is available as compactPath() function in the module. + For a description of parameters see PathCompacter.__call__() + + @note: you do not have to use this class directly. On the module level + its functionality is available as function compactPath() + """ + + ELLIPSIS = "..." + PARDIR = ".." + DOT = "." + + def __init__(self): + """ + constructor + """ + self.root = "" + self.path_module = None + + + def chewComponent(self, cpn, fromLeft=True): + """ + shorten a single path component by one char + + @param cpn the component to to be shortened + @keyparam fromLeft if True the component is shortened on the left side, + if False on the right + """ + if fromLeft: + while True: + cpn = cpn[3: ] + if not cpn: + yield self.PARDIR + break + + cpn = self.PARDIR + cpn + yield cpn + + else: + while True: + cpn = cpn[ :-3] + if not cpn: + yield self.PARDIR + break + cpn = cpn + self.PARDIR + yield cpn + + + def getMatrix(self, n): + """ + generates a sort matrix of lenght (n) + """ + if n == 0: + return [] + + # ...enshure last item in range is last item in the matrix + if n % 2: + rng = range(n) + else: + rng = range(n -1) + + start = len(rng) / 2 + out = [] + for i in rng: + if i == start: + out.append([0, ]) + elif i < start: + out.append([ (start - i) *2 - 1, ]) + else: + out.append([ (i - start) *2, ]) + + if not n % 2: + out.append([len(out)]) + return out + + + def matrixToPath(self, matrix): + """ + private method to generate a filepath from a sort matrix + """ + getter = operator.itemgetter + out = [] + + matrix.sort(key=getter(1)) + for i in matrix: + out.append(i[2]) + + matrix.sort() + + if self.type == "url": + return self.root + "/".join(out) + elif self.type == "nt": + return self.path_module.join(self.root, *out) + elif self.type == "posix": + return self.path_module.join(self.root, *out) + elif self.type == "mac": + return self.path_module.join(self.root, *out) + return self.path_module.join(*out) + + + def matrixFromPath(self, fpath): + """ + private method to convert a filepath into a sort matrix + """ + arrPath = self._path_to_list(fpath) + if not arrPath: + return [] + + self.root = "" + if self.type == "nt": + if is_drive(arrPath[0]): + self.root = arrPath[0] + arrPath.pop(0) + elif self.type == "posix": + if is_root(arrPath[0]): + self.root = arrPath[0] + arrPath.pop(0) + elif self.type == "mac": + if is_mac_root(arrPath[0]): + self.root = arrPath[0] + arrPath.pop(0) + elif self.type == "url": + host = arrPath[0] + "//" + if is_host(host): + self.root = host + arrPath.pop(0) + + matrix = self.getMatrix(len(arrPath)) + i = 0 + for item in matrix: + item.append(i) + item.append(arrPath[i]) + i += 1 + + return matrix + + + def _path_to_list(self, fpath): + head = fpath + out = [] + # taken from: http://www.jorendorff.com/articles/python/path/ + while head != self.path_module.curdir and head != self.path_module.pardir: + prev = head + head, tail = self.path_module.split(head) + if head == prev: break + out.append(tail) + if head: + out.append(head) + out.reverse() + return out + + + def __call__(self, fpath, w, measure=len, max_pardirs=2, type=DEFAULT): + """ + compacts a filepath or url to fit into the desired width + + @param fpath the filepath to be compacted + @param w the desired width the filepath should fit into + @keyparam measure function to measure the length of the filepath. + Default is len() but may be any other function that takes a filepath as + argument and returns its length as undigned int. + @keyparam max_pardirs maximum number of compacted parent dirs ("../") allowed + in the compacted path. Must be > 0. + @keyparam type use this explicitely specify the type of path passed. + Can be "posix", "nt", "mac" or "url" or DEFAULT. DEFAULT processes the + path with whatever os.path currently is. + + """ + if max_pardirs < 1: + raise PathError("max_pardirs < 1 is not allowed") + + if not fpath: + return "" + if measure(fpath) < w: + return fpath + + self.type, self.path_module = type, PATH_MODULES[type] + matrix = self.matrixFromPath(fpath) + if not matrix: + return "" # error here, the pattern does not match anything + matrix.sort() + + # process our sort matrix + i = 0 + while True: + + if len(matrix) > 2: + # ...chew next component till it's exhausted + item = matrix[i] + for cpn in self.chewComponent(item[2], fromLeft=False): + item[2] = cpn + path = self.matrixToPath(matrix) + if measure(path) < w: + return path + + if cpn == self.PARDIR: + i += 1 + break + + # ...pop 1 pardir if necessary + if i > max_pardirs or i >= len(matrix) -1: + matrix.pop(0) + matrix[0][2] = self.ELLIPSIS + i -= 1 + path = self.matrixToPath(matrix) + if measure(path) < w: + return path + + else: + # finalize + # 1. chew root away + if self.root: + for cpn in self.chewComponent(self.root, fromLeft=True): + self.root = cpn + if self.root == self.PARDIR: + if not matrix: + if measure(self.PARDIR) < w: + return self.PARDIR + if measure(self.DOT) < w: + return self.DOT + return "" + self.root = "" + break + path = self.matrixToPath(matrix) + if measure(path) < w: + return path + + path = self.matrixToPath(matrix) + if measure(path) < w: + return path + + # 2. chew filename away + if len(matrix) == 2: + component = matrix[0][2] + matrix[1][2] + if measure(component) < w: + return component + else: + component = matrix[0][2] + + for cpn in self.chewComponent(component, fromLeft=True): + if measure(cpn) < w: + return cpn + + if cpn == self.PARDIR: + if measure(self.DOT) < w: + return self.DOT + break + + # done it + break + + return "" + + +#******************************************** +# init PathCompacter() as function +#******************************************** +compactPath = PathCompacter() +#******************************************************************************** +# lightweight wrapper class for QLabels to display a compactable filepath or url +# the class is designed as a wrapper not a derrived class so it does not get in the +# way with QtDesigner +# +# problem to tackle is that every setText() triggers a resize of the whole layout +# so the filepath can't be adjusted in resizeEvent() (recursive). We handle +# paintEvent() instead and draw the label from scratch. +# +# similar wrappers can be easily implemented for QMenuItem() and QListView() +# header controls (...). +# +# Usage hint: +# if used on statusbars you should adjust the stretch factor, otherwise +# the label might appear invisible, 'cos it may not get resized. +# +# The label has a minimum size of 0 by default. To force a minimum +# size set any initial text to the label wrapped. Effect is 1. the text will not be +# displayed ++ the label will not get resized any smaller than this text. +# +#******************************************************************************* +class PathLabelWrap(object): + """ + class wrapping a QLabel to display a filepath or url that is compacted on + resizing of the label + """ + + def __init__(self, label, fpath="", prefix="", max_pardirs=1, type=DEFAULT): + """ + constructor + + @param label an initialized QLabel to wrap compactPath functionality around + @param fpath the filepath or url the label should display + @keyparam prefix: chars to be used as fixed prefix of the path like: "file: my/path/here" + @keyparam max_pardirs maximum number of compacted parent dirs ("../") allowed + in the compacted path. Must be > 0. + @keyparam type: use this to explicitely specify the type of path specified. + Can be "nt", "posix", "mac", "url" or DEFAULT to use whatever type of path + os.path currently handles. + + Note: currently the wrapper does not know how to display text for disabled state. + So disabling the underlaying QLabel will not result in the desired effect. + """ + self.label = label + self.fpath = fpath + self.prefix = prefix + self.max_pardirs= max_pardirs + self.type = type + + self.label.paintEvent = self.onPaint # overwrite + # for testing: + #self.label.setPaletteBackgroundColor(qt.QColor(10, 255, 22)) + + + def onPaint(self, event): + """ + overwritten method to handle painting of the filepath + """ + + # draw the label from scratch + # TODO: check if there is an easier processing via QStyle.drawControl() + fm = self.label.fontMetrics() + rc = self.label.rect() + frameW = self.label.frameWidth() + indent = self.label.indent() + if indent < 0: # see Qt docs: label.indent() + if frameW > 0: + indent = fm.width("x") /2 + else: + indent = 0 + rc.adjust(frameW + indent, frameW , -(frameW + indent), -frameW) + + w = rc.width() + if self.prefix: + w -= fm.width(self.prefix) + + fpath = compactPath( + self.fpath, + w, + measure=fm.width, + max_pardirs=self.max_pardirs, + type=self.type + ) + + painter = QtGui.QPainter(self.label) + painter.eraseRect(self.label.rect()) + self.label.drawFrame(painter) + # TODO: draw enabled/disabled text + # textColor is already ok but I haven't found a way to draw disabled text + # embossed + # if self.label.isEnabled(): + # else: + if self.prefix: + painter.drawText(rc, self.label.alignment(), '%s%s' % (self.prefix, fpath)) + else: + painter.drawText(rc, self.label.alignment(), fpath) + + + def setPath(self, fpath, prefix=None, max_pardirs=None, type=None): + """ + sets the filepath or url to be displayed + + If any of the keyword params is not None, the according property is adjusted to the specified value + """ + if prefix is not None: + self.prefix = prefix + if max_pardirs is not None: + self.max_pardirs = max_pardirs + if type is not None: + self.type = type + self.fpath = fpath + self.label.update() + + def getPath(self): + """ + retrieves the (uncompacted) filepath or url + """ + return self.fpath + +#****************************************************************************** +# tests +#****************************************************************************** +if __name__ == "__main__": + + def testGui(): + + CAPTION = "compactPath - [%s]" + PATHS = ( + ('posix', "/my/very/long/path/containing/many/compoponents/here.txt"), + ('nt', "c:\\my\\very\\long\\path\\containing\\many\\compoponents\\here.txt"), + ('mac', "my:very:long:path:containing:many:compoponents:here.txt"), + ('url', "http://my/very/long/path/containing/many/compoponents/here.txt"), + ) + BLINDTEXT = "xxxxxxxxxx" # this is set as text to the labels to force a minimum size. Adjust to your needs + + class TestGui(QtGui.QMainWindow): + """test gui""" + + def __init__(self): + QtGui.QMainWindow.__init__(self) + + self.mainWidget = QtGui.QWidget(self) + self.setCentralWidget(self.mainWidget) + + # NOTE: all the sep="" params are only thrown in for test purposes. + # + # Too bad, the label in the caption bar seems not to be available in Qt. So there is no way + # to adjust it dynamically. So init to some fixed width. Note that the width of the caption + # bar label has no effect on the GUIs width. + # + pathType, fpath = PATHS[0] + fpath = compactPath(fpath, 50 - len(CAPTION), type=pathType) + self.setWindowTitle(CAPTION % fpath) + + # throw labels into the Gui + layout = QtGui.QVBoxLayout(self.centralWidget()) + frameStyle = QtGui.QLabel.Sunken | QtGui.QLabel.Box + self.pathLabels = [] + for pathType, fpath in PATHS: + label = QtGui.QLabel(BLINDTEXT, self.mainWidget) + w = PathLabelWrap(label, fpath=fpath, prefix=pathType + ': ', type=pathType) + label.setFrameStyle(frameStyle) + layout.addWidget(label) + + # just a test label to see if our owner drawn labels are ok + self.labelTest = QtGui.QLabel("just a test", self.mainWidget) + self.labelTest.setFrameStyle(frameStyle) + layout.addWidget(self.labelTest) + + layout.addItem(QtGui.QSpacerItem(0, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding)) + + # add another one on the statusbar + self.status = self.statusBar() + self.statusLabelPosix = PathLabelWrap(QtGui.QLabel(BLINDTEXT, self.status), fpath=fpath) + self.status.addWidget(QtGui.QLabel('status::', self.status)) + self.status.addWidget(self.statusLabelPosix.label, 10) + self.status.addWidget(QtGui.QLabel('foo-bar', self.status)) + + def styleChange(self, oldStyle): + """styleChange handler""" + self.update() + + import sys + a = QtGui.QApplication(sys.argv) + QtCore.QObject.connect(a,QtCore.SIGNAL("lastWindowClosed()"),a,QtCore.SLOT("quit()")) + w = TestGui() + w.show() + a.exec_() + + ## + testGui() + + + + def test(): + """ + test compactPath() + """ + + def compact(path, start, stop, type=DEFAULT): + for i in range(start +1, stop, -1): + p = compactPath(path, i, max_pardirs=1, type=type) + print i, len(p), repr(p) + + def test1(): + path = "a:\\eeeeee\\fff\\foobarbaz\\ddd" + compact(path, len(path), 0, type="nt") + test1() + + def test2(): + path = "/home/eeeeee/fff/foobarbaz/ddd" + compact(path, len(path), 0, type="posix") + test2() + + def test3(): + path = "home:eeeeee:fff:foobarbaz:ddd" + compact(path, len(path), 0, type="mac") + test3() + + def test4(): + path = "home://eeeeee/fff/foobarbaz/ddd" + compact(path, len(path), 0, type="url") + test4() + + ## + #test() + + Added: trunk/fclient/fclient_lib/qt4ex/ctrls/dragtool.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/ctrls/dragtool.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/ctrls/dragtool.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,205 @@ +"""DraggableTool widget""" + + +from PyQt4 import QtCore, QtGui + +__version__ = '0.0.4' +#********************************************************************** +# +#********************************************************************** +class DragToolEventFilter(QtCore.QObject): + """helper widget to handle draging of bitmaps as cursor + + @note: the widget has to be visible and at least 1x1 in size, otherwise Qt refuses to + pass the mouse capture over to the widget + """ + + + def __init__(self, button, cursor=None, releaseOnClick=False): + """constructor + + @arg parent: parent of the grabber widget + @arg cursor: QCursor + @arg grab_cursor: if 'grab_cursor' is True the user may drag the pixmap around + without holding the left mouse button. If False the cursor is released as soon as + the user releases the the left mouse button. In both cases dragging can be endet + by hitting the escape key. In "grab_cursor" mode dragging may be endet aswell by + hitting the right mouse button. + + """ + QtGui.QWidget.__init__(self, button) + + self._isDraging = False + self._releaseOnClick = releaseOnClick + + self.button = button + self.cursor = None + + self.setCursor(cursor) + self.button.installEventFilter(self) + button.connect(button, QtCore.SIGNAL('clicked()'), self.startDrag) + + + def getReleaseOnClick(self): + return self._releaseOnClick + + def setReleaseOnClick(self, flag): + self._releaseOnClick = flag + + + def isDraging(self): + """Checks if the drag tool is currently about to be draged""" + return self._isDraging + + + def startDrag(self): + """Starts dragging the cursor around""" + + if self.cursor is None: + raise ValueError('No cursor set') + + if not self.isDraging(): + self._isDraging = True + #self.button.grabKeyboard() + self.button.grabMouse(self.cursor) + self.button.setMouseTracking(True) + pt = QtGui.QCursor().pos() + self.emit(QtCore.SIGNAL('startDrag(int, int)'), pt.x(), pt.y()) + + + def getCursor(self): + """Returns the dragg cursor + @return: QCursor + """ + return self.cursor + + + def setCursor(self, cursor): + """Sets the pixmap to be dragged around + @arg cursor: QCursor + """ + self.cursor = cursor + + + def setGrabCursor(self, flag): + self.grab_cursor = flag + + def getGrabCursor(self, flag): + return self.grab_cursor + + + def endDrag(self, x=0, y=0, canceled=False): + if self.isDraging(): + self._isDraging = False + #self.button.releaseKeyboard() + self.button.releaseMouse() + self.button.setMouseTracking(False) + if canceled: + self.emit(QtCore.SIGNAL('dragCanceled()')) + else: + self.emit(QtCore.SIGNAL('draged(int, int)'), x, y) + self.emit(QtCore.SIGNAL('dragEnd()')) + + + def eventFilter(self, obj, event): + + # TODO: grabKeyboard() will eat all keyboard input for a Gui but events never arrive here + #if event.type() == event.KeyRelease: + # if event.key() == QtCore.Qt.Key_Escape: + # self.endDrag(canceled=True) + # return True + + if event.type() == event.MouseButtonPress: + if event.button()== QtCore.Qt.LeftButton: + if self.isDraging(): + if self._releaseOnClick: + self.endDrag(event.globalX(), event.globalY(), canceled=False) + return True + + elif event.type() == event.MouseButtonRelease: + if event.button()== QtCore.Qt.LeftButton: + if self.isDraging(): + if not self._releaseOnClick: + self.emit(QtCore.SIGNAL('draged(int, int)'), event.globalX(), event.globalY()) + return True + elif event.button()== QtCore.Qt.RightButton: + if self.isDraging(): + self.endDrag(canceled=True) + return True + + elif event.type() == event.MouseMove: + if self._isDraging: + try: + self.emit( + QtCore.SIGNAL('draging(int, int)'), + event.globalPos().x(), + event.globalPos().y() + ) + except Exception, d: # just in case + self.endDrag(canceled=True) + return True + + return False + +#****************************************************************************** +# +#****************************************************************************** +if __name__ == '__main__': + import sys + + # test tool. Click on the green square to take it up, anpther click + # will release it again + class TestTool(DragTool): + + def __init__(self, parent, px, releaseOnClick=True): + DragTool.__init__(self, parent, px, releaseOnClick=releaseOnClick) + self.connect( + self, + QtCore.SIGNAL('draging(int, int)'), + self.on_draging + ) + self.connect( + self, + QtCore.SIGNAL('draged(int, int)'), + self.on_draged + ) + + def on_draging(self, x, y): + print "moving: %s, %s" % (x, y) + + def on_draged(self, x, y): + print "dragged: %s, %s" % (x, y) + + + + app = QtGui.QApplication(sys.argv) + + w = QtGui.QWidget(None) + w.setGeometry(50, 50, 200, 200) + + # setup pixmap + px = QtGui.QPixmap(QtCore.QSize(32, 32)) + px.fill(QtCore.Qt.green) + + # setup dragg tool and button + dragTool = TestTool( + w, + QtGui.QCursor(px, 0, 0), + #releaseOnClick=False + ) + dragTool.setGeometry(-1, -1, 1, 12) + + bt = QtGui.QToolButton(w) + bt.setIcon(QtGui.QIcon(px)) + w.connect( + bt, + QtCore.SIGNAL('clicked()'), + dragTool.startDrag + ) + + + w.show() + res = app.exec_() + sys.exit(res) + + Added: trunk/fclient/fclient_lib/qt4ex/ctrls/editboxwrap.py =================================================================== --- trunk/fclient/fclient_lib/qt4ex/ctrls/editboxwrap.py (rev 0) +++ trunk/fclient/fclient_lib/qt4ex/ctrls/editboxwrap.py 2007-11-05 12:50:42 UTC (rev 47) @@ -0,0 +1,201 @@ +"""Wrappers and tools for QLineEdit and QTextEdit""" + +from PyQt4 import QtCore, QtGui +#*********************************************************************** +# +#*********************************************************************** +class EditBoxContextMenuWrap(QtCore.QObject): + """Wrapper class for to handle custom context menus for editboxes + + @note: make shure to always keep a reference to the wrapper + """ + + def __init__(self, edit): + """ + @param edit: QTextEdit or QLineEdit + @attr edit: editbox + @attr actions: dict containing default actions for the editbox. Available actions are: + "Copy", "Cut", "Paste", "Redo", "SelectAll", "Undo". Caution: the dict is considered + to be read only! + @signal: 'customizeContextMenu(QObject* EditContextMenuWrap, QMenu* contextMenu)'. + This signal is emitted when the context menu for the editbox is requested. Use this signal + to customize the menu. + @signal: 'contextMenuActionSelected(QAction* action, bool isStandardAction)'. + Emitted when the user selected an action from the context menu. The flag indicates wether + a standard action or a custom action was selected + + """ + QtCore.QObject.__init__(self, edit) + + self.edit = edit + self.actions = {} + + edit.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.connect( + edit, + QtCore.SIGNAL('customContextMenuRequested(const QPoint &)'), + self.__call__ + ) + + def __call__(self, pt): + + # create custom LineEdit context menu + self.actions = {} + m = QtGui.QMenu(self.edit) + + # add default QLineEdit actions + actions = ( + ( + 'Undo', + self.edit.trUtf8("&Undo"), + QtGui.QKeySequence.Undo, + self.trUtf8('Undos the last ation'), + self.edit.undo, + ), + ( + 'Redo', + self.edit.trUtf8("&Redo"), + QtGui.QKeySequence.Redo, + self.trUtf8('Redos the last ation'), + self.edit.redo, + ), + ( + 'Cut', + self.edit.trUtf8("Cu&t"), + QtGui.QKeySequence.Cut, + self.trUtf8('Cuts the currently selected text'), + self.edit.cut, + ), + ( + 'Copy', + self.edit.trUtf8("&Copy"), + QtGui.QKeySequence.Copy, + self.trUtf8('Copies the currently selected text'), + self.edit.copy, + ), + ( + 'Paste', + self.edit.trUtf8("&Paste"), + QtGui.QKeySequence.Paste, + self.trUtf8('Pastes text from the clipboard'), + self.edit.paste, + ), + ( + 'SelectAll', + self.edit.trUtf8("Select All"), + QtGui.QKeySequence.SelectAll, + self.trUtf8('Selects all text'), + self.edit.selectAll, + ), + #TODO: Delete + #d->actions[QLineEditPrivate::ClearAct] = new QAction(QLineEdit::tr("Delete"), this); + #QObject::connect(d->actions[QLineEditPrivate::ClearAct], SIGNAL(triggered()), this, SLOT(_q_deleteSelected())); + ) + + # setup actions + for name, text, shortcut, tip, trigger in actions: + act = QtGui.QAction(text, None) + act.setObjectName(name) + act.setStatusTip(tip) + if shortcut.__class__.__name__ == 'StandardKey': + # BUG: in PyQt4.3 - already reported + # initializing QKeySequence() with a Qt.StandardKey gives segfault + act.setShortcut(shortcut) + else: + act.setShortcut(QtGui.QKeySequence(shortcut) ) + self.connect(act, QtCore.SIGNAL('triggered()'), trigger) + m.addAction(act) + self.actions[name] = act + + # adjust actions + # NOTE: disabling "SelectAll" is somewhat unnecessary, so leave it out + if isinstance(self.edit, QtGui.QLineEdit): + canPaste = not QtGui.QApplication.clipboard().text().isEmpty() + canRedo = self.edit.isRedoAvailable() + canUndo = self.edit.isUndoAvailable() + hasSelection = self.edit.hasSelectedText() + isReadOnly = self.edit.isReadOnly() + else: + canPaste = self.edit.canPaste() + canRedo = self.edit.document().isRedoAvailable() + canUndo = self.edit.document().isUndoAvailable() + hasSelection = self.edit.textCursor().hasSelection() + isReadOnly = self.edit.isReadOnly() + + self.actions['Undo'].setEnabled(canUndo) + self.actions['Redo'].setEnabled(canRedo) + self.actions['Cut'].setEnabled(not isReadOnly and hasSelection) + self.actions['Copy'].setEnabled(hasSelection) + self.actions['Paste'].setEnabled(not isReadOnly and canPaste) + self.actions['SelectAll'].setEnabled(True) + + # TODO: control chars submenu not yet implemented + # one problem is that one can not remove a menu from a menu + #~ ControlCharacters = ( + #~ (QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "LRM Left-to-right mark"), 0x200e), + #~ (QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "RLM Right-to-left mark"), 0x200f), + #~ (QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWJ Zero width joiner"), 0x200d), + #~ (QT_TRANSLATE_NOOP("QUnicodeControlCharacterMenu", "ZWNJ Zero width non-joiner"), 0x200c), + ... [truncated message content] |
From: <jU...@us...> - 2007-11-05 12:43:06
|
Revision: 46 http://fclient.svn.sourceforge.net/fclient/?rev=46&view=rev Author: jUrner Date: 2007-11-05 04:43:07 -0800 (Mon, 05 Nov 2007) Log Message: ----------- error handling Modified Paths: -------------- trunk/fclient/fclient_lib/fcp/fcp2_0.py Modified: trunk/fclient/fclient_lib/fcp/fcp2_0.py =================================================================== --- trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-05 12:42:27 UTC (rev 45) +++ trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-05 12:43:07 UTC (rev 46) @@ -1057,9 +1057,13 @@ def pythonTime(self, fcpTime): """Converts a fcp time value to a python time value @param fcpTime: (int, str) time to convert + @raise ValueError: if the fcpTime could not be converted @return: (int) python time """ - fcpTime = int(fcpTime) + try: + fcpTime = int(fcpTime) + except Exception, d: + raise ValueError(d) return fcpTime / 1000 ######################################################## This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-05 12:42:26
|
Revision: 45 http://fclient.svn.sourceforge.net/fclient/?rev=45&view=rev Author: jUrner Date: 2007-11-05 04:42:27 -0800 (Mon, 05 Nov 2007) Log Message: ----------- added refresh action an some fixes Modified Paths: -------------- trunk/fclient/fclient_widgets/peer_widget.py Modified: trunk/fclient/fclient_widgets/peer_widget.py =================================================================== --- trunk/fclient/fclient_widgets/peer_widget.py 2007-11-04 17:33:33 UTC (rev 44) +++ trunk/fclient/fclient_widgets/peer_widget.py 2007-11-05 12:42:27 UTC (rev 45) @@ -152,16 +152,12 @@ if not observer in event: event += observer - self.clear() - self._peers = {} + ## + self.listPeers() - self._cfg.fcpClientManager.setPriority( - self._fcpClient.connectionName(), - pollPriority=self._cfg.fcpClientManager.priorities['Highest'] - ) - self._fcpClient.listPeers() - + + def handleClientDisconnected(self, event, params): """ """ @@ -175,6 +171,7 @@ identity = params['identity'] item = self._peers.get(identity, None) + # add item if necessary if item is None: itemStrings = [ @@ -191,6 +188,13 @@ item.settext(self.HeaderIndexLastConnected, timeLastConnected) item.setPeerData(params) + # hide item if necessary + flags = self.showPeers() + if params['opennet'] == self._fcpClient.FcpTrue: + item.setHidden(not flags & self.ShowPeersOpennet) + else: + item.setHidden(not flags & self.ShowPeersDarknet) + # get peer notes if possible if params['opennet'] == self._fcpClient.FcpFalse: self._requestsPending += 1 @@ -267,14 +271,26 @@ """ result = '' try: - t = self._cfg.fcpClient.pythonTime(t) - except: pass + t = self._fcpClient.pythonTime(t) + except ValueError: pass else: if t > 0: result = numbers.format_time_delta(t, time.time()) return result + def listPeers(self): + self.clear() + self._peers = {} + + self._cfg.fcpClientManager.setPriority( + self._fcpClient.connectionName(), + pollPriority=self._cfg.fcpClientManager.priorities['Highest'] + ) + self._fcpClient.listPeers() + + + def peers(self): """Returns all currently known peers @return: (dict) identity --> treeItem @@ -290,6 +306,10 @@ actions.append(ShowPeersMenu(menu, self)) + act = RefreshAction('Refresh', self.trUtf8('Refresh'), menu, self) + menu.addAction(act) + actions.append(act) + return actions @@ -308,7 +328,7 @@ self.setUpdatesEnabled(False) for item in self.peers().values(): - if item.peerData()['opennet'] == self._cfg.fcpClient.FcpTrue: + if item.peerData()['opennet'] == self._fcpClient.FcpTrue: item.setHidden(not flags & self.ShowPeersOpennet) else: item.setHidden(not flags & self.ShowPeersDarknet) @@ -434,6 +454,35 @@ +class RefreshAction(QtGui.QAction): + """ + """ + + def __init__(self, name, text, parent, tree): + """ + """ + QtGui.QAction.__init__(self, text, parent) + self.setObjectName(name) + + #self.setShortcuts( + # [QtGui.QKeySequence(self.trUtf8('Ctrl+A'))] + # ) + self.tree = tree + self.connect( + self, + QtCore.SIGNAL('triggered()'), + self.__call__ + ) + + + def __call__(self): + """ + """ + self.tree.listPeers() + + + + '''######################################################## Sample Peer message >> Peer This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-04 17:33:30
|
Revision: 44 http://fclient.svn.sourceforge.net/fclient/?rev=44&view=rev Author: jUrner Date: 2007-11-04 09:33:33 -0800 (Sun, 04 Nov 2007) Log Message: ----------- bit of renaming Modified Paths: -------------- trunk/fclient/fclient_ui/fcp_client_manager.py trunk/fclient/fclient_widgets/peer_widget.py Modified: trunk/fclient/fclient_ui/fcp_client_manager.py =================================================================== --- trunk/fclient/fclient_ui/fcp_client_manager.py 2007-11-04 17:31:30 UTC (rev 43) +++ trunk/fclient/fclient_ui/fcp_client_manager.py 2007-11-04 17:33:33 UTC (rev 44) @@ -109,7 +109,7 @@ self.priorities = Priorities(self) - def stopFcpClient(self, name): #TODO: name of client or client??? + def stopClient(self, name): #TODO: name of client or client??? """Stops a FcpClient @name: (str) connection name of the client to stop @note: the client is not closed in the call. To close the connection call FcpClient.close() @@ -118,7 +118,7 @@ handler.stop() - def newFcpClient(self, + def newClient(self, name, eventConnectedHandler, connectPriority=None, Modified: trunk/fclient/fclient_widgets/peer_widget.py =================================================================== --- trunk/fclient/fclient_widgets/peer_widget.py 2007-11-04 17:31:30 UTC (rev 43) +++ trunk/fclient/fclient_widgets/peer_widget.py 2007-11-04 17:33:33 UTC (rev 44) @@ -112,7 +112,7 @@ """ if not self._isCreated: self._isCreated = True - self._fcpClient = self._cfg.fcpClientManager.newFcpClient('MeName', self.handleClientConnected) + self._fcpClient = self._cfg.fcpClientManager.newClient('MeName', self.handleClientConnected) ############################################################# ## This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-04 17:31:25
|
Revision: 43 http://fclient.svn.sourceforge.net/fclient/?rev=43&view=rev Author: jUrner Date: 2007-11-04 09:31:30 -0800 (Sun, 04 Nov 2007) Log Message: ----------- some more consts Modified Paths: -------------- trunk/fclient/fclient_lib/fcp/fcp2_0.py Modified: trunk/fclient/fclient_lib/fcp/fcp2_0.py =================================================================== --- trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-04 17:29:50 UTC (rev 42) +++ trunk/fclient/fclient_lib/fcp/fcp2_0.py 2007-11-04 17:31:30 UTC (rev 43) @@ -121,10 +121,20 @@ Version = '2.0' FcpTrue = 'true' FcpFalse = 'false' + class ConnectReason: + Connect = '1' + Reconnect = '2' + + class DisconnectReason: - """Reason for client disconnect""" + """Reasons for client disconnect + @cvar Shutdown: regular shutdown of the connection + @cvar SocketDied: connection to the node died unexpectingly + @cvar ConnectFailed: connection could not be established + """ Shutdown = '1' SocketDied = '2' + ConnectFailed = '3' class Events(events.Events): @@ -757,10 +767,16 @@ timeElapsed += timeout time.sleep(timeout) + sef.events.ClientDisconnect({'DisconnectReason': DisconnectReason.ConectFailed}) self._log.info(self.LogMessages.ConnectingFailed) raise StopIteration + + + def connectionName(self): + """Returns the connection name the client uses""" + return self._connectionName - + def handleMessage(self, msg): """Handles a message from the freenet node @param msg: (Message) to handle This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-04 17:29:46
|
Revision: 42 http://fclient.svn.sourceforge.net/fclient/?rev=42&view=rev Author: jUrner Date: 2007-11-04 09:29:50 -0800 (Sun, 04 Nov 2007) Log Message: ----------- new: FcPClient manager Modified Paths: -------------- trunk/fclient/fclient_widgets/peer_widget.py Modified: trunk/fclient/fclient_widgets/peer_widget.py =================================================================== --- trunk/fclient/fclient_widgets/peer_widget.py 2007-11-04 17:29:34 UTC (rev 41) +++ trunk/fclient/fclient_widgets/peer_widget.py 2007-11-04 17:29:50 UTC (rev 42) @@ -1,8 +1,4 @@ """Sketch for a widget handling peer nodes - -Note: super slow currently. Have to experiment with multiple clients -to enable playing around with poll timeouts for individual clients - """ import os, sys @@ -54,6 +50,8 @@ #*************************************************************************************************** # #*************************************************************************************************** +#TODO: user should not be alowed to show / hide peers while we are listing them + class PeerWidget(QtGui.QTreeWidget): """ """ @@ -76,6 +74,7 @@ self._fcpEvents = None self._isCreated = False self._peers = {} # identity --> item + self._requestsPending = 0 self._showPeersFlags = self.ShowPeersAll QtGui.QWidget.__init__(self, parent) @@ -113,7 +112,7 @@ """ if not self._isCreated: self._isCreated = True - self._cfg.connectFcpNode(self.handleClientConnected) + self._fcpClient = self._cfg.fcpClientManager.newFcpClient('MeName', self.handleClientConnected) ############################################################# ## @@ -137,18 +136,17 @@ """ """ self._fcpEvents = ( - (self._cfg.fcpClient.events.ClientDisconnected, self.handleClientDisconnected), + (self._fcpClient.events.ClientDisconnected, self.handleClientDisconnected), - (self._cfg.fcpClient.events.Peer, self.handlePeer), - (self._cfg.fcpClient.events.EndListPeers, self.handleEndListPeers), - (self._cfg.fcpClient.events.PeerRemoved, self.handlePeerRemoved), - (self._cfg.fcpClient.events.UnknownNodeIdentifier, self.handleUnknownNodeIdentifier), + (self._fcpClient.events.Peer, self.handlePeer), + (self._fcpClient.events.EndListPeers, self.handleEndListPeers), + (self._fcpClient.events.PeerRemoved, self.handlePeerRemoved), + (self._fcpClient.events.UnknownNodeIdentifier, self.handleUnknownNodeIdentifier), - (self._cfg.fcpClient.events.PeerNote, self.handlePeerNote), - (self._cfg.fcpClient.events.EndListPeerNotes, self.handleEndListPeerNotes), + (self._fcpClient.events.PeerNote, self.handlePeerNote), + (self._fcpClient.events.EndListPeerNotes, self.handleEndListPeerNotes), ) - # take care to not connect twice for event, observer in self._fcpEvents: if not observer in event: @@ -156,8 +154,13 @@ self.clear() self._peers = {} - self._cfg.fcpClient.listPeers() + self._cfg.fcpClientManager.setPriority( + self._fcpClient.connectionName(), + pollPriority=self._cfg.fcpClientManager.priorities['Highest'] + ) + self._fcpClient.listPeers() + def handleClientDisconnected(self, event, params): """ @@ -189,14 +192,20 @@ item.setPeerData(params) # get peer notes if possible - if params['opennet'] == self._cfg.fcpClient.FcpFalse: - self._cfg.fcpClient.listPeerNotes(identity) - - + if params['opennet'] == self._fcpClient.FcpFalse: + self._requestsPending += 1 + self._fcpClient.listPeerNotes(identity) + def handleEndListPeers(self, event, params): """ """ + if self._requestsPending <= 0: + self._cfg.fcpClientManager.setPriority( + self._fcpClient.connectionName(), + pollPriority=self._cfg.fcpClientManager.priorities['Lowest'] + ) + def handlePeerRemoved(self, peer): @@ -218,15 +227,22 @@ #TODO: shouldn' t happen return - if params['PeerNoteType'] == self._cfg.fcpClient.PeerNoteType.Private: + if params['PeerNoteType'] == self._fcpClient.PeerNoteType.Private: item.setText(self.HeaderIndexNotes, params['NoteText']) def handleEndListPeerNotes(self, event, params): """ """ + self._requestsPending -= 1 + if self._requestsPending <= 0: + self._requestsPending = 0 + self._cfg.fcpClientManager.setPriority( + self._fcpClient.connectionName(), + pollPriority=self._cfg.fcpClientManager.priorities['Lowest'] + ) + - ####################################################### ## ## methods @@ -240,6 +256,8 @@ for event, observer in self._fcpEvents: if not observer in event: event -= observer + + self._cfg.fcpClientHandler.closeClient(self._fcpClient.connectionName()) def formatTimeDelta(self, t): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-04 17:29:34
|
Revision: 41 http://fclient.svn.sourceforge.net/fclient/?rev=41&view=rev Author: jUrner Date: 2007-11-04 09:29:34 -0800 (Sun, 04 Nov 2007) Log Message: ----------- new: FcPClient manager Modified Paths: -------------- trunk/fclient/config.py Modified: trunk/fclient/config.py =================================================================== --- trunk/fclient/config.py 2007-11-04 17:28:46 UTC (rev 40) +++ trunk/fclient/config.py 2007-11-04 17:29:34 UTC (rev 41) @@ -7,7 +7,9 @@ from PyQt4 import QtCore from fclient_lib.qt4ex import assistant, language, settingsbase, resources +from fclient_ui.fcp_client_manager import FcpClientManager + _ = os.path.join #*********************************************************************** # @@ -34,7 +36,7 @@ #***************************************************************************** class Config(object): - def __init__(self, parent, connectDelay=100, pollDelay=300): + def __init__(self, parent): """ @param parent: parent widget @param connectDelay: (miliseconds) frequency by qich a connection to the node is made @@ -42,95 +44,32 @@ value will make messages from the node come in quickly but make the gui unresponsive to user interaction - @ivar assistant: app global user docs management + @ivar assistant: app global user documentation management @ivar defaultDownloadsDir: default directory for downloads - @ivar fcpClient: app global FcpClient + @ivar fcpClientManager: app global FcpClientManager @ivar language: app global language management @ivar nodeHello: NodeHello message as returned from the client as soon as a connection is established @ivar resources: app global resource management - @ivar settings: global settings + @ivar settings: app global settings """ #TODO: assistant can not handle locale dependend docs yet self.assistant = assistant.Assistant(parent, profile=AssistantProfile, pages=DocPages) self.defaultDownloadsDir = os.path.join(BaseDir, 'downloads') - + self.fcpClientManager = FcpClientManager(parent) self.language = language.Language([LanguageDir, ], translationPrefixes=TranslationPrefixes) - self.resources = resources.Resources([ResourceDir, ], ) self.settings = settingsbase.Settings(OrgName, AppName) - self.fcpClient = None - self.nodeHello = None - self.connectDelay = connectDelay - self.pollDelay = pollDelay - self._pollTimer = QtCore.QTimer() - self._connectMethod = None - self._connectTimer = QtCore.QTimer() - QtCore.QObject.connect( - self._connectTimer, - QtCore.SIGNAL('timeout()'), - self.handleFcpConnect - ) - - #TODO: have to find a way to handle multiple clients - def connectFcpNode(self, eventConnectedHandler): - """Establishes a connection to the freenet node - @param eventConnectedHandler: handler to handle the events.ClientConnected of the client - @note: the clients events.ClientConnected gets automatically connected in the call. So no - need to connect once more. - @note: make shure not to access the 'fcpClient' or 'nodeHello' attrs of the config before - the handler gets called. - """ - if self.fcpClient is None: - # some magic here to determine the client version to use - from fclient_lib.fcp.fcp2_0 import FcpClient - self.fcpClient = FcpClient( - connectionName='', - #verbosity=FcpClient.Verbosity.Debug, - ) - self.fcpClient.events.ClientDisconnected += self.handleFcpClientDisconnected - QtCore.QObject.connect( - self._pollTimer, - QtCore.SIGNAL('timeout()'), - self.fcpClient.next - ) - self._connectMethod = self.fcpClient.connect() - self.fcpClient.events.ClientConnected += eventConnectedHandler - self._connectTimer.start(self.connectDelay) - - else: - cb(self.fcpClient.events.ClientConnected, self.nodeHello) - def handleFcpConnect(self): - """Handles establishing of the node connection""" - self._connectTimer.stop() - try: - result = self._connectMethod.next() - if result is not None: - self.nodeHello = result - self._pollTimer.start(self.pollDelay) - - except StopIteration: - return - self._connectTimer.start() - - - def handleFcpClientDisconnected(self, params): - """Handles client disconneting""" - self.fcpClient = None - self.nodeHello = None - - - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-04 17:28:44
|
Revision: 40 http://fclient.svn.sourceforge.net/fclient/?rev=40&view=rev Author: jUrner Date: 2007-11-04 09:28:46 -0800 (Sun, 04 Nov 2007) Log Message: ----------- added support to handle multiple clients Added Paths: ----------- trunk/fclient/fclient_ui/ trunk/fclient/fclient_ui/__init__.py trunk/fclient/fclient_ui/fcp_client_manager.py Added: trunk/fclient/fclient_ui/__init__.py =================================================================== --- trunk/fclient/fclient_ui/__init__.py (rev 0) +++ trunk/fclient/fclient_ui/__init__.py 2007-11-04 17:28:46 UTC (rev 40) @@ -0,0 +1 @@ + Added: trunk/fclient/fclient_ui/fcp_client_manager.py =================================================================== --- trunk/fclient/fclient_ui/fcp_client_manager.py (rev 0) +++ trunk/fclient/fclient_ui/fcp_client_manager.py 2007-11-04 17:28:46 UTC (rev 40) @@ -0,0 +1,299 @@ + + +from PyQt4 import QtCore +#************************************************************************************** +# +#************************************************************************************** +class Priority(object): + """Priority""" + __slots__ = ('name', 'value') + + + def __init__(self, name, value): + """ + @param name: (str) name of the priority + @param value: (int) value of the priority + """ + self.name = name + self.value = value + def __int__(self): return self.value + def __long__(self): return self.__int__() + +#************************************************************************************** +# +#************************************************************************************** +class Priorities(QtCore.QObject): + """Class handling poll priorities + + >>> p = Priorities(None) + >>> priority = p['Highest'] + >>> priority.name + 'Highest' + >>> int(priority) + 0 + + >>> p['Highest'] = 33 + >>> int(priority) + 33 + + >>> for translatedName, priority in p.listPriorityNames(): priority.name + 'Highest' + 'High' + 'Normal' + 'Low' + 'Lowest' + """ + + # cheat a bit to get dynamic translation working + def trUtf8(self, what): return what + + + def __init__(self, parent): + """ + @param parent: (QObject) parent or None + @note: supported priorities are: 'Highest', 'High', 'Normal', 'Low', 'Lowest' + """ + QtCore.QObject.__init__(self, parent) + + self._values = ( + Priority(self.trUtf8('Highest'), 0), + Priority(self.trUtf8('High'), 100), + Priority(self.trUtf8('Normal'), 300), + Priority(self.trUtf8('Low'), 600), + Priority(self.trUtf8('Lowest'), 1000), + ) + self._valuesDict = {} + for priority in self._values: + self._valuesDict[priority.name] = priority + + + def __getitem__(self, name): + """Returns the a priority given its name""" + return self._valuesDict[name] + + + def __setitem__(self, name, value): + """Sets the value of a priority + @param name: (str) name of the priority + @param value: (int) value in milisecomds + """ + self._valuesDict[name].value = value + + + def listPriorityNames(self): + """Returns a list containing all priorities + @return: (list) [(QString) translatedName, (Priority) priority] + """ + out = [] + for priority in self._values: + out.append( (QtCore.QObject.trUtf8(self, priority.name), priority) ) + return out + +#***************************************************************************** +# +#***************************************************************************** +class FcpClientManager(QtCore.QObject): + """Manager to handle multiple Fcp clients + """ + + + def __init__(self, parent): + """ + @param parent: (QObject) parent or None + @ivar fcpClients: dict mapping connection names to FcpClients + @ivar priorities: L{Priorities} the manager supports + """ + QtCore.QObject.__init__(self, parent) + + self.fcpClients = {} + self.priorities = Priorities(self) + + + def stopFcpClient(self, name): #TODO: name of client or client??? + """Stops a FcpClient + @name: (str) connection name of the client to stop + @note: the client is not closed in the call. To close the connection call FcpClient.close() + """ + handler = self.fcpClients.pop(name) + handler.stop() + + + def newFcpClient(self, + name, + eventConnectedHandler, + connectPriority=None, + pollPriority=None, + ): + """Creates a new Fcp client + @param name: (str) unique connection name + @param eventConnectedHandler: method that handles FcpClient.EventConnected + @param connectPriority: (L{Priority}) priority when establishing node connection + @param pollPriority: (L{Priority}) runtime priority + @return: (FcpClient) instance + + @note: the eventConnectedHandler handler passed is automatically connected + to FcpClient.EventConnected. No need to connect to receive events. + """ + if name in self.fcpClients: + raise ValueError('Fcp client already exists: %r' % name) + + # some magic here to determine the client version to use + from fclient_lib.fcp.fcp2_0 import FcpClient + fcpClient = FcpClient( + connectionName=name, + #verbosity=FcpClient.Verbosity.Debug, + ) + fcpClient.events.ClientConnected += eventConnectedHandler + handler = FcpClientHandler( + fcpClient, + self.handleFcpClientDisconnected, + connectPriority if connectPriority is not None else self.priorities['Normal'], + pollPriority if pollPriority is not None else self.priorities['Normal'], + ) + self.fcpClients[name] = handler + fcpClient.events.ClientDisconnected += handler.handleFcpClientDisconnected + handler.connect() + return fcpClient + + + def setPriority(self, name, connectPriority=None, pollPriority=None): + """Sets priorities of a FcpClient + @param connectPriority: (L{Priority}) priority when establishing node connection + @param pollPriority: (L{Priority}) runtime priority + """ + handler = self.fcpClients[name] + if connectPriority is not None: + handler.setConnectPriority(connectPriority) + if pollPriority is not None: + handler.setPollPriority(pollPriority) + + + def handleFcpClientDisconnected(self, handler, params): + """Handles FcpClient.EventDisconnect""" + + disconnectReason = params['DisconnectReason'] + fcpClient = handler.fcpClient + + # see how to handle the cases + if disconnectReason == fcpClient.DisconnectReason.Shutdown: + handler.stop() + + elif disconnectReason == fcpClient.DisconnectReason.SocketDied: + # 1. wait some time, cos more alerts may come in + # 2. inform user + # 3. reconnect if desired + pass + + elif disconnectReason == fcpClient.DisconnectReason.ConnectFailed: + # 1. wait some time, cos more alerts may come in + # 2. inform user + # 3. reconnect if desired + pass + + else: + raise ValueError('Unknown disconnect reason: %r' % disconnectReason) + + + +#************************************************************************************** +# +#************************************************************************************** +class FcpClientHandler(object): + """Handles one single FcpClient + """ + + + def __init__(self, fcpClient, handleFcpClientDisconnected, connectPriority, pollPriority): + """ + @param fcpClient: FcpClient to handle + @param handleFcpClientDisconnected: handler to call when the client disconnects + @param connectPriority: (L{Priority}) priority when establishing node connection + @param pollPriority: (L{Priority}) runtime priority + """ + self.fcpClient = fcpClient + self.handleFcpClientDisconnected = handleFcpClientDisconnected + self.connectMethod = fcpClient.connect() + + self.connectPriority = connectPriority + self.pollPriority = pollPriority + + self.connectTimer = QtCore.QTimer() + QtCore.QObject.connect( + self.connectTimer, + QtCore.SIGNAL('timeout()'), + self.handleConnect + ) + + self.pollTimer = QtCore.QTimer() + QtCore.QObject.connect( + self.pollTimer, + QtCore.SIGNAL('timeout()'), + self.fcpClient.next + ) + + + def connect(self): + """Establishes the connection from the client to the node""" + self.stop() + self.connectTimer.start(int(self.connectPriority)) + + + def start(self): + """Starts polling the FcpClient""" + self.pollTimer.start(int(self.pollPriority)) + + + def stop(self): + """Stops polling the FcpClient""" + self.connectTimer.stop() + self.pollTimer.stop() + + + def setPollPriority(self, priority): + """Adjusts the poll priority of the FcpClient + @param priority: L{Priority} + """ + isActive = self.pollTimer.isActive() + self.pollTimer.stop() + if isActive: + self.pollTimer.start(int(priority)) + self.pollPriority = priority + + + def setConnectPriority(self, priority): + """Adjusts the priority wich is used to establish a connection to the node + @param priority: L{Priority} + """ + isActive = self.connectTimer.isActive() + self.connectTimer.stop() + if isActive: + self.connectTimer.start(int(priority)) + self.connectPriority = priority + + + def handleFcpClientDisconnected(self, params): + """Handles FcpClient.EventDisconnect""" + self.stop() + self.handleFcpClientDisconnected(self, params) + + + def handleConnect(self): + """Handles establishing of the node connection""" + self.connectTimer.stop() + try: + result = self.connectMethod.next() + if result is not None: + self.nodeHello = result + self.pollTimer.start(int(self.pollPriority)) + return + except StopIteration: + return + self.connectTimer.start(int(self.connectPriority)) + +#****************************************************************************************** +# +#****************************************************************************************** +if __name__ == '__main__': + import doctest + doctest.testmod() + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-02 16:02:47
|
Revision: 39 http://fclient.svn.sourceforge.net/fclient/?rev=39&view=rev Author: jUrner Date: 2007-11-02 09:02:52 -0700 (Fri, 02 Nov 2007) Log Message: ----------- notes Modified Paths: -------------- trunk/fclient/config.py Modified: trunk/fclient/config.py =================================================================== --- trunk/fclient/config.py 2007-11-02 16:02:28 UTC (rev 38) +++ trunk/fclient/config.py 2007-11-02 16:02:52 UTC (rev 39) @@ -80,6 +80,7 @@ ) + #TODO: have to find a way to handle multiple clients def connectFcpNode(self, eventConnectedHandler): """Establishes a connection to the freenet node @param eventConnectedHandler: handler to handle the events.ClientConnected of the client This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jU...@us...> - 2007-11-02 16:02:27
|
Revision: 38 http://fclient.svn.sourceforge.net/fclient/?rev=38&view=rev Author: jUrner Date: 2007-11-02 09:02:28 -0700 (Fri, 02 Nov 2007) Log Message: ----------- added peer notes handling Modified Paths: -------------- trunk/fclient/fclient_widgets/peer_widget.py Modified: trunk/fclient/fclient_widgets/peer_widget.py =================================================================== --- trunk/fclient/fclient_widgets/peer_widget.py 2007-11-02 16:01:29 UTC (rev 37) +++ trunk/fclient/fclient_widgets/peer_widget.py 2007-11-02 16:02:28 UTC (rev 38) @@ -1,5 +1,8 @@ """Sketch for a widget handling peer nodes +Note: super slow currently. Have to experiment with multiple clients +to enable playing around with poll timeouts for individual clients + """ import os, sys @@ -14,7 +17,6 @@ import config -from fclient_lib import fcp from fclient_lib.pyex import numbers @@ -28,6 +30,30 @@ #*************************************************************************************************** # #*************************************************************************************************** +class PeerItem(QtGui.QTreeWidgetItem): + """ + """ + + def __init__(self, *args): + """ + """ + QtGui.QTreeWidgetItem.__init__(self, *args) + + self._peerData = None + + def peerData(self): + """ + """ + return self._peerData + + def setPeerData(self, peerData): + """ + """ + self._peerData = peerData + +#*************************************************************************************************** +# +#*************************************************************************************************** class PeerWidget(QtGui.QTreeWidget): """ """ @@ -117,6 +143,10 @@ (self._cfg.fcpClient.events.EndListPeers, self.handleEndListPeers), (self._cfg.fcpClient.events.PeerRemoved, self.handlePeerRemoved), (self._cfg.fcpClient.events.UnknownNodeIdentifier, self.handleUnknownNodeIdentifier), + + (self._cfg.fcpClient.events.PeerNote, self.handlePeerNote), + (self._cfg.fcpClient.events.EndListPeerNotes, self.handleEndListPeerNotes), + ) # take care to not connect twice @@ -150,20 +180,24 @@ timeLastConnected, '' ] - item = QtGui.QTreeWidgetItem(self, itemStrings) - item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(identity)) - self._peers[identity] = (item, params) + item = PeerItem(self, itemStrings) + self._peers[identity] = item # update item else: item.settext(self.HeaderIndexLastConnected, timeLastConnected) - self._peers[identity] = (item, params) + item.setPeerData(params) + + # get peer notes if possible + if params['opennet'] == self._cfg.fcpClient.FcpFalse: + self._cfg.fcpClient.listPeerNotes(identity) + def handleEndListPeers(self, event, params): """ """ - + def handlePeerRemoved(self, peer): """ @@ -174,6 +208,25 @@ """ """ + + def handlePeerNote(self, event, params): + """ + """ + identity = params['NodeIdentifier'] + item = self._peers.get(identity, None) + if item is None: + #TODO: shouldn' t happen + return + + if params['PeerNoteType'] == self._cfg.fcpClient.PeerNoteType.Private: + item.setText(self.HeaderIndexNotes, params['NoteText']) + + + def handleEndListPeerNotes(self, event, params): + """ + """ + + ####################################################### ## ## methods @@ -204,18 +257,9 @@ return result - def identityFromItem(self, item): - """Returns the peer identity an item is associated to - @param item: treeItem - @return: (str) peer identity - """ - v = item.data(0, QtCore.Qt.UserRole) - return str(v.toString()) - - def peers(self): """Returns all currently known peers - @return: (dict) identity --> (treeItem, peer) + @return: (dict) identity --> treeItem """ return self._peers @@ -245,8 +289,8 @@ """ self.setUpdatesEnabled(False) - for item, peer in self.peers().values(): - if peer['opennet'] == self._cfg.fcpClient.FcpTrue: + for item in self.peers().values(): + if item.peerData()['opennet'] == self._cfg.fcpClient.FcpTrue: item.setHidden(not flags & self.ShowPeersOpennet) else: item.setHidden(not flags & self.ShowPeersDarknet) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |