SF.net SVN: fclient:[916] trunk/fclient/fclient/impl
Status: Pre-Alpha
Brought to you by:
jurner
|
From: <jU...@us...> - 2008-08-16 08:13:49
|
Revision: 916
http://fclient.svn.sourceforge.net/fclient/?rev=916&view=rev
Author: jUrner
Date: 2008-08-16 08:13:58 +0000 (Sat, 16 Aug 2008)
Log Message:
-----------
fixes for downloads widget
Modified Paths:
--------------
trunk/fclient/fclient/impl/ViewDownloads/ViewDownloads.py
Removed Paths:
-------------
trunk/fclient/fclient/impl/BaseRequestsWidget.py
Deleted: trunk/fclient/fclient/impl/BaseRequestsWidget.py
===================================================================
--- trunk/fclient/fclient/impl/BaseRequestsWidget.py 2008-08-16 08:10:49 UTC (rev 915)
+++ trunk/fclient/fclient/impl/BaseRequestsWidget.py 2008-08-16 08:13:58 UTC (rev 916)
@@ -1,554 +0,0 @@
-
-#**************************************************************************************************************
-#TODO:
-#
-# x. prtty tricky to get dls right when the node or client may disconnect unexpectedly
-# problem: dls that we send and that have not reached the node
-# problem: what to do when the user wants to quit and we still have dls to push to the node
-#
-# solution would require keeping track of all requests
-# a. keep identifiers of requests that reached the node (have to do it anyways)
-# b. keep track of requests ahead of sending them to the node (feels not too good doubeling downloads.dat.gz)
-#
-# best idea seems to be to ignore the problem
-# 1. wait till (if ever) freenet devels fdrop node keeping track of client requests
-#
-# 2. a box thrown to the user (x. do not show this message again) to inform him about pendings
-# should be enough. maybe along with a separate widget or some separate color code for pendings
-#
-# x. performance
-# start with a standard model and improve it over time. no need to set any hard
-# limits. the user will find out by himself how many dls his machine can handle.
-# have to be carefrul though when adding dls, like from a *.frdx to provide appropriate
-# warnings
-# x. it may take a while untill the final DataFound message arrives when a request is % completed. feedback would be nice
-# x. DataFound for a request the file has been removed by the user. no idea what happens. have to test this
-# x. when the node is about to start up, looks like persistents may arrive or not. check
-# x. how to get early information about mimetype/size? maybe use FcpClient.getFileInfo()
-# x. show/hide header izems
-# x. sort by header
-# x. indicate over all time / dl speed
-# x. indicate status / remove items by status
-# x. item properties
-# x. how to handle inserting huge number of dls?
-# idea: insert with lowest priority to get the node to know them, increase priority when a slot in
-# MaxSimultaneousDls (if set) is free. atatch progressBar no sooner as priority > MinDlPriority
-# x. how to handle huge numbers of dls. the node will flood us on startup with persistents. looks
-# like the only way to control the flood is to have one connection/dl. maybe wait for freenet devels
-# to realize that this is a serious problem...
-# x. byte amount postfixes must be transllated ++ use Kib or Kb or let the user decide?
-# x. sometimes groups of dls get not removed
-#**************************************************************************************************************
-from __future__ import absolute_import
-if __name__ == '__main__': # see --> http://bugs.python.org/issue1510172 . works only current dir and below
- import os; __path__ = [os.path.dirname(__file__)]
-
-import mimetypes
-import os
-from PyQt4 import QtCore, QtGui
-
-from . import config
-from .lib import fcp2
-from .lib.fcp2.lib import pmstruct
-from .lib.qt4ex import treewidgetwrap
-from .lib import numbers
-
-from . import DlgDownloadKeyToDisk
-
-from .tpls.Ui_ViewRequestsWidgetTpl import Ui_ViewRequestsWidget
-#**********************************************************************************
-#
-#**********************************************************************************
-BLOCK_SIZE = 32768 # from CHKBlock.java
-
-#**********************************************************************************
-#
-#**********************************************************************************
-class PersistentRequestData(pmstruct.PMStruct):
- _fields_ = (
- ('ClientName', pmstruct.STRING),
- )
-
-#**********************************************************************************
-#
-#**********************************************************************************
-class TreeItem(QtGui.QTreeWidgetItem):
-
- IndexName = 0
- IndexSize = 1
- IndexMimeType = 2
- IndexStatus = 3
- IndexProgress = 4
- IndexPriority = 5
- IndexElapsed = 6
-
- ProgressBarName = 'downloadKey'
-
- StatusPending = 'pending'
- StatusLoading = 'loading'
- StatusComplete = 'complete'
- StatusError = 'error'
- StatusRemoved = 'removed'
- StatusCompressing = 'compressing'
- ##StatusCompressed = 'compressed' #TODO: no way to distinguish compressed an loading
-
- def __init__(self, fcpRequest, *params):
- QtGui.QTreeWidgetItem.__init__(self, *params)
- self.fcpRequest = fcpRequest
- self.fcOldStatus = self.StatusPending
-
- def status(self):
- if self.fcpRequest is None:
- return self.StatusRemoved
- elif self.fcpRequest['RequestStatus'] & fcp2.ConstRequestStatus.Success:
- return self.StatusComplete
- elif self.fcpRequest['RequestStatus'] & fcp2.ConstRequestStatus.Error:
- return self.StatusError
-
- #TODO: more or less aguess ..have to check this in detail
- elif self.fcpRequest['RequestStatus'] & fcp2.ConstRequestStatus.Started:
- if self.fcpRequest['RequestStatus'] & fcp2.ConstRequestStatus.Compressing:
- if self.fcpRequest['RequestStatus'] & fcp2.ConstRequestStatus.Compressed:
- return self.StatusLoading
- else:
- return self.compressing
- else:
- return self.StatusLoading
- else:
- return self.StatusPending
-
-# exposes status property for stylesheets
-class ProgressBar(QtGui.QProgressBar):
-
- def __init__(self, parent, item):
- QtGui.QProgressBar.__init__(self, parent)
- self.item = item
-
- def _get_status(self):
- return self.item.status()
- status= QtCore.pyqtProperty("QString", _get_status)
-
-
-class RequestsWidgetActions(config.ActionsBase):
-
- def __init__(self, parent):
- config.ActionsBase.__init__(self, parent)
-
- self.action(
- name='ActionDownloadKeyToDisk',
- text=self.trUtf8('Download &key...'),
- trigger=parent.onDlgDownloadKey,
- )
- self.action(
- name='ActionRemoveSelectedDownloads',
- text=self.trUtf8('Remove download'),
- trigger=parent.onRemoveSelectedDownloads,
- isEnabled=False,
- )
- self.action(
- name='ActionRestartSelectedDownloads',
- text=self.trUtf8('Restart download'),
- trigger=parent.onRestartSelectedDownloads,
- isEnabled=False,
- )
-
- #TODO: enable/disable if items of that type are available?
- group = self.group(
- name='GroupRemoveGroup',
- trigger=parent.onRemoveGroup,
- )
- self.action(
- name='ActionRemoveFailed',
- group=group,
- text=self.trUtf8('Failed'),
- isEnabled=False,
- )
- self.action(
- name='ActionRemoveCompleted',
- group=group,
- text=self.trUtf8('Completed'),
- isEnabled=False,
- )
-
-#**********************************************************************************
-#
-#**********************************************************************************
-class RequestsWidget(QtGui.QWidget, Ui_ViewRequestsWidget):
-
- IdTree = 'tree'
-
- def __init__(self, parent, idGlobalFeedback=config.IdMainWindow):
- QtGui.QWidget.__init__(self, parent)
- self._isCreated = False
- self.fcHeaderLabels = {} # fcpIdentifier --> treeItem
- self.fcActions = RequestsWidgetActions(self)
- self.fcpRequests = {}
- self.fcRequestStatusNames = {}
- self.menuRemoveGroup = QtGui.QMenu(self)
-
- self.setupUi(self)
- self.fcpClientEvents = (
- (config.fcpClient.events.ClientConnected, self.onFcpClientConnected),
- (config.fcpClient.events.ClientDisconnected, self.onFcpClientDisconnected),
- (config.fcpClient.events.ConfigData, self.onFcpConfigData),
- (config.fcpClient.events.RequestCompleted, self.onFcpClientRequestCompleted),
- (config.fcpClient.events.RequestFailed, self.onFcpClientRequestFailed),
- (config.fcpClient.events.RequestModified, self.onFcpClientRequestModified),
- (config.fcpClient.events.RequestProgress, self.onFcpClientRequestProgress),
- (config.fcpClient.events.RequestStarted, self.onFcpClientRequestStarted),
- (config.fcpClient.events.RequestRemoved, self.onFcpClientRequestRemoved),
- (config.fcpClient.events.RequestCompressionStarted, self.onFcpClientRequestCompressionStarted),
- (config.fcpClient.events.RequestCompressionCompleted, self.onFcpClientRequestCompressionCompleted),
- )
- config.fcpClient.events += self.fcpClientEvents
-
-
- ############################
- ## private methods
- ############################
- def _adjustItemStatus(self, item):
- # to take Css styling into account we have to set a new statusBar for each state change
- tree = self.controlById(self.IdTree)
- oldProgressBar = progressBar = self.tree.itemWidget(item, TreeItem.IndexProgress)
- itemStatus = item.status()
- itemStatusChanged = itemStatus != item.fcOldStatus
- if itemStatusChanged:
- progressBar = ProgressBar(self.tree, item)
-
- # adjust statusBar and set a new one if necessary
- # ..bit much work here, but necessary, cos Fcp might come up with
- # ..a completed message without any prior progress notifications
- if itemStatus == TreeItem.StatusPending:
- progressBar.setRange(0, 0)
- elif itemStatus == TreeItem.StatusLoading:
- progressBar.setRange(0, item.fcpRequest['ProgressRequired'])
- progressBar.setValue(item.fcpRequest['ProgressSucceeded'])
- elif itemStatus == TreeItem.StatusComplete:
- progressBar.setRange(0, 1)
- progressBar.setValue(progressBar.maximum())
- elif itemStatus == TreeItem.StatusError:
- progressBar.setMinimum(oldProgressBar.minimum())
- progressBar.setMaximum(oldProgressBar.maximum())
- progressBar.setValue(oldProgressBar.value())
- elif itemStatus == TreeItem.StatusRemoved:
- pass
- elif itemStatus == TreeItem.StatusCompressing:
- progressBar.setRange(0, 0)
- else:
- raise ValueError('Unknown status: %r' % itemStatus)
- if itemStatusChanged:
- progressBar.setObjectName(TreeItem.ProgressBarName)
- tree.setItemWidget(item, TreeItem.IndexProgress, progressBar)
- item.setData(
- TreeItem.IndexStatus,
- QtCore.Qt.DisplayRole,
- QtCore.QVariant(self.fcRequestStatusNames[itemStatus]),
- )
- item.fcOldStatus = itemStatus
-
- ############################
- ##
- ############################
- def retranslateUi(self, parent):
- Ui_ViewRequestsWidget.retranslateUi(self, parent)
- tree = self.controlById(self.IdTree)
- root = tree.invisibleRootItem()
-
- # adjust header labels
- self.fcHeaderLabels = {
- TreeItem.IndexName: self.trUtf8('Name'),
- TreeItem.IndexSize: self.trUtf8('Size'),
- TreeItem.IndexMimeType: self.trUtf8('MimeType'),
- TreeItem.IndexStatus: self.trUtf8('Status'),
- TreeItem.IndexPriority: self.trUtf8('Priority'),
- TreeItem.IndexProgress: self.trUtf8('Progress'),
- TreeItem.IndexElapsed: self.trUtf8('Elapsed'),
- }
- tree.setHeaderLabels([i[1] for i in sorted(self.fcHeaderLabels.items())])
-
- # adjust status names and retranslate all items
- self.fcRequestStatusNames = {
- TreeItem.StatusPending: self.trUtf8('Pending'),
- TreeItem.StatusLoading: self.trUtf8('Loading'),
- TreeItem.StatusComplete: self.trUtf8('Complete'),
- TreeItem.StatusError: self.trUtf8('Error'),
- TreeItem.StatusRemoved: self.trUtf8('Removed'),
- TreeItem.StatusCompressing: self.trUtf8('Compressing'),
- ##TreeItem.StatusCompressed: self.trUtf8('Compressed'), #TODO: no way to distinguish compressed an loading
- }
- for item in treewidgetwrap.walkItem(root):
- fcpRequest = getattr(item, 'fcpRequest', None)
- if hasattr(item, 'fcpRequest'):
- item.setText(TreeItem.IndexStatus, self.fcRequestStatusNames[item.status()])
-
- # others
- self.menuRemoveGroup.setTitle(self.trUtf8('Remove group'))
-
- def closeEvent(self):
- self.viewClose()
-
- def hideEvent(self, event):
- self.fcGlobalFeedback.setVisible(False)
-
- def showEvent(self, event):
- self.fcGlobalFeedback.setVisible(True)
- if not self._isCreated:
- self._isCreated = True
-
- # setup tree
- tree = self.controlById(self.IdTree)
- tree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
- tree.setRootIsDecorated(False)
- tree.setSelectionMode(tree.ExtendedSelection)
- tree.setUniformRowHeights(True)
- self.connect(tree, QtCore.SIGNAL('customContextMenuRequested(const QPoint &)'), self.onTreeCustomContextMenuRequested)
- self.connect(tree, QtCore.SIGNAL('itemSelectionChanged() '), self.onTreeItemSelectionChanged)
-
- def viewClose(self):
- config.fcpClient.events -= self.fcpClientEvents
-
- #########################################
- ## methods
- #########################################
- #TODO: much...
- def addFcpRequest(self, fcpRequest):
- """
- @note: if you add a newly created request, make shure to set the requests PersistentUserData
- to the result of the call to L{persistentFcpRequestData}
- """
- tree = self.controlById(self.IdTree)
- root = tree.invisibleRootItem()
- item= TreeItem(fcpRequest, root)
- progressBar = ProgressBar(self.tree, item)
-
- progressBar.setObjectName(TreeItem.ProgressBarName)
- tree.setItemWidget(item, TreeItem.IndexProgress, progressBar)
- fileName = fcpRequest['Filename']
- mimeType = mimetypes.guess_type(fileName)[0]
- icon = config.fcResources.getIcon(
- config.mimeTypeIconName(mimeType),
- config.fcSettings.value('IconSize'),
- config.fcSettings.value('IconTheme'),
- )
- item.setIcon(0, icon)
- item.setData(
- TreeItem.IndexName,
- QtCore.Qt.DisplayRole,
- QtCore.QVariant(os.path.basename(fcpRequest['Filename']))
- )
-
- #TODO: take a wild guess at the size. no other way to do it currently
- estimatedSize = int((BLOCK_SIZE * fcpRequest['ProgressRequired']) + 1)
- item.setData(
- TreeItem.IndexSize,
- QtCore.Qt.DisplayRole,
- QtCore.QVariant(self.trUtf8('< ') + numbers.format_num_bytes(estimatedSize)),
- )
-
- self.fcpRequests[fcpRequest['Identifier']] = item
- self._adjustItemStatus(item)
- return item
-
- self.fcpRequests[fcpRequest['Identifier']] = item
- self._adjustItemStatus(item)
- return item
-
-
- def controlById(idGlobalFeedback, idControl):
- return getattr(idGlobalFeedback, idControl)
-
- # overwrite
- def populateMenu(self, menu):
- return menu
-
- def execDlgDownloadKey(self, fcpKey=None):
- """pops up the dialog to allow the user to download a key to disk
- @param fcpKey: key to initialize the key with or None
- """
- dlg = DlgDownloadKeyToDisk.DlgDownloadKeyToDisk(self, fcpKey=fcpKey)
- if dlg.exec_() == dlg.Accepted:
- self.downloadFile(
- dlg.fcpKey(),
- dlg.fileName(),
- persistence=fcp2.ConstPersistence.Forever,
- handleFilenameCollision=True,
- )
-
- def persistentFcpRequestData(self):
- return PersistentRequestData(ClientName=unicode(self.objectName())).dump()
-
- #########################################
- ## event handlers
- #########################################
- def onDlgDownloadKey(self, action):
- self.execDlgDownloadKey(fcpKey=None)
-
- def onRemoveSelectedDownloads(self, action):
- tree = self.controlById(self.IdTree)
- selectedItems = tree.selectedItems()
-
- # remove items
- for item in selectedItems:
- parent = item.parent()
- if parent is None:
- parent = tree.invisibleRootItem()
- parent.removeChild(item)
-
- # cancel all requests
- for item in selectedItems:
- for tmp_item in treewidgetwrap.walkItem(item):
- if tmp_item.fcpRequest is not None: # we may come across the same item multiple times
- if tmp_item.fcpRequest['Identifier'] in self.fcpRequests: #TODO: should never be False?! check
- del self.fcpRequests[tmp_item.fcpRequest['Identifier']]
- config.fcpClient.removeRequest(tmp_item.fcpRequest)
- tmp_item.fcpRequest = None
-
- def onRestartSelectedDownloads(self, action):
- tree = self.controlById(self.IdTree)
- for item in tree.selectedItems():
- if item.fcpRequest is None:
- raise RuntimeError('fcpRequest is None. should not happen')
- del self.fcpRequests[item.fcpRequest['Identifier']]
- item.fcpRequest = config.fcpClient.resendRequest(item.fcpRequest)
- self.fcpRequests[item.fcpRequest['Identifier']] = item
- self._adjustItemStatus(item)
-
- def onRemoveGroup(self, action):
- tree = self.controlById(self.IdTree)
- root = tree.invisibleRootItem()
-
- if action == self.fcActions['ActionRemoveCompleted']:
- flag = fcp2.ConstRequestStatus.Success
- elif action == self.fcActions['ActionRemoveFailed']:
- flag = fcp2.ConstRequestStatus.Error
- else:
- raise ValueError('Not implemented')
-
- for item in treewidgetwrap.walkItem(root, topdown=False):
- fcpRequest = getattr(item, 'fcpRequest', None)
- if fcpRequest is not None:
- if fcpRequest['Identifier'] in self.fcpRequests: #TODO: should never be False?! check
- del self.fcpRequests[fcpRequest['Identifier']]
-
- if fcpRequest['RequestStatus'] & fcp2.ConstRequestStatus.Removed:
- pass
- elif not fcpRequest['RequestStatus'] & flag:
- continue
- else:
- item.fcpRequest = None
- config.fcpClient.removeRequest(fcpRequest)
- parent = item.parent()
- if parent is None:
- parent = root
- parent.removeChild(item)
-
-
- # overwrite
- def onTreeCustomContextMenuRequested(self, pt):
- pass
-
- # overwrite
- def onTreeItemSelectionChanged(self):
- pass
-
- #########################################
- ## fcp event handlers
- #########################################
- def onFcpClientConnected(self, event, msg):
- for action in self.fcActions:
- action.setEnabled(True)
-
-
- def onFcpClientDisconnected(self, event, msg):
- for action in self.fcActions:
- action.setEnabled(False)
-
- def onFcpConfigData(self, fcpEvent, fcpRequest):
- pass
-
-
- def onFcpClientRequestCompleted(self, fcpEvent, fcpRequest):
- item = self.fcpRequests.get(fcpRequest['Identifier'], None)
- if item is not None:
- mimeType = fcpRequest['MetadataContentType']
- item.setData(
- TreeItem.IndexSize,
- QtCore.Qt.DisplayRole,
- QtCore.QVariant(numbers.format_num_bytes(fcpRequest['MetadataSize']))
- )
- item.setData(
- TreeItem.IndexMimeType,
- QtCore.Qt.DisplayRole,
- QtCore.QVariant(mimeType)
- )
- icon = config.fcResources.getIcon(
- config.mimeTypeIconName(mimeType),
- config.fcSettings.value('IconSize'),
- config.fcSettings.value('IconTheme'),
- )
- item.setIcon(0, icon)
- self._adjustItemStatus(item)
-
- def onFcpClientRequestCompressionStarted(self, fcpEvent, fcpRequest):
- item = self.fcpRequests.get(fcpRequest['Identifier'], None)
- if item is not None:
- self._adjustItemStatus(item)
-
- def onFcpClientRequestCompressionCompleted(self, fcpEvent, fcpRequest):
- item = self.fcpRequests.get(fcpRequest['Identifier'], None)
- if item is not None:
- self._adjustItemStatus(item)
-
- def onFcpClientRequestFailed(self, fcpEvent, fcpRequest):
- item = self.fcpRequests.get(fcpRequest['Identifier'], None)
- if item is not None:
- self._adjustItemStatus(item)
-
- #TODO: not tested
- def onFcpClientRequestModified(self, fcpEvent, fcpRequest):
-
- requestIdentifier = fcpRequest['Modified'].get(fcp2.ConstRequestModified.Identifier, None)
- if requestIdentifier is None:
- requestIdentifier = fcpRequest['Identifier']
- item = self.fcpRequests.get(requestIdentifier, None)
-
- if item is not None:
- if fcp2.ConstRequestModified.Identifier in fcpRequest['Modified']:
- newFcpIdentifier = fcpRequest['Identifier']
- del self.fcpRequests[requestIdentifier]
- self.fcpRequests[newFcpIdentifier] = item
-
- if fcp2.ConstRequestModified.Filename in fcpRequest['Modified']:
- item.setData(
- TreeItem.IndexName,
- QtCore.Qt.DisplayRole,
- QtCore.QVariant(os.path.basename(fcpRequest['Filename']))
- )
-
-
- def onFcpClientRequestProgress(self, fcpEvent, fcpRequest):
- item = self.fcpRequests.get(fcpRequest['Identifier'], None)
- if item is not None:
- self._adjustItemStatus(item)
-
- def onFcpClientRequestRemoved(self, fcpEvent, fcpRequest):
- pass
-
- def onFcpClientRequestStarted(self, fcpEvent, fcpRequest):
- if fcpRequest['RequestStatus'] & fcp2.ConstRequestStatus.Restored:
- try:
- requestData = PersistentRequestData.load(fcpRequest['PersistentUserData'])
- except pmstruct.PMStructError:
- pass
- else:
- if requestData.get('ClientName', None) == self.objectName():
- item = self.addFcpRequest(fcpRequest)
- else:
- item = self.fcpRequests.get(fcpRequest['Identifier'], None)
- if item is not None:
- self._adjustItemStatus(item)
-
-
-#**********************************************************************************
-#
-#**********************************************************************************
Modified: trunk/fclient/fclient/impl/ViewDownloads/ViewDownloads.py
===================================================================
--- trunk/fclient/fclient/impl/ViewDownloads/ViewDownloads.py 2008-08-16 08:10:49 UTC (rev 915)
+++ trunk/fclient/fclient/impl/ViewDownloads/ViewDownloads.py 2008-08-16 08:13:58 UTC (rev 916)
@@ -16,7 +16,7 @@
from PyQt4 import QtCore, QtGui
from .. import config
-from ..BaseRequestsWidget import RequestsWidget
+from ..BaseRequestsWidget import BaseRequestsWidget
#************************************************************************************
#
#************************************************************************************
@@ -98,13 +98,13 @@
)
-class ViewDownloadsWidget(RequestsWidget):
+class ViewDownloadsWidget(BaseRequestsWidget.RequestsWidget):
def __init__(self, parent, idGlobalFeedback=config.IdMainWindow):
self.menuRemoveGroup = None
- RequestsWidget.__init__(self, parent)
+ BaseRequestsWidget.RequestsWidget.__init__(self, parent)
self.setObjectName(config.IdViewDownloadsWidget)
config.ObjectRegistry.register(self)
@@ -124,18 +124,18 @@
###################################
def hideEvent(self, event):
self.fcGlobalFeedback.setVisible(False)
- RequestsWidget.hideEvent(self, event)
+ BaseRequestsWidget.RequestsWidget.hideEvent(self, event)
def showEvent(self, event):
self.fcGlobalFeedback.setVisible(True)
- RequestsWidget.showEvent(self, event)
+ BaseRequestsWidget.RequestsWidget.showEvent(self, event)
def populateMenu(self, menu):
menu.addAction(self.fcActions['ActionDownloadKeyToDisk'])
return menu
def retranslateUi(self, parent):
- RequestsWidget.retranslateUi(self, parent)
+ BaseRequestsWidget.RequestsWidget.retranslateUi(self, parent)
if self.menuRemoveGroup is not None:
self.menuRemoveGroup.setTitle(self.trUtf8('Remove group'))
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|