pstatbar.py
9.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
###############################################################################
# Name: pstatbar.py #
# Purpose: Custom statusbar with builtin progress indicator #
# Author: Cody Precord <cprecord@editra.org> #
# Copyright: (c) 2008 Cody Precord <staff@editra.org> #
# License: wxWindows License #
###############################################################################
"""
Editra Control Library: ProgressStatusBar
Custom StatusBar that has a builtin progress gauge to indicate busy status and
progress of long running tasks in a window.
The Progress Gauge is only shown when it is active. When shown it is shown in
the far rightmost field of the StatusBar. The size of the progress Guage is
also determined by the size of the right most field.When created the StatusBar
will creates two fields by default, field 0 is expanding, field 1 is set as a
small fixed field on the right. To change this behavior simply call SetFields
after creating the bar to change it.
"""
__author__ = "Cody Precord <cprecord@editra.org>"
__svnid__ = "$Id: pstatbar.py 66840 2011-02-03 21:05:28Z CJP $"
__revision__ = "$Revision: 66840 $"
__all__ = ["ProgressStatusBar",]
#--------------------------------------------------------------------------#
# Dependancies
import wx
#--------------------------------------------------------------------------#
# Globals
#--------------------------------------------------------------------------#
class ProgressStatusBar(wx.StatusBar):
"""Custom StatusBar with a built-in progress bar"""
def __init__(self, parent, id_=wx.ID_ANY,
style=wx.SB_FLAT,
name="ProgressStatusBar"):
"""Creates a status bar that can hide and show a progressbar
in the far right section. The size of the progressbar is also
determined by the size of the right most section.
@param parent: Frame this status bar belongs to
"""
super(ProgressStatusBar, self).__init__(parent, id_, style, name)
# Attributes
self._changed = False # position has changed ?
self.busy = False # Bar in busy mode ?
self.stop = False # Stop flag to stop progress from other threads
self.progress = 0 # Current progress value of the bar
self.range = 0 # Range of progress indicator
self.tmp = None # Temp for text that may be pushed when busy
self.timer = wx.Timer(self)
self.prog = wx.Gauge(self, style=wx.GA_HORIZONTAL)
self.prog.Hide()
# Layout
self.SetFieldsCount(2)
self.SetStatusWidths([-1, 155])
# Event Handlers
self.Bind(wx.EVT_IDLE, lambda evt: self.__Reposition())
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.Bind(wx.EVT_SIZE, self.OnSize)
def __del__(self):
"""Make sure the timer is stopped
@postcondition: timer is cleaned up
"""
if self.timer.IsRunning():
self.timer.Stop()
def __Reposition(self):
"""Does the actual repositioning of progress bar
@postcondition: Progress bar is repostioned inside right most field
"""
if self._changed:
rect = self.GetFieldRect(self.GetFieldsCount() - 1)
self.prog.SetPosition((rect.x + 2, rect.y + 2))
self.prog.SetSize((rect.width - 8, rect.height - 4))
self._changed = False
def _UpdateRange(self, range):
"""Update the internal progress gauges range
@param range: int
"""
self.range = range
try:
self.prog.SetRange(range)
except OverflowError:
# range too large, scale everything to 100
self.prog.SetRange(100)
def _UpdateValue(self, value):
"""Update the internal progress gauges value
@param range: int
"""
# Ensure value is within range
range = self.prog.GetRange()
if range != self.range: # need to scale value
value = int((float(value) / float(range)) * 100)
self.progress = value
self.prog.SetValue(value)
#---- Public Methods ----#
def Destroy(self):
"""Destroy the control"""
if self.timer.IsRunning():
self.timer.Stop()
del self.timer
super(ProgressStatusBar, self).Destroy()
def DoStop(self):
"""Stop any progress indication action and hide the bar"""
self.timer.Stop()
self.ShowProgress(False)
self.prog.SetValue(0) # Reset progress value
self.busy = False
self.stop = False
# Restore any status text that was sent while busy
if self.tmp is not None:
self.SetStatusText(self.tmp, self.GetFieldsCount() - 1)
self.tmp = None
def GetGauge(self):
"""Return the wx.Gauge used by this window
@return: wx.Gauge
"""
return self.prog
def GetProgress(self):
"""Get the progress of the progress bar
@return: int
"""
return self.prog.GetValue()
def GetRange(self):
"""Get the what the range of the progress bar is
@return: int
"""
return self.prog.GetRange()
def IsBusy(self):
"""Is the progress indicator busy or not
@return: bool
"""
return self.timer.IsRunning()
def OnSize(self, evt):
"""Reposition progress bar on resize
@param evt: wx.EVT_SIZE
"""
self.__Reposition()
self._changed = True
evt.Skip()
def OnTimer(self, evt):
"""Update the progress bar while the timer is running
@param evt: wx.EVT_TIMER
"""
# Check stop flag that can be set from non main thread
if self.stop:
self.DoStop()
return
if not self.prog.IsShown():
self.Stop()
if self.busy or self.progress < 0:
self.prog.Pulse()
else:
# Update the Range if it has changed
if self.range >= 0 and self.range != self.prog.GetRange():
self._UpdateRange(self.range)
# Update the progress value if it is less than the range
if self.progress <= self.range:
self._UpdateValue(self.progress)
def Run(self, rate=100):
"""Start the bar's timer to check for updates to progress
@keyword rate: rate at which to check for updates in msec
"""
if not self.timer.IsRunning():
self.timer.Start(rate)
def SetProgress(self, val):
"""Set the controls internal progress value that is reflected in the
progress bar when the timer next updates. Be sure to call Start before
calling this method if you want the changes to be visible. This method
can be called from non gui threads.
@param val: int
"""
self.progress = val
if val > 0 and wx.Thread_IsMain():
self._UpdateValue(val)
def SetRange(self, val):
"""Set the what the range of the progress bar is. This method can safely
be called from non gui threads.
@param val: int
"""
self.range = val
if val > 0 and wx.Thread_IsMain():
self._UpdateRange(val)
def ShowProgress(self, show=True):
"""Manually show or hide the progress bar
@keyword show: bool
"""
# If showing make sure bar is positioned properly
if show:
self.__Reposition()
self.prog.Show(show)
wx.GetApp().ProcessPendingEvents()
def SetStatusText(self, txt, number=0):
"""Override wx.StatusBar method to prevent text from being
put in when the progress indicator is running. Any text that
comes when it is running is buffered to be displayed afterwords.
@param txt: Text to put on status bar
@keyword number: Section number to put text in
"""
if number == self.GetFieldsCount() - 1 and self.IsBusy():
if self.tmp is None:
self.tmp = txt
else:
try:
super(ProgressStatusBar, self).SetStatusText(txt, number)
except wx.PyAssertionError:
pass
# Alias for SetStatusText
PushStatusText = SetStatusText
def Start(self, rate=100):
"""Show and the progress indicator and start the timer
@keyword rate: rate to update progress bar in msec
"""
self.__Reposition()
bfield = self.GetFieldsCount() - 1
self.tmp = self.GetStatusText(bfield)
# Clear the progress field so the text doesn't show behind
# the progress indicator.
super(ProgressStatusBar, self).SetStatusText(u'', bfield)
self.stop = False
self.ShowProgress(True)
self.Run(rate)
def StartBusy(self, rate=100):
"""Show and start the progress indicator in pulse mode
@keyword rate: interval to pulse indicator at in msec
"""
self.busy = True
self.Start(rate)
def Stop(self):
"""Stop and hide the progress bar. This method may safely be called
from background threads.
@precondition: Bar is already running
"""
if wx.Thread_IsMain():
self.DoStop()
else:
self.stop = True # Set flag from non main thread
self.progress = 0
def StopBusy(self):
"""Stop and hide the progress indicator
@postcondition: Progress bar is hidden from view
"""
self.busy = False
self.Stop()