mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-29 06:32:40 -06:00
917 lines
29 KiB
Python
917 lines
29 KiB
Python
# --------------------------------------------------------------------------------- #
|
|
# PYPROGRESS wxPython IMPLEMENTATION
|
|
#
|
|
# Andrea Gavana, @ 03 Nov 2006
|
|
# Latest Revision: 19 Dec 2012, 21.00 GMT
|
|
#
|
|
#
|
|
# TODO List
|
|
#
|
|
# 1. Do we support all the styles of wx.ProgressDialog in indeterminated mode?
|
|
#
|
|
# 2. Other ideas?
|
|
#
|
|
#
|
|
# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
|
|
# Write To Me At:
|
|
#
|
|
# andrea.gavana@maerskoil.com
|
|
# andrea.gavana@gmail.com
|
|
#
|
|
# Or, Obviously, To The wxPython Mailing List!!!
|
|
#
|
|
# Tags: phoenix-port, unittest, documented
|
|
#
|
|
# End Of Comments
|
|
# --------------------------------------------------------------------------------- #
|
|
|
|
"""
|
|
:class:`PyProgress` is similar to :class:`ProgressDialog` in indeterminated mode, but with a
|
|
different gauge appearance and a different spinning behavior.
|
|
|
|
|
|
Description
|
|
===========
|
|
|
|
:class:`PyProgress` is similar to :class:`ProgressDialog` in indeterminated mode, but with a
|
|
different gauge appearance and a different spinning behavior. The moving gauge
|
|
can be drawn with a single solid colour or with a shading gradient foreground.
|
|
The gauge background colour is user customizable.
|
|
|
|
The bar does not move always from the beginning to the end as in :class:`ProgressDialog`
|
|
in indeterminated mode, but spins cyclically forward and backward.
|
|
|
|
Other options include:
|
|
|
|
- Possibility to change the proportion between the spinning bar and the
|
|
entire gauge, so that the bar can be longer or shorter (the default is 20%);
|
|
- Modifying the number of steps the spinning bar performs before a forward
|
|
(or backward) loop reverses.
|
|
|
|
:class:`PyProgress` can optionally display a ``Cancel`` button, and a :class:`StaticText` which
|
|
outputs the elapsed time from the starting of the process.
|
|
|
|
|
|
Usage
|
|
=====
|
|
|
|
Usage example::
|
|
|
|
import wx
|
|
import wx.lib.agw.pyprogress as PP
|
|
|
|
# Our normal wxApp-derived class, as usual
|
|
app = wx.App(0)
|
|
|
|
dlg = PP.PyProgress(None, -1, "PyProgress Example",
|
|
"An Informative Message",
|
|
agwStyle=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME)
|
|
|
|
dlg.SetGaugeProportion(0.2)
|
|
dlg.SetGaugeSteps(50)
|
|
dlg.SetGaugeBackground(wx.WHITE)
|
|
dlg.SetFirstGradientColour(wx.WHITE)
|
|
dlg.SetSecondGradientColour(wx.BLUE)
|
|
|
|
max = 400
|
|
keepGoing = 1
|
|
count = 0
|
|
|
|
while keepGoing and count < max:
|
|
count += 1
|
|
wx.MilliSleep(30)
|
|
|
|
if count >= max // 2:
|
|
keepGoing = dlg.UpdatePulse("Half-time!")
|
|
else:
|
|
keepGoing = dlg.UpdatePulse()
|
|
|
|
dlg.Destroy()
|
|
|
|
app.MainLoop()
|
|
|
|
|
|
|
|
Supported Platforms
|
|
===================
|
|
|
|
:class:`PyProgress` has been tested on the following platforms:
|
|
* Windows (Windows XP);
|
|
* Linux Ubuntu (Dapper 6.06)
|
|
|
|
|
|
Window Styles
|
|
=============
|
|
|
|
This class supports the following window styles:
|
|
|
|
=================== =========== ==================================================
|
|
Window Styles Hex Value Description
|
|
=================== =========== ==================================================
|
|
``PD_CAN_ABORT`` 0x1 This flag tells the dialog that it should have a ``Cancel`` button which the user may press. If this happens, the next call to `Update` will return ``False``.
|
|
``PD_APP_MODAL`` 0x2 Make the progress dialog modal. If this flag is not given, it is only 'locally' modal - that is the input to the parent window is disabled, but not to the other ones.
|
|
``PD_AUTO_HIDE`` 0x4 Causes the progress dialog to disappear from screen as soon as the maximum value of the progress meter has been reached.
|
|
``PD_ELAPSED_TIME`` 0x8 This flag tells the dialog that it should show elapsed time (since creating the dialog).
|
|
=================== =========== ==================================================
|
|
|
|
|
|
Events Processing
|
|
=================
|
|
|
|
`No custom events are available for this class.`
|
|
|
|
|
|
License And Version
|
|
===================
|
|
|
|
:class:`PyProgress` is distributed under the wxPython license.
|
|
|
|
Latest Revision: Andrea Gavana @ 19 Dec 2012, 21.00 GMT
|
|
|
|
Version 0.5
|
|
|
|
"""
|
|
|
|
import wx
|
|
import time
|
|
|
|
# Some constants, taken straight from wx.ProgressDialog
|
|
Uncancelable = -1
|
|
""" Classifies the :class:`PyProgress` as "uncancelable". """
|
|
Canceled = 0
|
|
""" :class:`PyProgress` has been canceled. """
|
|
Continue = 1
|
|
""" :class:`PyProgress` can continue. """
|
|
Finished = 2
|
|
""" :class:`PyProgress` has finished. """
|
|
|
|
# Margins between gauge and text/button
|
|
LAYOUT_MARGIN = 8
|
|
""" Margins between gauge and text/button (in pixels). """
|
|
|
|
# PyProgress styles
|
|
PD_CAN_ABORT = wx.PD_CAN_ABORT
|
|
""" This flag tells the dialog that it should have a "Cancel" button which the user may press. If this happens, the next call to `Update()` will return ``False``. """
|
|
PD_APP_MODAL = wx.PD_APP_MODAL
|
|
""" Make the progress dialog modal. If this flag is not given, it is only 'locally' modal - that is the input to the parent window is disabled, but not to the other ones. """
|
|
PD_AUTO_HIDE = wx.PD_AUTO_HIDE
|
|
""" Causes the progress dialog to disappear from screen as soon as the maximum value of the progress meter has been reached. """
|
|
PD_ELAPSED_TIME = wx.PD_ELAPSED_TIME
|
|
""" This flag tells the dialog that it should show elapsed time (since creating the dialog). """
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class ProgressGauge
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class ProgressGauge(wx.Window):
|
|
""" This class provides a visual alternative for :class:`Gauge`."""
|
|
|
|
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
|
|
size=(-1,30)):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `parent`: parent window. Must not be ``None``;
|
|
:param `id`: window identifier. A value of -1 indicates a default value;
|
|
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `size`: the control size. A value of (-1, -1) indicates a default size,
|
|
chosen by either the windowing system or wxPython, depending on platform.
|
|
"""
|
|
|
|
wx.Window.__init__(self, parent, id, pos, size, style=wx.SUNKEN_BORDER)
|
|
|
|
self._value = 0
|
|
self._steps = 50
|
|
self._pos = 0
|
|
self._current = 0
|
|
self._gaugeproportion = 0.2
|
|
self._firstGradient = wx.WHITE
|
|
self._secondGradient = wx.BLUE
|
|
self._background = wx.Brush(wx.WHITE)
|
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
|
|
|
|
|
|
def GetFirstGradientColour(self):
|
|
""" Returns the first gradient colour. """
|
|
|
|
return self._firstGradient
|
|
|
|
|
|
def SetFirstGradientColour(self, colour):
|
|
"""
|
|
Sets the first gradient colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object.
|
|
"""
|
|
|
|
self._firstGradient = colour
|
|
self.Refresh()
|
|
|
|
|
|
def GetSecondGradientColour(self):
|
|
""" Returns the second gradient colour. """
|
|
|
|
return self._secondGradient
|
|
|
|
|
|
def SetSecondGradientColour(self, colour):
|
|
"""
|
|
Sets the second gradient colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object.
|
|
"""
|
|
|
|
self._secondGradient = colour
|
|
self.Refresh()
|
|
|
|
|
|
def GetGaugeBackground(self):
|
|
""" Returns the gauge background colour. """
|
|
|
|
return self._background
|
|
|
|
|
|
def SetGaugeBackground(self, colour):
|
|
"""
|
|
Sets the gauge background colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object.
|
|
"""
|
|
|
|
self._background = wx.Brush(colour)
|
|
|
|
|
|
def SetGaugeSteps(self, steps):
|
|
"""
|
|
Sets the number of steps the gauge performs before switching from
|
|
forward to backward (or vice-versa) movement.
|
|
|
|
:param `steps`: the number of steps the gauge performs before switching from
|
|
forward to backward (or vice-versa) movement.
|
|
"""
|
|
|
|
if steps <= 0:
|
|
raise Exception("ERROR:\n Gauge steps must be greater than zero. ")
|
|
|
|
if steps != self._steps:
|
|
self._steps = steps
|
|
|
|
|
|
def GetGaugeSteps(self):
|
|
"""
|
|
Returns the number of steps the gauge performs before switching from
|
|
forward to backward (or vice-versa) movement.
|
|
"""
|
|
|
|
return self._steps
|
|
|
|
|
|
def GetGaugeProportion(self):
|
|
"""
|
|
Returns the relative proportion between the sliding bar and the
|
|
whole gauge.
|
|
"""
|
|
|
|
return self._gaugeproportion
|
|
|
|
|
|
def SetGaugeProportion(self, proportion):
|
|
"""
|
|
Sets the relative proportion between the sliding bar and the
|
|
whole gauge.
|
|
|
|
:param `proportion`: a floating point number representing the relative
|
|
proportion between the sliding bar and the whole gauge.
|
|
"""
|
|
|
|
if proportion <= 0 or proportion >= 1:
|
|
raise Exception("ERROR:\n Gauge proportion must be between 0 and 1. ")
|
|
|
|
if proportion != self._gaugeproportion:
|
|
self._gaugeproportion = proportion
|
|
|
|
|
|
def OnEraseBackground(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`ProgressGauge`.
|
|
|
|
:param `event`: a :class:`EraseEvent` event to be processed.
|
|
|
|
:note: This method is intentionally empty to reduce flicker.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
def OnPaint(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_PAINT`` event for :class:`ProgressGauge`.
|
|
|
|
:param `event`: a :class:`PaintEvent` event to be processed.
|
|
"""
|
|
|
|
dc = wx.BufferedPaintDC(self)
|
|
dc.SetBackground(self._background)
|
|
|
|
dc.Clear()
|
|
|
|
xsize, ysize = self.GetClientSize()
|
|
interval = xsize/float(self._steps)
|
|
|
|
self._pos = interval*self._value
|
|
|
|
status = self._current//(self._steps - int(self._gaugeproportion*xsize)//int(interval))
|
|
|
|
if status%2 == 0:
|
|
increment = 1
|
|
else:
|
|
increment = -1
|
|
|
|
self._value = self._value + increment
|
|
self._current = self._current + 1
|
|
self.DrawProgress(dc, xsize, ysize, increment)
|
|
|
|
|
|
def DrawProgress(self, dc, xsize, ysize, increment):
|
|
"""
|
|
Actually draws the sliding bar.
|
|
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `xsize`: the width of the whole progress bar;
|
|
:param `ysize`: the height of the whole progress bar;
|
|
:param `increment`: a positive value if we are spinning from left to right,
|
|
a negative one if we are spinning from right to left.
|
|
"""
|
|
|
|
if increment > 0:
|
|
col1 = self.GetFirstGradientColour()
|
|
col2 = self.GetSecondGradientColour()
|
|
else:
|
|
col1 = self.GetSecondGradientColour()
|
|
col2 = self.GetFirstGradientColour()
|
|
|
|
interval = self._gaugeproportion*xsize
|
|
|
|
r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
|
|
r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
|
|
|
|
rstep = float((r2 - r1)) / interval
|
|
gstep = float((g2 - g1)) / interval
|
|
bstep = float((b2 - b1)) / interval
|
|
|
|
rf, gf, bf = 0, 0, 0
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
|
|
for ii in range(int(self._pos), int(self._pos+interval)):
|
|
currCol = (r1 + rf, g1 + gf, b1 + bf)
|
|
dc.SetPen(wx.Pen(currCol, 2))
|
|
dc.DrawLine(ii, 1, ii, ysize-2)
|
|
rf = rf + rstep
|
|
gf = gf + gstep
|
|
bf = bf + bstep
|
|
|
|
|
|
def Update(self):
|
|
""" Updates the gauge with a new value. """
|
|
|
|
self.Refresh()
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class PyProgress
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class PyProgress(wx.Dialog):
|
|
"""
|
|
:class:`PyProgress` is similar to :class:`ProgressDialog` in indeterminated mode, but with a
|
|
different gauge appearance and a different spinning behavior. The moving gauge
|
|
can be drawn with a single solid colour or with a shading gradient foreground.
|
|
The gauge background colour is user customizable.
|
|
The bar does not move always from the beginning to the end as in :class:`ProgressDialog`
|
|
in indeterminated mode, but spins cyclically forward and backward.
|
|
"""
|
|
|
|
def __init__(self, parent=None, id=-1, title="", message="",
|
|
agwStyle=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `parent`: parent window;
|
|
:param `id`: window identifier. A value of -1 indicates a default value;
|
|
:param `title`: dialog title to show in titlebar;
|
|
:param `message`: message displayed above the progress bar;
|
|
:param `style`: the dialog style. This can be a combination of the following bits:
|
|
|
|
=================== =========== ==================================================
|
|
Window Styles Hex Value Description
|
|
=================== =========== ==================================================
|
|
``PD_CAN_ABORT`` 0x1 This flag tells the dialog that it should have a ``Cancel`` button which the user may press. If this happens, the next call to `UpdatePulse` will return ``False``.
|
|
``PD_APP_MODAL`` 0x2 Make the progress dialog modal. If this flag is not given, it is only 'locally' modal - that is the input to the parent window is disabled, but not to the other ones.
|
|
``PD_AUTO_HIDE`` 0x4 Causes the progress dialog to disappear from screen as soon as the maximum value of the progress meter has been reached.
|
|
``PD_ELAPSED_TIME`` 0x8 This flag tells the dialog that it should show elapsed time (since creating the dialog).
|
|
=================== =========== ==================================================
|
|
|
|
"""
|
|
|
|
wx.Dialog.__init__(self, parent, id, title)
|
|
|
|
self._delay = 3
|
|
self._hasAbortButton = False
|
|
|
|
# we may disappear at any moment, let the others know about it
|
|
self.SetExtraStyle(self.GetExtraStyle()|wx.WS_EX_TRANSIENT)
|
|
|
|
self._hasAbortButton = (agwStyle & wx.PD_CAN_ABORT)
|
|
|
|
if wx.Platform == "__WXMSW__":
|
|
# we have to remove the "Close" button from the title bar then as it is
|
|
# confusing to have it - it doesn't work anyhow
|
|
# FIXME: should probably have a (extended?) window style for this
|
|
if not self._hasAbortButton:
|
|
self.EnableClose(False)
|
|
|
|
self._state = (self._hasAbortButton and [Continue] or [Uncancelable])[0]
|
|
self._parentTop = wx.GetTopLevelParent(parent)
|
|
|
|
dc = wx.ClientDC(self)
|
|
dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT))
|
|
widthText, dummy = dc.GetTextExtent(message)
|
|
|
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
self._msg = wx.StaticText(self, wx.ID_ANY, message)
|
|
sizer.Add(self._msg, 0, wx.LEFT|wx.TOP, 2*LAYOUT_MARGIN)
|
|
|
|
sizeDlg = wx.Size()
|
|
sizeLabel = self._msg.GetSize()
|
|
sizeDlg.y = 2*LAYOUT_MARGIN + sizeLabel.y
|
|
|
|
self._gauge = ProgressGauge(self, -1)
|
|
|
|
sizer.Add(self._gauge, 0, wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND, 2*LAYOUT_MARGIN)
|
|
|
|
sizeGauge = self._gauge.GetSize()
|
|
sizeDlg.y += 2*LAYOUT_MARGIN + sizeGauge.y
|
|
|
|
# create the estimated/remaining/total time zones if requested
|
|
self._elapsed = None
|
|
self._display_estimated = self._last_timeupdate = self._break = 0
|
|
self._ctdelay = 0
|
|
|
|
label = None
|
|
|
|
nTimeLabels = 0
|
|
|
|
if agwStyle & wx.PD_ELAPSED_TIME:
|
|
|
|
nTimeLabels += 1
|
|
self._elapsed = self.CreateLabel("Elapsed time : ", sizer)
|
|
|
|
if nTimeLabels > 0:
|
|
|
|
label = wx.StaticText(self, -1, "")
|
|
# set it to the current time
|
|
self._timeStart = int(time.time())
|
|
sizeDlg.y += nTimeLabels*(label.GetSize().y + LAYOUT_MARGIN)
|
|
label.Destroy()
|
|
|
|
sizeDlgModified = False
|
|
|
|
if wx.Platform == "__WXMSW__":
|
|
sizerFlags = wx.ALIGN_RIGHT|wx.ALL
|
|
else:
|
|
sizerFlags = wx.ALIGN_CENTER_HORIZONTAL|wx.BOTTOM|wx.TOP
|
|
|
|
if self._hasAbortButton:
|
|
buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
self._btnAbort = wx.Button(self, -1, "Cancel")
|
|
self._btnAbort.Bind(wx.EVT_BUTTON, self.OnCancel)
|
|
|
|
# Windows dialogs usually have buttons in the lower right corner
|
|
buttonSizer.Add(self._btnAbort, 0, sizerFlags, LAYOUT_MARGIN)
|
|
|
|
if not sizeDlgModified:
|
|
sizeDlg.y += 2*LAYOUT_MARGIN + wx.Button.GetDefaultSize().y
|
|
|
|
if self._hasAbortButton:
|
|
sizer.Add(buttonSizer, 0, sizerFlags, LAYOUT_MARGIN )
|
|
|
|
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
|
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
|
|
|
|
self._agwStyle = agwStyle
|
|
|
|
self.SetSizerAndFit(sizer)
|
|
|
|
sizeDlg.y += 2*LAYOUT_MARGIN
|
|
|
|
# try to make the dialog not square but rectangular of reasonable width
|
|
sizeDlg.x = max(widthText, 4*sizeDlg.y//3)
|
|
sizeDlg.x *= 3
|
|
sizeDlg.x //= 2
|
|
self.SetClientSize(sizeDlg)
|
|
|
|
self.Centre(wx.CENTER|wx.BOTH)
|
|
|
|
if agwStyle & wx.PD_APP_MODAL:
|
|
self._winDisabler = wx.WindowDisabler(self)
|
|
else:
|
|
if self._parentTop:
|
|
self._parentTop.Disable()
|
|
self._winDisabler = None
|
|
|
|
self.ShowDialog()
|
|
self.Enable()
|
|
|
|
# this one can be initialized even if the others are unknown for now
|
|
# NB: do it after calling Layout() to keep the labels correctly aligned
|
|
if self._elapsed:
|
|
self.SetTimeLabel(0, self._elapsed)
|
|
|
|
self.evtloop = None
|
|
|
|
# This is causing the unittests to hang, investigate it later.
|
|
#if not wx.EventLoopBase.GetActive():
|
|
# self.evtloop = wx.GetApp().GetTraits().CreateEventLoop()
|
|
# wx.EventLoopBase.SetActive(self.evtloop)
|
|
|
|
self.Update()
|
|
|
|
|
|
def CreateLabel(self, text, sizer):
|
|
"""
|
|
Creates the :class:`StaticText` that holds the elapsed time label.
|
|
|
|
:param `text`: the dialog message to be displayed above the gauge;
|
|
:param `sizer`: the main :class:`BoxSizer` for :class:`PyProgress`.
|
|
"""
|
|
|
|
locsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
dummy = wx.StaticText(self, wx.ID_ANY, text)
|
|
label = wx.StaticText(self, wx.ID_ANY, "unknown")
|
|
|
|
if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
|
|
# label and time centered in one row
|
|
locsizer.Add(dummy, 1, wx.ALIGN_LEFT)
|
|
locsizer.Add(label, 1, wx.ALIGN_LEFT|wx.LEFT, LAYOUT_MARGIN)
|
|
sizer.Add(locsizer, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.TOP, LAYOUT_MARGIN)
|
|
else:
|
|
# label and time to the right in one row
|
|
sizer.Add(locsizer, 0, wx.ALIGN_RIGHT|wx.RIGHT|wx.TOP, LAYOUT_MARGIN)
|
|
locsizer.Add(dummy)
|
|
locsizer.Add(label, 0, wx.LEFT, LAYOUT_MARGIN)
|
|
|
|
return label
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# wxProgressDialog operations
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def UpdatePulse(self, newmsg=""):
|
|
"""
|
|
Updates the dialog, setting the progress bar to the new value and, if given
|
|
changes the message above it. Returns ``True`` unless the ``Cancel`` button
|
|
has been pressed.
|
|
|
|
If ``False`` is returned, the application can either immediately destroy the
|
|
dialog or ask the user for the confirmation.
|
|
|
|
:param `newmsg`: The new messages for the progress dialog text, if it is
|
|
empty (which is the default) the message is not changed.
|
|
"""
|
|
|
|
self._gauge.Update()
|
|
|
|
if newmsg and newmsg != self._msg.GetLabel():
|
|
self._msg.SetLabel(newmsg)
|
|
wx.SafeYield()
|
|
|
|
if self._elapsed:
|
|
elapsed = int(time.time()) - self._timeStart
|
|
if self._last_timeupdate < elapsed:
|
|
self._last_timeupdate = elapsed
|
|
|
|
self.SetTimeLabel(elapsed, self._elapsed)
|
|
|
|
if self._state == Finished:
|
|
|
|
if not self._agwStyle & wx.PD_AUTO_HIDE:
|
|
|
|
self.EnableClose()
|
|
|
|
if newmsg == "":
|
|
# also provide the finishing message if the application didn't
|
|
self._msg.SetLabel("Done.")
|
|
|
|
wx.SafeYield()
|
|
self.ShowModal()
|
|
return False
|
|
|
|
else:
|
|
# reenable other windows before hiding this one because otherwise
|
|
# Windows wouldn't give the focus back to the window which had
|
|
# been previously focused because it would still be disabled
|
|
self.ReenableOtherWindows()
|
|
self.Hide()
|
|
|
|
# we have to yield because not only we want to update the display but
|
|
# also to process the clicks on the cancel and skip buttons
|
|
wx.SafeYield()
|
|
|
|
return self._state != Canceled
|
|
|
|
|
|
def GetFirstGradientColour(self):
|
|
""" Returns the gauge first gradient colour. """
|
|
|
|
return self._gauge.GetFirstGradientColour()
|
|
|
|
|
|
def SetFirstGradientColour(self, colour):
|
|
"""
|
|
Sets the gauge first gradient colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object.
|
|
"""
|
|
|
|
self._gauge.SetFirstGradientColour(colour)
|
|
|
|
|
|
def GetSecondGradientColour(self):
|
|
""" Returns the gauge second gradient colour. """
|
|
|
|
return self._gauge.GetSecondGradientColour()
|
|
|
|
|
|
def SetSecondGradientColour(self, colour):
|
|
"""
|
|
Sets the gauge second gradient colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object.
|
|
"""
|
|
|
|
self._gauge.SetSecondGradientColour(colour)
|
|
|
|
|
|
def GetGaugeBackground(self):
|
|
""" Returns the gauge background colour. """
|
|
|
|
return self._gauge.GetGaugeBackground()
|
|
|
|
|
|
def SetGaugeBackground(self, colour):
|
|
"""
|
|
Sets the gauge background colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object.
|
|
"""
|
|
|
|
self._gauge.SetGaugeBackground(colour)
|
|
|
|
|
|
def SetGaugeSteps(self, steps):
|
|
"""
|
|
Sets the number of steps the gauge performs before switching from
|
|
forward to backward (or vice-versa) movement.
|
|
|
|
:param `steps`: the number of steps the gauge performs before switching from
|
|
forward to backward (or vice-versa) movement.
|
|
"""
|
|
|
|
self._gauge.SetGaugeSteps(steps)
|
|
|
|
|
|
def GetGaugeSteps(self):
|
|
"""
|
|
Returns the number of steps the gauge performs before switching from
|
|
forward to backward (or vice-versa) movement.
|
|
"""
|
|
|
|
return self._gauge.GetGaugeSteps()
|
|
|
|
|
|
def GetGaugeProportion(self):
|
|
"""
|
|
Returns the relative proportion between the sliding bar and the
|
|
whole gauge.
|
|
"""
|
|
|
|
return self._gauge.GetGaugeProportion()
|
|
|
|
|
|
def SetGaugeProportion(self, proportion):
|
|
"""
|
|
Sets the relative proportion between the sliding bar and the
|
|
whole gauge.
|
|
|
|
:param `proportion`: a floating point number representing the relative
|
|
proportion between the sliding bar and the whole gauge.
|
|
"""
|
|
|
|
self._gauge.SetGaugeProportion(proportion)
|
|
|
|
|
|
def ShowDialog(self, show=True):
|
|
"""
|
|
Show the dialog.
|
|
|
|
:param `show`: ``True`` to show the dialog and re-enable all the other windows,
|
|
``False`` otherwise.
|
|
"""
|
|
|
|
# reenable other windows before hiding this one because otherwise
|
|
# Windows wouldn't give the focus back to the window which had
|
|
# been previously focused because it would still be disabled
|
|
if not show:
|
|
self.ReenableOtherWindows()
|
|
|
|
return self.Show()
|
|
|
|
|
|
def GetAGWWindowStyleFlag(self):
|
|
"""
|
|
Returns the :class:`PyProgress` style.
|
|
|
|
:see: The :meth:`~PyProgress.__init__` method for a list of possible style flags.
|
|
"""
|
|
|
|
return self._agwStyle
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# event handlers
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def OnCancel(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_BUTTON`` event for the dialog.
|
|
|
|
:param `event`: a :class:`CommandEvent` event to be processed.
|
|
|
|
:note: This method handles the ``Cancel`` button press.
|
|
"""
|
|
|
|
if self._state == Finished:
|
|
|
|
# this means that the count down is already finished and we're being
|
|
# shown as a modal dialog - so just let the default handler do the job
|
|
event.Skip()
|
|
|
|
else:
|
|
|
|
# request to cancel was received, the next time Update() is called we
|
|
# will handle it
|
|
self._state = Canceled
|
|
|
|
# update the buttons state immediately so that the user knows that the
|
|
# request has been noticed
|
|
self.DisableAbort()
|
|
|
|
# save the time when the dialog was stopped
|
|
self._timeStop = int(time.time())
|
|
|
|
self.ReenableOtherWindows()
|
|
|
|
|
|
def OnDestroy(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_WINDOW_DESTROY`` event for :class:`PyProgress`.
|
|
|
|
:param `event`: a :class:`WindowDestroyEvent` event to be processed.
|
|
"""
|
|
if self.evtloop:
|
|
wx.EventLoopBase.SetActive(None)
|
|
self.ReenableOtherWindows()
|
|
event.Skip()
|
|
|
|
|
|
def OnClose(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_CLOSE`` event for :class:`PyProgress`.
|
|
|
|
:param `event`: a :class:`CloseEvent` event to be processed.
|
|
"""
|
|
|
|
if self._state == Uncancelable:
|
|
|
|
# can't close this dialog
|
|
event.Veto()
|
|
|
|
elif self._state == Finished:
|
|
|
|
# let the default handler close the window as we already terminated
|
|
self.Hide()
|
|
event.Skip()
|
|
|
|
else:
|
|
|
|
# next Update() will notice it
|
|
self._state = Canceled
|
|
self.DisableAbort()
|
|
|
|
self._timeStop = int(time.time())
|
|
|
|
|
|
def ReenableOtherWindows(self):
|
|
""" Re-enables the other windows if using :class:`WindowDisabler`. """
|
|
|
|
if self._agwStyle & wx.PD_APP_MODAL:
|
|
if hasattr(self, "_winDisabler"):
|
|
del self._winDisabler
|
|
|
|
else:
|
|
|
|
if self._parentTop:
|
|
self._parentTop.Enable()
|
|
|
|
|
|
def SetTimeLabel(self, val, label=None):
|
|
"""
|
|
Sets the elapsed time label.
|
|
|
|
:param `val`: the elapsed time since the dialog was shown, in seconds;
|
|
:param `label`: the new message to display in the elapsed time text.
|
|
"""
|
|
|
|
if label:
|
|
|
|
hours = val//3600
|
|
minutes = (val%3600)//60
|
|
seconds = val%60
|
|
strs = ("%lu:%02lu:%02lu")%(hours, minutes, seconds)
|
|
|
|
if strs != label.GetLabel():
|
|
label.SetLabel(strs)
|
|
|
|
|
|
def EnableAbort(self, enable=True):
|
|
"""
|
|
Enables or disables the ``Cancel`` button.
|
|
|
|
:param `enable`: ``True`` to enable the ``Cancel`` button, ``False`` to disable it.
|
|
"""
|
|
|
|
if self._hasAbortButton:
|
|
if self._btnAbort:
|
|
self._btnAbort.Enable(enable)
|
|
|
|
|
|
def EnableClose(self, enable=True):
|
|
"""
|
|
Enables or disables the ``Close`` button.
|
|
|
|
:param `enable`: ``True`` to enable the ``Close`` button, ``False`` to disable it.
|
|
"""
|
|
|
|
if self._hasAbortButton:
|
|
if self._btnAbort:
|
|
self._btnAbort.Enable(enable)
|
|
self._btnAbort.SetLabel("Close")
|
|
self._btnAbort.Bind(wx.EVT_BUTTON, self.OnClose)
|
|
|
|
|
|
def DisableAbort(self):
|
|
""" Disables the ``Cancel`` button. """
|
|
|
|
self.EnableAbort(False)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
import wx
|
|
|
|
# Our normal wxApp-derived class, as usual
|
|
app = wx.App(0)
|
|
|
|
dlg = PyProgress(None, -1, "PyProgress Example",
|
|
"An Informative Message",
|
|
agwStyle=wx.PD_APP_MODAL|wx.PD_ELAPSED_TIME)
|
|
|
|
dlg.SetGaugeProportion(0.2)
|
|
dlg.SetGaugeSteps(50)
|
|
dlg.SetGaugeBackground(wx.WHITE)
|
|
dlg.SetFirstGradientColour(wx.WHITE)
|
|
dlg.SetSecondGradientColour(wx.BLUE)
|
|
|
|
max = 400
|
|
keepGoing = True
|
|
count = 0
|
|
|
|
while keepGoing and count < max:
|
|
count += 1
|
|
wx.MilliSleep(30)
|
|
|
|
if count >= max // 2:
|
|
keepGoing = dlg.UpdatePulse("Half-time!")
|
|
else:
|
|
keepGoing = dlg.UpdatePulse()
|
|
|
|
dlg.Destroy()
|
|
|
|
app.MainLoop()
|