more work on long operation framework.
svn: r8126
This commit is contained in:
		@@ -1,3 +1,7 @@
 | 
			
		||||
2007-02-16  Richard Taylor  <rjt-gramps@thegrindstone.me.uk>
 | 
			
		||||
	* src/GrampsDb/_LongOpStatus.py: more work on long operation framework
 | 
			
		||||
	* src/GrampsDb/_CursorIterator.py: more work on long operation framework
 | 
			
		||||
 | 
			
		||||
2007-02-15  Don Allingham  <don@gramps-project.org>
 | 
			
		||||
	* src/GrampsDbUtils/_GedcomParse.py: refactoring
 | 
			
		||||
	* src/GrampsDbUtils/_GedcomUtils.py: refactoring
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,11 @@
 | 
			
		||||
 | 
			
		||||
class LongOpStatus(object):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
	self._cancel = False
 | 
			
		||||
 | 
			
		||||
    def cancel(self):
 | 
			
		||||
	self._cancel = True
 | 
			
		||||
 | 
			
		||||
    def shouldCancel(self):
 | 
			
		||||
	return self._cancel
 | 
			
		||||
from _LongOpStatus import LongOpStatus
 | 
			
		||||
 | 
			
		||||
class CursorIterator(object):
 | 
			
		||||
    
 | 
			
		||||
    def __init__(self,db,cursor):
 | 
			
		||||
	self._db = db
 | 
			
		||||
	self._cursor = cursor
 | 
			
		||||
	#self._status = LongOpStatus(total_steps=cursor.get_length(),interval=10)
 | 
			
		||||
	self._status = LongOpStatus()
 | 
			
		||||
 | 
			
		||||
    def __iter__(self):
 | 
			
		||||
@@ -30,18 +22,21 @@ class CursorIterator(object):
 | 
			
		||||
		    yield next
 | 
			
		||||
 | 
			
		||||
		    # check for cancel
 | 
			
		||||
		    #if self._status.shouldCancel():
 | 
			
		||||
		    #if self._status.should_cancel():
 | 
			
		||||
		    #    raise GrampsDbUserCancel
 | 
			
		||||
 | 
			
		||||
		    # emit heartbeat
 | 
			
		||||
		    self._db.emit('long-op-heartbeat')
 | 
			
		||||
		    self._status.heartbeat()
 | 
			
		||||
		    next = self._cursor.next()
 | 
			
		||||
 | 
			
		||||
	    # emit stop signal
 | 
			
		||||
	    self._db.emit('long-op-end')
 | 
			
		||||
	    self._status.end()
 | 
			
		||||
	    self._cursor.close()
 | 
			
		||||
	    raise StopIteration
 | 
			
		||||
	except:
 | 
			
		||||
	    # Not allowed to use 'finally' because we 
 | 
			
		||||
	    # yeild inside the try clause.
 | 
			
		||||
	    self._cursor.close()
 | 
			
		||||
	    self._db.emit('long-op-end')
 | 
			
		||||
	    self._status_end()
 | 
			
		||||
	    raise
 | 
			
		||||
    
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										165
									
								
								src/GrampsDb/_LongOpStatus.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								src/GrampsDb/_LongOpStatus.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from _GrampsDBCallback import GrampsDBCallback
 | 
			
		||||
 | 
			
		||||
