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 Description
29 ===========
30
31 PyProgress is similar to wx.ProgressDialog in indeterminated mode, but with a
32 different gauge appearance and a different spinning behavior. The moving gauge
33 can be drawn with a single solid colour or with a shading gradient foreground.
34 The gauge background colour is user customizable.
35 The bar does not move always from the beginning to the end as in wx.ProgressDialog
36 in indeterminated mode, but spins cyclically forward and backward.
37 Other options include:
38
39 - Possibility to change the proportion between the spinning bar and the
40 entire gauge, so that the bar can be longer or shorter (the default is 20%);
41 - Modifying the number of steps the spinning bar performs before a forward
42 (or backward) loop reverses.
43
44 PyProgress can optionally display a Cancel button, and a wx.StaticText which
45 outputs the elapsed time from the starting of the process.
46
47
48 Supported Platforms
49 ===================
50
51 PyProgress has been tested on the following platforms:
52 * Windows (Windows XP);
53 * Linux Ubuntu (Dapper 6.06)
54
55
56 License And Version:
57 ===================
58
59 PyProgress is freeware and distributed under the wxPython license.
60
61
62 Latest Revision: Andrea Gavana @ 03 Nov 2006, 22.30 CET
63 Version 0.1
64
65 """
66
67 __docformat__ = "epytext"
68
69
70 import wx
71
72
73 Uncancelable = -1
74 Canceled = 0
75 Continue = 1
76 Finished = 2
77
78
79 LAYOUT_MARGIN = 8
80
81
82
83
84
85
87 """ This class provides a visual alternative for wx.Gauge."""
88
89 - def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
90 size=(-1,30)):
91 """ Default class constructor. """
92
93 wx.PyWindow.__init__(self, parent, id, pos, size, style=wx.SUNKEN_BORDER)
94
95 self._value = 0
96 self._steps = 50
97 self._pos = 0
98 self._current = 0
99 self._gaugeproportion = 0.2
100 self._firstGradient = wx.WHITE
101 self._secondGradient = wx.BLUE
102 self._background = wx.Brush(wx.WHITE, wx.SOLID)
103
104 self.Bind(wx.EVT_PAINT, self.OnPaint)
105 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
106
107
109 """ Returns the first gradient colour. """
110
111 return self._firstGradient
112
113
115 """ Sets the first gradient colour. """
116
117 self._firstGradient = colour
118 self.Refresh()
119
120
122 """ Returns the second gradient colour. """
123
124 return self._secondGradient
125
126
128 """ Sets the second gradient colour. """
129
130 self._secondGradient = colour
131 self.Refresh()
132
133
135 """ Returns the gauge background colour. """
136
137 return self._background
138
139
141 """ Sets the gauge background colour. """
142
143 self._background = wx.Brush(colour, wx.SOLID)
144
145
147 """
148 Sets the number of steps the gauge performs before switching from
149 forward to backward (or vice-versa) movement.
150 """
151
152 if steps <= 0:
153 raise "ERROR:\n Gauge steps must be greater than zero. "
154 return
155
156 if steps != self._steps:
157 self._steps = steps
158
159
161 """
162 Returns the number of steps the gauge performs before switching from
163 forward to backward (or vice-versa) movement.
164 """
165
166 return self._steps
167
168
170 """
171 Returns the relative proportion between the sliding bar and the
172 whole gauge.
173 """
174
175 return self._gaugeproportion
176
177
179 """
180 Sets the relative proportion between the sliding bar and the
181 whole gauge.
182 """
183
184 if proportion <= 0 or proportion >= 1:
185 raise "ERROR:\n Gauge proportion must be between 0 and 1. "
186 return
187
188 if proportion != self._gaugeproportion:
189 self._gaugeproportion = proportion
190
191
193 """ Handles the wx.EVT_ERASE_BACKGROUND event for ProgressGauge. """
194
195 pass
196
197
199 """ Handles the wx.EVT_PAINT event for ProgressGauge. """
200
201 dc = wx.BufferedPaintDC(self)
202 dc.SetBackground(self._background)
203
204 dc.Clear()
205
206 xsize, ysize = self.GetClientSize()
207 interval = xsize/float(self._steps)
208
209 self._pos = interval*self._value
210
211 status = self._current/(self._steps - int(self._gaugeproportion*xsize)/int(interval))
212
213 if status%2 == 0:
214 increment = 1
215 else:
216 increment = -1
217
218 self._value = self._value + increment
219 self._current = self._current + 1
220 self.DrawProgress(dc, xsize, ysize, increment)
221
222
224 """ Actually draws the sliding bar. """
225
226 if increment > 0:
227 col1 = self.GetFirstGradientColour()
228 col2 = self.GetSecondGradientColour()
229 else:
230 col1 = self.GetSecondGradientColour()
231 col2 = self.GetFirstGradientColour()
232
233 interval = self._gaugeproportion*xsize
234
235 r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
236 r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
237
238 rstep = float((r2 - r1)) / interval
239 gstep = float((g2 - g1)) / interval
240 bstep = float((b2 - b1)) / interval
241
242 rf, gf, bf = 0, 0, 0
243 dc.SetBrush(wx.TRANSPARENT_BRUSH)
244
245 for ii in xrange(int(self._pos), int(self._pos+interval)):
246 currCol = (r1 + rf, g1 + gf, b1 + bf)
247 dc.SetPen(wx.Pen(currCol, 2))
248 dc.DrawLine(ii, 1, ii, ysize-2)
249 rf = rf + rstep
250 gf = gf + gstep
251 bf = bf + bstep
252
253
255 """ Updates the gauge with a new value. """
256
257 self.Refresh()
258
259
260
261
262
263
265 """
266 PyProgress is similar to wx.ProgressDialog in indeterminated mode, but with a
267 different gauge appearance and a different spinning behavior. The moving gauge
268 can be drawn with a single solid colour or with a shading gradient foreground.
269 The gauge background colour is user customizable.
270 The bar does not move always from the beginning to the end as in wx.ProgressDialog
271 in indeterminated mode, but spins cyclically forward and backward.
272 """
273
274 - def __init__(self, parent=None, id=-1, title="", message="",
275 style=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE):
276 """ Default class constructor. """
277
278 wx.Dialog.__init__(self, parent, id, title)
279
280 self._delay = 3
281 self._hasAbortButton = False
282
283
284 self.SetExtraStyle(self.GetExtraStyle()|wx.WS_EX_TRANSIENT)
285
286 self._hasAbortButton = (style & wx.PD_CAN_ABORT)
287
288 if wx.Platform == "__WXMSW__":
289
290
291
292 if not self._hasAbortButton:
293 self.EnableClose(False)
294
295 self._state = (self._hasAbortButton and [Continue] or [Uncancelable])[0]
296 self._parentTop = wx.GetTopLevelParent(parent)
297
298 dc = wx.ClientDC(self)
299 dc.SetFont(wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT))
300 widthText, dummy = dc.GetTextExtent(message)
301
302 sizer = wx.BoxSizer(wx.VERTICAL)
303
304 self._msg = wx.StaticText(self, wx.ID_ANY, message)
305 sizer.Add(self._msg, 0, wx.LEFT|wx.TOP, 2*LAYOUT_MARGIN)
306
307 sizeDlg = wx.Size()
308 sizeLabel = self._msg.GetSize()
309 sizeDlg.y = 2*LAYOUT_MARGIN + sizeLabel.y
310
311 self._gauge = ProgressGauge(self, -1)
312
313 sizer.Add(self._gauge, 0, wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND, 2*LAYOUT_MARGIN)
314
315 sizeGauge = self._gauge.GetSize()
316 sizeDlg.y += 2*LAYOUT_MARGIN + sizeGauge.y
317
318
319 self._elapsed = None
320 self._display_estimated = self._last_timeupdate = self._break = 0
321 self._ctdelay = 0
322
323 label = None
324
325 nTimeLabels = 0
326
327 if style & wx.PD_ELAPSED_TIME:
328
329 nTimeLabels += 1
330 self._elapsed = self.CreateLabel("Elapsed time : ", sizer)
331
332 if nTimeLabels > 0:
333
334 label = wx.StaticText(self, -1, "")
335
336 self._timeStart = wx.GetCurrentTime()
337 sizeDlg.y += nTimeLabels*(label.GetSize().y + LAYOUT_MARGIN)
338 label.Destroy()
339
340 sizeDlgModified = False
341
342 if wx.Platform == "__WXMSW__":
343 sizerFlags = wx.ALIGN_RIGHT|wx.ALL
344 else:
345 sizerFlags = wx.ALIGN_CENTER_HORIZONTAL|wx.BOTTOM|wx.TOP
346
347 if self._hasAbortButton:
348 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
349
350 self._btnAbort = wx.Button(self, -1, "Cancel")
351 self._btnAbort.Bind(wx.EVT_BUTTON, self.OnCancel)
352
353
354 buttonSizer.Add(self._btnAbort, 0, sizerFlags, LAYOUT_MARGIN)
355
356 if not sizeDlgModified:
357 sizeDlg.y += 2*LAYOUT_MARGIN + wx.Button.GetDefaultSize().y
358
359 if self._hasAbortButton:
360 sizer.Add(buttonSizer, 0, sizerFlags, LAYOUT_MARGIN )
361
362 self.Bind(wx.EVT_CLOSE, self.OnClose)
363 self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
364
365 self._windowStyle = style
366
367 self.SetSizerAndFit(sizer)
368
369 sizeDlg.y += 2*LAYOUT_MARGIN
370
371
372 sizeDlg.x = max(widthText, 4*sizeDlg.y/3)
373 sizeDlg.x *= 3
374 sizeDlg.x /= 2
375 self.SetClientSize(sizeDlg)
376
377 self.Centre(wx.CENTER_FRAME|wx.BOTH)
378
379 if style & wx.PD_APP_MODAL:
380 self._winDisabler = wx.WindowDisabler(self)
381 else:
382 if self._parentTop:
383 self._parentTop.Disable()
384 self._winDisabler = None
385
386 self.ShowDialog()
387 self.Enable()
388
389
390
391 if self._elapsed:
392 self.SetTimeLabel(0, self._elapsed)
393
394 if not wx.EventLoop().GetActive():
395 self.evtloop = wx.EventLoop()
396 wx.EventLoop.SetActive(self.evtloop)
397
398 self.Update()
399
400
402 """ Creates the wx.StaticText that holds the elapsed time label. """
403
404 locsizer = wx.BoxSizer(wx.HORIZONTAL)
405 dummy = wx.StaticText(self, wx.ID_ANY, text)
406 label = wx.StaticText(self, wx.ID_ANY, "unknown")
407
408 if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
409
410 locsizer.Add(dummy, 1, wx.ALIGN_LEFT)
411 locsizer.Add(label, 1, wx.ALIGN_LEFT|wx.LEFT, LAYOUT_MARGIN)
412 sizer.Add(locsizer, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.TOP, LAYOUT_MARGIN)
413 else:
414
415 sizer.Add(locsizer, 0, wx.ALIGN_RIGHT|wx.RIGHT|wx.TOP, LAYOUT_MARGIN)
416 locsizer.Add(dummy)
417 locsizer.Add(label, 0, wx.LEFT, LAYOUT_MARGIN)
418
419 return label
420
421
422
423
424
425
427 """ Update the progress dialog with a (optionally) new message. """
428
429 self._gauge.Update()
430
431 if newmsg and newmsg != self._msg.GetLabel():
432 self._msg.SetLabel(newmsg)
433 wx.YieldIfNeeded()
434
435 if self._elapsed:
436 elapsed = wx.GetCurrentTime() - self._timeStart
437 if self._last_timeupdate < elapsed:
438 self._last_timeupdate = elapsed
439
440 self.SetTimeLabel(elapsed, self._elapsed)
441
442 if self._state == Finished:
443
444 if not self._windowStyle & wx.PD_AUTO_HIDE:
445
446 self.EnableClose()
447
448 if newmsg == "":
449
450 self._msg.SetLabel("Done.")
451
452 wx.YieldIfNeeded()
453 self.ShowModal()
454 return False
455
456 else:
457
458
459
460 self.ReenableOtherWindows()
461 self.Hide()
462
463
464
465 wx.YieldIfNeeded()
466
467 return self._state != Canceled
468
469
474
475
480
481
486
487
492
493
495 """ Returns the gauge background colour. """
496
497 return self._gauge.GetGaugeBackground()
498
499
501 """ Sets the gauge background colour. """
502
503 self._gauge.SetGaugeBackground(colour)
504
505
507 """
508 Sets the number of steps the gauge performs before switching from
509 forward to backward (or vice-versa) movement.
510 """
511
512 self._gauge.SetGaugeSteps(steps)
513
514
516 """
517 Returns the number of steps the gauge performs before switching from
518 forward to backward (or vice-versa) movement.
519 """
520
521 return self._gauge.GetGaugeSteps()
522
523
525 """
526 Returns the relative proportion between the sliding bar and the
527 whole gauge.
528 """
529
530 return self._gauge.GetGaugeProportion()
531
532
534 """
535 Sets the relative proportion between the sliding bar and the
536 whole gauge.
537 """
538
539 self._gauge.SetGaugeProportion(proportion)
540
541
543 """ Show the dialog. """
544
545
546
547
548 if not show:
549 self.ReenableOtherWindows()
550
551 return self.Show()
552
553
554
555
556
557
559 """ Handles the wx.EVT_BUTTON event for the Cancel button. """
560
561 if self._state == Finished:
562
563
564
565 event.Skip()
566
567 else:
568
569
570
571 self._state = Canceled
572
573
574
575 self.DisableAbort()
576
577
578 self._timeStop = wx.GetCurrentTime()
579
580 self.ReenableOtherWindows()
581
582
584 """ Handles the wx.EVT_WINDOW_DESTROY event for PyProgress. """
585
586 self.ReenableOtherWindows()
587 event.Skip()
588
589
591 """ Handles the wx.EVT_CLOSE event for PyProgress. """
592
593 if self._state == Uncancelable:
594
595
596 event.Veto()
597
598 elif self._state == Finished:
599
600
601 self.Hide()
602 event.Skip()
603
604 else:
605
606
607 self._state = Canceled
608 self.DisableAbort()
609
610 self._timeStop = wx.GetCurrentTime()
611
612
614 """ Re-enables the other windows if using wx.WindowDisabler. """
615
616 if self._windowStyle & wx.PD_APP_MODAL:
617 if hasattr(self, "_winDisabler"):
618 del self._winDisabler
619
620 else:
621
622 if self._parentTop:
623 self._parentTop.Enable()
624
625
627 """ Sets the elapsed time label. """
628
629 if label:
630
631 hours = val/3600
632 minutes = (val%3600)/60
633 seconds = val%60
634 strs = ("%lu:%02lu:%02lu")%(hours, minutes, seconds)
635
636 if strs != label.GetLabel():
637 label.SetLabel(strs)
638
639
641 """ Enables or disables the Cancel button. """
642
643 if self._hasAbortButton:
644 if self._btnAbort:
645 self._btnAbort.Enable(enable)
646
647
649 """ Enables or disables the Close button. """
650
651 if self._hasAbortButton:
652 if self._btnAbort:
653 self._btnAbort.Enable(enable)
654 self._btnAbort.SetLabel("Close")
655 self._btnAbort.Bind(wx.EVT_BUTTON, self.OnClose)
656
657
659 """ Disables the Cancel button. """
660
661 self.EnableAbort(False)
662