# 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