class LongOpStatus(GrampsDBCallback):
 | 
			
		||||
    """LongOpStatus provides a way of communicating the status of a long
 | 
			
		||||
    running operations. The intended use is that when a long running operation
 | 
			
		||||
    is about to start it should create an instance of this class and emit
 | 
			
		||||
    it so that any listeners can pick it up and use it to record the status 
 | 
			
		||||
    of the operation.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Signals
 | 
			
		||||
    =======
 | 
			
		||||
    
 | 
			
		||||
      op-heartbeat - emitted every 'interval' calls to heartbeat. 
 | 
			
		||||
      op-end       - emitted once when the operation completes.
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    Example usage:
 | 
			
		||||
 | 
			
		||||
    class MyClass(GrampsDBCallback):
 | 
			
		||||
 | 
			
		||||
        __signals__ = {
 | 
			
		||||
	   'op-start'   : object
 | 
			
		||||
	}
 | 
			
		||||
    
 | 
			
		||||
        def long(self):
 | 
			
		||||
	    status = LongOpStatus("doing long job", 100, 10)
 | 
			
		||||
 | 
			
		||||
            for i in xrange(0,99):
 | 
			
		||||
	       time.sleep(0.1)
 | 
			
		||||
	       status.heartbeat()
 | 
			
		||||
 | 
			
		||||
            status.end()
 | 
			
		||||
	    
 | 
			
		||||
 | 
			
		||||
    class MyListener(object):
 | 
			
		||||
 | 
			
		||||
         def __init__(self):
 | 
			
		||||
	     self._op = MyClass()
 | 
			
		||||
	     self._op.connect('op-start', self.start)
 | 
			
		||||
	     self._current_op = None
 | 
			
		||||
 | 
			
		||||
         def start(self,long_op):
 | 
			
		||||
	     self._current_op.connect('op-heartbeat', self.heartbeat)
 | 
			
		||||
	     self._current_op.connect('op-end', self.stop)
 | 
			
		||||
 | 
			
		||||
         def hearbeat(self):
 | 
			
		||||
	     # update status display
 | 
			
		||||
 | 
			
		||||
         def stop(self):
 | 
			
		||||
	     # close the status display
 | 
			
		||||
             self._current_op = None
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __signals__ = {
 | 
			
		||||
	'op-heartbeat'   : None,
 | 
			
		||||
	'op-end'         : None
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    def __init__(self,msg="",total_steps=None,interval=1,can_cancel=False):
 | 
			
		||||
	"""
 | 
			
		||||
	@param msg: A Message to indicated the purpose of the operation.
 | 
			
		||||
	@type msg: string
 | 
			
		||||
	
 | 
			
		||||
	@param total_steps: The total number of steps that the operation 
 | 
			
		||||
	will perform.
 | 
			
		||||
	@type total_steps:
 | 
			
		||||
 | 
			
		||||
	@param interval: The number of iterations between emissions.
 | 
			
		||||
	@type interval:
 | 
			
		||||
 | 
			
		||||
	@param can_cancel: Set to True if the operation can be cancelled.
 | 
			
		||||
	If this is set the operation that creates the status object should
 | 
			
		||||
	check the 'should_cancel' method regularly so that it can cancel 
 | 
			
		||||
	the operation.
 | 
			
		||||
	@type can_cancel:
 | 
			
		||||
	"""
 | 
			
		||||
        GrampsDBCallback.__init__(self)
 | 
			
		||||
	self._msg = msg
 | 
			
		||||
	self._total = total_steps
 | 
			
		||||
	self._interval = interval
 | 
			
		||||
	self._can_cancel = can_cancel
 | 
			
		||||
 | 
			
		||||
	self._cancel = False
 | 
			
		||||
	self._count = 0
 | 
			
		||||
	self._countdown = interval
 | 
			
		||||
	self._secs_left = 0
 | 
			
		||||
	self._start = time.time()
 | 
			
		||||
 | 
			
		||||
    def heartbeat(self):
 | 
			
		||||
	"""This should be called for each step in the operation. It will
 | 
			
		||||
	emit a 'op-heartbeat' every 'interval' steps. It recalcuates the
 | 
			
		||||
	'estimated_secs_to_complete' from the time taken for previous 
 | 
			
		||||
	steps.
 | 
			
		||||
	"""
 | 
			
		||||
	self._countdown -= 1
 | 
			
		||||
	if self._countdown <= 0:
 | 
			
		||||
	    elapsed = time.time() - self._start
 | 
			
		||||
	    self._secs_left = \
 | 
			
		||||
		( elapsed / self._interval ) \
 | 
			
		||||
		* (self._total - self._count)
 | 
			
		||||
	    self._count += self._interval
 | 
			
		||||
	    self._countdown = self._interval
 | 
			
		||||
	    self._start = time.time()
 | 
			
		||||
	    self.emit('op-heartbeat')
 | 
			
		||||
 | 
			
		||||
    def estimated_secs_to_complete(self):
 | 
			
		||||
	"""Return the number of seconds estimated left before operation 
 | 
			
		||||
	completes. This will change as 'hearbeat' is called.
 | 
			
		||||
 | 
			
		||||
	@return: estimated seconds to complete.
 | 
			
		||||
	@rtype: int
 | 
			
		||||
	"""
 | 
			
		||||
	return self._secs_left
 | 
			
		||||
 | 
			
		||||
    def cancel(self):
 | 
			
		||||
	"""Inform the operation that it should complete.
 | 
			
		||||
	"""
 | 
			
		||||
	self._cancel = True
 | 
			
		||||
	self.end()
 | 
			
		||||
 | 
			
		||||
    def end(self):
 | 
			
		||||
	"""End the operation. Causes the 'op-end' signal to be emitted.    
 | 
			
		||||
	"""
 | 
			
		||||
	self.emit('op-end')
 | 
			
		||||
 | 
			
		||||
    def should_cancel(self):
 | 
			
		||||
	"""Returns true of the user has asked for the operation to be cancelled.
 | 
			
		||||
	
 | 
			
		||||
	@return: True of the operation should be cancelled.
 | 
			
		||||
	@rtype: bool
 | 
			
		||||
	"""
 | 
			
		||||
	return self._cancel
 | 
			
		||||
 | 
			
		||||
    def can_cancel(self):
 | 
			
		||||
	return self._can_cancel
 | 
			
		||||
 | 
			
		||||
    def get_msg(self):
 | 
			
		||||
	return msg
 | 
			
		||||
 | 
			
		||||
    def set_msg(self, msg):
 | 
			
		||||
	self._msg = msg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
 | 
			
		||||
    s = LongOpStatus("msg", 100, 10)
 | 
			
		||||
 | 
			
		||||
    def heartbeat():
 | 
			
		||||
	print "heartbeat ", s.estimated_secs_to_complete()
 | 
			
		||||
 | 
			
		||||
    def end():
 | 
			
		||||
	print "end"
 | 
			
		||||
 | 
			
		||||
    s.connect('op-heartbeat', heartbeat)
 | 
			
		||||
    s.connect('op-end', end)
 | 
			
		||||
 | 
			
		||||
    for i in xrange(0,99):
 | 
			
		||||
	time.sleep(0.1)
 | 
			
		||||
	s.heartbeat()
 | 
			
		||||
 | 
			
		||||
    s.end()
 | 
			
		||||
    
 | 
			
		||||
		Reference in New Issue
	
	Block a user