1 import time
2
3 from callback import Callback
4
6 """LongOpStatus provides a way of communicating the status of a long
7 running operations. The intended use is that when a long running operation
8 is about to start it should create an instance of this class and emit
9 it so that any listeners can pick it up and use it to record the status
10 of the operation.
11
12
13 Signals
14 =======
15
16 op-heartbeat - emitted every 'interval' calls to heartbeat.
17 op-end - emitted once when the operation completes.
18
19
20 Example usage:
21
22 class MyClass(Callback):
23
24 __signals__ = {
25 'op-start' : object
26 }
27
28 def long(self):
29 status = LongOpStatus("doing long job", 100, 10)
30
31 for i in xrange(0,99):
32 time.sleep(0.1)
33 status.heartbeat()
34
35 status.end()
36
37
38 class MyListener(object):
39
40 def __init__(self):
41 self._op = MyClass()
42 self._op.connect('op-start', self.start)
43 self._current_op = None
44
45 def start(self,long_op):
46 self._current_op.connect('op-heartbeat', self.heartbeat)
47 self._current_op.connect('op-end', self.stop)
48
49 def hearbeat(self):
50 # update status display
51
52 def stop(self):
53 # close the status display
54 self._current_op = None
55 """
56
57 __signals__ = {
58 'op-heartbeat' : None,
59 'op-end' : None
60 }
61
62 - def __init__(self, msg="",
63 total_steps=None,
64 interval=1,
65 can_cancel=False):
66 """
67 @param msg: A Message to indicated the purpose of the operation.
68 @type msg: string
69
70 @param total_steps: The total number of steps that the operation
71 will perform.
72 @type total_steps:
73
74 @param interval: The number of iterations between emissions.
75 @type interval:
76
77 @param can_cancel: Set to True if the operation can be cancelled.
78 If this is set the operation that creates the status object should
79 check the 'should_cancel' method regularly so that it can cancel
80 the operation.
81 @type can_cancel:
82 """
83 Callback.__init__(self)
84 self._msg = msg
85 self._total_steps = total_steps
86
87 self._interval = max(interval,1)
88 self._can_cancel = can_cancel
89
90 self._cancel = False
91 self._count = 0
92 self._countdown = interval
93 self._secs_left = 0
94 self._start = time.time()
95 self._running = True
96
98 if self._running:
99 self.emit('op-end')
100
102 """This should be called for each step in the operation. It will
103 emit a 'op-heartbeat' every 'interval' steps. It recalcuates the
104 'estimated_secs_to_complete' from the time taken for previous
105 steps.
106 """
107 self._countdown -= 1
108 if self._countdown <= 0:
109 elapsed = time.time() - self._start
110 self._secs_left = \
111 ( elapsed / self._interval ) \
112 * (self._total_steps - self._count)
113 self._count += self._interval
114 self._countdown = self._interval
115 self._start = time.time()
116 self.emit('op-heartbeat')
117
119 """Return the number of seconds estimated left before operation
120 completes. This will change as 'hearbeat' is called.
121
122 @return: estimated seconds to complete.
123 @rtype: int
124 """
125 return self._secs_left
126
128 """Inform the operation that it should complete.
129 """
130 self._cancel = True
131 self.end()
132
134 """End the operation. Causes the 'op-end' signal to be emitted.
135 """
136 self.emit('op-end')
137 self._running = False
138
140 """Return true of the user has asked for the operation to be cancelled.
141
142 @return: True of the operation should be cancelled.
143 @rtype: bool
144 """
145 return self._cancel
146
148 """@return: True if the operation can be cancelled.
149 @rtype: bool
150 """
151 return self._can_cancel
152
154 """@return: The current status description messages.
155 @rtype: string
156 """
157 return self._msg
158
160 """Set the current description message.
161
162 @param msg: The description message.
163 @type msg: string
164 """
165 self._msg = msg
166
168 """Get to total number of steps. NOTE: this is not the
169 number of times that the 'op-heartbeat' message will be
170 emited. 'op-heartbeat' is emited get_total_steps/interval
171 times.
172
173 @return: total number of steps.
174 @rtype: int
175 """
176 return self._total_steps
177
179 """Get the interval between 'op-hearbeat' signals.
180
181 @return: the interval between 'op-hearbeat' signals.
182 @rtype: int
183 """
184 return self._interval
185
186
187 if __name__ == '__main__':
188
189 s = LongOpStatus("msg", 100, 10)
190
193
196
197 s.connect('op-heartbeat', heartbeat)
198 s.connect('op-end', end)
199
200 for i in xrange(0, 99):
201 time.sleep(0.1)
202 s.heartbeat()
203
204 s.end()
205