X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5f3c9dc69ccb2fe33c9efd3aece87e9d65162994..caeac82e4d649027472b6696513d632d4370f4d4:/wxPython/wx/lib/analogclock/analogclock.py?ds=inline diff --git a/wxPython/wx/lib/analogclock/analogclock.py b/wxPython/wx/lib/analogclock/analogclock.py new file mode 100644 index 0000000000..0a0eeff32c --- /dev/null +++ b/wxPython/wx/lib/analogclock/analogclock.py @@ -0,0 +1,644 @@ +# AnalogClock's main class +# E. A. Tacao +# http://j.domaindlx.com/elements28/wxpython/ +# 15 Fev 2006, 22:00 GMT-03:00 +# Distributed under the wxWidgets license. +# +# For more info please see the __init__.py file. + +import wx + +from styles import * +from helpers import Dyer, Face, Hand, HandSet, TickSet, Box +from setup import Setup + +#---------------------------------------------------------------------- + +class AnalogClock(wx.PyWindow): + """An analog clock.""" + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.NO_BORDER, name="AnalogClock", + clockStyle=DEFAULT_CLOCK_STYLE, + minutesStyle=TICKS_CIRCLE, hoursStyle=TICKS_POLY): + + wx.PyWindow.__init__(self, parent, id, pos, size, style, name) + + # Base size for scale calc purposes. + self.basesize = wx.Size(348, 348) + + # Store some references. + self.clockStyle = clockStyle + self.minutesStyle = minutesStyle + self.hoursStyle = hoursStyle + + self.DrawHands = self._drawHands + self.DrawBox = self._drawBox + self.RecalcCoords = self._recalcCoords + + self.shadowOffset = 3 + + self.allHandStyles = [SHOW_HOURS_HAND, + SHOW_MINUTES_HAND, + SHOW_SECONDS_HAND] + + # Initialize clock face. + # + # By default we don't use colours or borders on the clock face. + bg = self.GetBackgroundColour() + face = Face(dyer=Dyer(bg, 0, bg)) + + # Initialize tick marks. + # + # TickSet is a set of tick marks; there's always two TickSets defined + # regardless whether they're being shown or not. + ticksM = TickSet(self, style=minutesStyle, size=5, kind="minutes") + ticksH = TickSet(self, style=hoursStyle, size=25, kind="hours", + rotate=clockStyle&ROTATE_TICKS) + + # Box holds the clock face and tick marks. + self.Box = Box(self, face, ticksM, ticksH) + + # Initialize hands. + # + # HandSet is the set of hands; there's always one HandSet defined + # regardless whether hands are being shown or not. + # + # A 'lenfac = 0.95', e.g., means that the lenght of that hand will + # be 95% of the maximum allowed hand lenght ('nice' maximum lenght). + handH = Hand(size=7, lenfac=0.7) + handM = Hand(size=5, lenfac=0.95) + handS = Hand(size=1, lenfac=0.95) + self.Hands = HandSet(self, handH, handM, handS) + + # Create the customization dialog. + self.Setup = None + + # Make a context menu. + popup1 = wx.NewId() + popup2 = wx.NewId() + cm = self.cm = wx.Menu() + cm.Append(popup1, "Customize...") + cm.Append(popup2, "About...") + + # Set event handlers. + self.Bind(wx.EVT_SIZE, self._OnSize) + self.Bind(wx.EVT_PAINT, self._OnPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, lambda evt: None) + self.Bind(wx.EVT_TIMER, self._OnTimer) + self.Bind(wx.EVT_WINDOW_DESTROY, self._OnDestroyWindow) + self.Bind(wx.EVT_CONTEXT_MENU, self._OnContextMenu) + self.Bind(wx.EVT_MENU, self._OnShowSetup, id=popup1) + self.Bind(wx.EVT_MENU, self._OnShowAbout, id=popup2) + + # Set initial size based on given size, or best size + self.SetBestFittingSize(size) + + # Do initial drawing (in case there is not an initial size event) + self.RecalcCoords(self.GetSize()) + self.DrawBox() + + # Initialize the timer that drives the update of the clock face. + # Update every half second to ensure that there is at least one true + # update during each realtime second. + self.timer = wx.Timer(self) + self.timer.Start(500) + + + def DoGetBestSize(self): + # Just pull a number out of the air. If there is a way to + # calculate this then it should be done... + size = wx.Size(50,50) + self.CacheBestSize(size) + return size + + + def _OnSize(self, evt): + size = self.GetClientSize() + if size.x < 1 or size.y < 1: + return + + self.RecalcCoords(size) + self.DrawBox() + + + def _OnPaint(self, evt): + dc = wx.BufferedPaintDC(self) + self.DrawHands(dc) + + + def _OnTimer(self, evt): + dc = wx.BufferedDC(wx.ClientDC(self), self.GetClientSize()) + self.DrawHands(dc) + + + def _OnDestroyWindow(self, evt): + self.timer.Stop() + del self.timer + + + def _OnContextMenu(self, evt): + self.PopupMenu(self.cm) + + + def _OnShowSetup(self, evt): + if self.Setup is None: + self.Setup = Setup(self) + self.Setup.Show() + self.Setup.Raise() + + + def _OnShowAbout(self, evt): + msg = "AnalogClock\n\n" \ + "by Several folks on wxPython-users\n" \ + "with enhancements from E. A. Tacao." + title = "About..." + style = wx.OK|wx.ICON_INFORMATION + + dlg = wx.MessageDialog(self, msg, title, style) + dlg.ShowModal() + dlg.Destroy() + + + def _recalcCoords(self, size): + """ + Recalculates all coordinates/geometry and inits the faceBitmap + to make sure the buffer is always the same size as the window. + """ + + self.faceBitmap = wx.EmptyBitmap(*size.Get()) + + # Recalc all coords. + scale = min([float(size.width) / self.basesize.width, + float(size.height) / self.basesize.height]) + + centre = wx.Point(size.width / 2., size.height / 2.) + + self.Box.RecalcCoords(size, centre, scale) + self.Hands.RecalcCoords(size, centre, scale) + + # Try to find a 'nice' maximum length for the hands so that they won't + # overlap the tick marks. OTOH, if you do want to allow overlapping the + # lenfac value (defined on __init__ above) has to be set to + # something > 1. + niceradius = self.Box.GetNiceRadiusForHands(centre) + self.Hands.SetMaxRadius(niceradius) + + + def _drawBox(self): + """Draws clock face and tick marks.""" + + dc = wx.BufferedDC(wx.ClientDC(self), self.GetClientSize()) + dc.BeginDrawing() + dc.SelectObject(self.faceBitmap) + dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID)) + dc.Clear() + self.Box.Draw(dc) + dc.EndDrawing() + + + def _drawHands(self, dc): + """ + Draws the face bitmap, created on the last DrawBox call, and + clock hands. + """ + + dc.BeginDrawing() + dc.DrawBitmap(self.faceBitmap, 0, 0) + self.Hands.Draw(dc) + dc.EndDrawing() + + + # Public methods -------------------------------------------------- + + def GetHandSize(self, target=ALL): + """Gets thickness of hands.""" + + return self.Hands.GetSize(target) + + + def GetHandFillColour(self, target=ALL): + """Gets fill colours of hands.""" + + return self.Hands.GetFillColour(target) + + + def GetHandBorderColour(self, target=ALL): + """Gets border colours of hands.""" + + return self.Hands.GetBorderColour(target) + + + def GetHandBorderWidth(self, target=ALL): + """Gets border widths of hands.""" + + return self.Hands.GetBorderWidth(target) + + + def GetTickSize(self, target=ALL): + """Gets sizes of ticks.""" + + return self.Box.GetTickSize(target) + + + + def GetTickFillColour(self, target=ALL): + """Gets fill colours of ticks.""" + + return self.Box.GetTickFillColour(target) + + + + def GetTickBorderColour(self, target=ALL): + """Gets border colours of ticks.""" + + return self.Box.GetTickBorderColour(target) + + + + def GetTickBorderWidth(self, target=ALL): + """Gets border widths of ticks.""" + + return self.Box.GetTickBorderWidth(target) + + + + def GetTickPolygon(self, target=ALL): + """ + Gets lists of points to be used as polygon shapes + when using the TICKS_POLY style. + """ + + return self.Box.GetTickPolygon(target) + + + + def GetTickFont(self, target=ALL): + """ + Gets fonts for tick marks when using TICKS_DECIMAL or + TICKS_ROMAN style. + """ + + return self.Box.GetTickFont(target) + + + + def GetTickOffset(self, target=ALL): + """Gets the distance of tick marks for hours from border.""" + + return self.Box.GetTickOffset(target) + + + + def GetFaceFillColour(self): + """Gets fill colours of watch.""" + + return self.Box.Face.GetFillColour() + + + + def GetFaceBorderColour(self): + """Gets border colours of watch.""" + + return self.Box.Face.GetBorderColour() + + + + def GetFaceBorderWidth(self): + """Gets border width of watch.""" + + return self.Box.Face.GetBorderWidth() + + + + def GetShadowColour(self): + """Gets the colour to be used to draw shadows.""" + + a_clock_part = self.Box + return a_clock_part.GetShadowColour() + + + + def GetClockStyle(self): + """Returns the current clock style.""" + + return self.clockStyle + + + def GetTickStyle(self, target=ALL): + """Gets the tick style(s).""" + + return self.Box.GetTickStyle(target) + + + def Refresh(self): + """ + Overriden base wx.Window method. Forces an immediate + recalculation and redraw of all clock elements. + """ + + size = self.GetClientSize() + if size.x < 1 or size.y < 1: + return + self.Freeze() + self.RecalcCoords(size) + self.DrawBox() + dc = wx.BufferedDC(wx.ClientDC(self), self.GetClientSize()) + self.DrawHands(dc) + self.Thaw() + + + def SetHandSize(self, size, target=ALL): + """Sets thickness of hands.""" + + self.Hands.SetSize(size, target) + + + def SetHandFillColour(self, colour, target=ALL): + """Sets fill colours of hands.""" + + self.Hands.SetFillColour(colour, target) + + + def SetHandBorderColour(self, colour, target=ALL): + """Sets border colours of hands.""" + + self.Hands.SetBorderColour(colour, target) + + + def SetHandBorderWidth(self, width, target=ALL): + """Sets border widths of hands.""" + + self.Hands.SetBorderWidth(width, target) + + + def SetTickSize(self, size, target=ALL): + """Sets sizes of ticks.""" + + self.Box.SetTickSize(size, target) + self.Refresh() + + + def SetTickFillColour(self, colour, target=ALL): + """Sets fill colours of ticks.""" + + self.Box.SetTickFillColour(colour, target) + self.Refresh() + + + def SetTickBorderColour(self, colour, target=ALL): + """Sets border colours of ticks.""" + + self.Box.SetTickBorderColour(colour, target) + self.Refresh() + + + def SetTickBorderWidth(self, width, target=ALL): + """Sets border widths of ticks.""" + + self.Box.SetTickBorderWidth(width, target) + self.Refresh() + + + def SetTickPolygon(self, polygon, target=ALL): + """ + Sets lists of points to be used as polygon shapes + when using the TICKS_POLY style. + """ + + self.Box.SetTickPolygon(polygon, target) + self.Refresh() + + + def SetTickFont(self, font, target=ALL): + """ + Sets fonts for tick marks when using text-based tick styles + such as TICKS_DECIMAL or TICKS_ROMAN. + """ + + self.Box.SetTickFont(font, target) + self.Refresh() + + + def SetTickOffset(self, offset, target=ALL): + """Sets the distance of tick marks for hours from border.""" + + self.Box.SetTickOffset(offset, target) + self.Refresh() + + + def SetFaceFillColour(self, colour): + """Sets fill colours of watch.""" + + self.Box.Face.SetFillColour(colour) + self.Refresh() + + + def SetFaceBorderColour(self, colour): + """Sets border colours of watch.""" + + self.Box.Face.SetBorderColour(colour) + self.Refresh() + + + def SetFaceBorderWidth(self, width): + """Sets border width of watch.""" + + self.Box.Face.SetBorderWidth(width) + self.Refresh() + + + def SetShadowColour(self, colour): + """Sets the colour to be used to draw shadows.""" + + self.Hands.SetShadowColour(colour) + self.Box.SetShadowColour(colour) + self.Refresh() + + + def SetClockStyle(self, style): + """ + Set the clock style, according to the options below. + + ==================== ================================ + SHOW_QUARTERS_TICKS Show marks for hours 3, 6, 9, 12 + SHOW_HOURS_TICKS Show marks for all hours + SHOW_MINUTES_TICKS Show marks for minutes + + SHOW_HOURS_HAND Show hours hand + SHOW_MINUTES_HAND Show minutes hand + SHOW_SECONDS_HAND Show seconds hand + + SHOW_SHADOWS Show hands and marks shadows + + ROTATE_TICKS Align tick marks to watch + OVERLAP_TICKS Draw tick marks for minutes even + when they match the hours marks. + ==================== ================================ + """ + + self.clockStyle = style + self.Box.SetIsRotated(style & ROTATE_TICKS) + self.Refresh() + + + def SetTickStyle(self, style, target=ALL): + """ + Set the tick style, according to the options below. + + ================= ====================================== + TICKS_NONE Don't show tick marks. + TICKS_SQUARE Use squares as tick marks. + TICKS_CIRCLE Use circles as tick marks. + TICKS_POLY Use a polygon as tick marks. A + polygon can be passed using + SetTickPolygon, otherwise the default + polygon will be used. + TICKS_DECIMAL Use decimal numbers as tick marks. + TICKS_ROMAN Use Roman numbers as tick marks. + TICKS_BINARY Use binary numbers as tick marks. + TICKS_HEX Use hexadecimal numbers as tick marks. + ================= ====================================== + """ + + self.Box.SetTickStyle(style, target) + self.Refresh() + + + def SetBackgroundColour(self, colour): + """Overriden base wx.Window method.""" + + wx.Window.SetBackgroundColour(self, colour) + self.Refresh() + + + def SetForegroundColour(self, colour): + """ + Overriden base wx.Window method. This method sets a colour for + all hands and ticks at once. + """ + + wx.Window.SetForegroundColour(self, colour) + self.SetHandFillColour(colour) + self.SetHandBorderColour(colour) + self.SetTickFillColour(colour) + self.SetTickBorderColour(colour) + self.Refresh() + + + def SetWindowStyle(self, *args, **kwargs): + """Overriden base wx.Window method.""" + + size = self.GetSize() + self.Freeze() + wx.Window.SetWindowStyle(self, *args, **kwargs) + self.SetSize((10, 10)) + self.SetSize(size) + self.Thaw() + + + def SetWindowStyleFlag(self, *args, **kwargs): + """Overriden base wx.Window method.""" + + self.SetWindowStyle(*args, **kwargs) + + +# For backwards compatibility ----------------------------------------- + +class AnalogClockWindow(AnalogClock): + """ + A simple derived class that provides some backwards compatibility + with the old analogclock module. + """ + def SetTickShapes(self, tsh, tsm=None): + self.SetTickPolygon(tsh) + + def SetHandWeights(self, h=None, m=None, s=None): + if h: + self.SetHandSize(h, HOUR) + if m: + self.SetHandSize(m, MINUTE) + if s: + self.SetHandSize(h, SECOND) + + def SetHandColours(self, h=None, m=None, s=None): + if h and not m and not s: + m=h + s=h + if h: + self.SetHandBorderColour(h, HOUR) + self.SetHandFillColour(h, HOUR) + if m: + self.SetHandBorderColour(m, MINUTE) + self.SetHandFillColour(m, MINUTE) + if s: + self.SetHandBorderColour(h, SECOND) + self.SetHandFillColour(h, SECOND) + + def SetTickColours(self, h=None, m=None): + if not m: + m=h + if h: + self.SetTickBorderColour(h, HOUR) + self.SetTickFillColour(h, HOUR) + if m: + self.SetTickBorderColour(m, MINUTE) + self.SetTickFillColour(m, MINUTE) + + def SetTickSizes(self, h=None, m=None): + if h: + self.SetTickSize(h, HOUR) + if m: + self.SetTickSize(h, MINUTE) + + def SetTickFontss(self, h=None, m=None): + if h: + self.SetTickFont(h, HOUR) + if m: + self.SetTickFont(h, MINUTE) + + + def SetMinutesOffset(self, o): + pass + + def SetShadowColour(self, s): + pass + + def SetWatchPenBrush(self, p=None, b=None): + if p: + self.SetFaceBorderColour(p.GetColour()) + self.SetFaceBorderWidth(p.GetWidth()) + if b: + self.SetFaceFillColour(b.GetColour()) + + def SetClockStyle(self, style): + style |= SHOW_HOURS_HAND|SHOW_MINUTES_HAND|SHOW_SECONDS_HAND + AnalogClock.SetClockStyle(self, style) + + def SetTickStyles(self, h=None, m=None): + if h: + self.SetTickStyle(h, HOUR) + if m: + self.SetTickStyle(h, MINUTE) + + +# Test stuff ---------------------------------------------------------- + +if __name__ == "__main__": + print wx.VERSION_STRING + + class AcDemoApp(wx.App): + def OnInit(self): + frame = wx.Frame(None, -1, "AnalogClock", size=(375, 375)) + clock = AnalogClock(frame) + frame.CentreOnScreen() + frame.Show() + return True + + acApp = AcDemoApp(0) + acApp.MainLoop() + + +# +## +### eof