📄 helpers.py
字号:
# AnalogClock's base classes
# E. A. Tacao <e.a.tacao |at| estadao.com.br>
# http://j.domaindlx.com/elements28/wxpython/
# 15 Fev 2006, 22:00 GMT-03:00
# Distributed under the wxWidgets license.
from time import strftime, localtime
import math
import wx
from styles import *
#----------------------------------------------------------------------
_targets = [HOUR, MINUTE, SECOND]
#----------------------------------------------------------------------
class Element:
"""Base class for face, hands and tick marks."""
def __init__(self, idx=0, pos=None, size=None, offset=0, clocksize=None,
scale=1, rotate=False, kind=""):
self.idx = idx
self.pos = pos
self.size = size
self.offset = offset
self.clocksize = clocksize
self.scale = scale
self.rotate = rotate
self.kind = kind
self.text = None
self.angfac = [6, 30][self.kind == "hours"]
def _pol2rect(self, m, t):
return m * math.cos(math.radians(t)), m * math.sin(math.radians(t))
def _rect2pol(self, x, y):
return math.hypot(x, y), math.degrees(math.atan2(y, x))
def DrawRotated(self, dc, offset=0):
pass
def DrawStraight(self, dc, offset=0):
pass
def Draw(self, dc, offset=0):
if self.rotate:
self.DrawRotated(dc, offset)
else:
self.DrawStraight(dc, offset)
def RecalcCoords(self, clocksize, centre, scale):
pass
def GetSize(self):
return self.size
def GetOffset(self):
return self.offset
def GetIsRotated(self, rotate):
return self.rotate
def GetMaxSize(self, scale=1):
return self.size * scale
def GetScale(self):
return self.scale
def SetIsRotated(self, rotate):
self.rotate = rotate
def GetMaxSize(self, scale=1):
return self.size * scale
def GetPolygon(self):
return self.polygon
def SetPosition(self, pos):
self.pos = pos
def SetSize(self, size):
self.size = size
def SetOffset(self, offset):
self.offset = offset
def SetClockSize(self, clocksize):
self.clocksize = clocksize
def SetScale(self, scale):
self.scale = scale
def SetIsRotated(self, rotate):
self.rotate = rotate
def SetPolygon(self, polygon):
self.polygon = polygon
#----------------------------------------------------------------------
class ElementWithDyer(Element):
"""Base class for clock face and hands."""
def __init__(self, **kwargs):
self.dyer = kwargs.pop("dyer", Dyer())
Element.__init__(self, **kwargs)
def GetFillColour(self):
return self.dyer.GetFillColour()
def GetBorderColour(self):
return self.dyer.GetBorderColour()
def GetBorderWidth(self):
return self.dyer.GetBorderWidth()
def GetShadowColour(self):
return self.dyer.GetShadowColour()
def SetFillColour(self, colour):
self.dyer.SetFillColour(colour)
def SetBorderColour(self, colour):
self.dyer.SetBorderColour(colour)
def SetBorderWidth(self, width):
self.dyer.SetBorderWidth(width)
def SetShadowColour(self, colour):
self.dyer.SetShadowColour(colour)
#----------------------------------------------------------------------
class Face(ElementWithDyer):
"""Holds info about the clock face."""
def __init__(self, **kwargs):
ElementWithDyer.__init__(self, **kwargs)
def Draw(self, dc):
self.dyer.Select(dc)
dc.DrawCircle(self.pos.x, self.pos.y, self.radius)
def RecalcCoords(self, clocksize, centre, scale):
self.radius = min(clocksize.Get()) / 2. - self.dyer.width / 2.
self.pos = centre
#----------------------------------------------------------------------
class Hand(ElementWithDyer):
"""Holds info about a clock hand."""
def __init__(self, **kwargs):
self.lenfac = kwargs.pop("lenfac")
ElementWithDyer.__init__(self, **kwargs)
self.SetPolygon([[-1, 0], [0, -1], [1, 0], [0, 4]])
def Draw(self, dc, end, offset=0):
radius, centre, r = end
angle = math.degrees(r)
polygon = self.polygon[:]
vscale = radius / max([y for x, y in polygon])
for i, (x, y) in enumerate(polygon):
x *= self.scale * self.size
y *= vscale * self.lenfac
m, t = self._rect2pol(x, y)
polygon[i] = self._pol2rect(m, t - angle)
dc.DrawPolygon(polygon, centre.x + offset, centre.y + offset)
def RecalcCoords(self, clocksize, centre, scale):
self.pos = centre
self.scale = scale
#----------------------------------------------------------------------
class TickSquare(Element):
"""Holds info about a tick mark."""
def __init__(self, **kwargs):
Element.__init__(self, **kwargs)
def Draw(self, dc, offset=0):
width = height = self.size * self.scale
x = self.pos.x - width / 2.
y = self.pos.y - height / 2.
dc.DrawRectangle(x + offset, y + offset, width, height)
#----------------------------------------------------------------------
class TickCircle(Element):
"""Holds info about a tick mark."""
def __init__(self, **kwargs):
Element.__init__(self, **kwargs)
def Draw(self, dc, offset=0):
radius = self.size * self.scale / 2.
x = self.pos.x
y = self.pos.y
dc.DrawCircle(x + offset, y + offset, radius)
#----------------------------------------------------------------------
class TickPoly(Element):
"""Holds info about a tick mark."""
def __init__(self, **kwargs):
Element.__init__(self, **kwargs)
self.SetPolygon([[0, 1], [1, 0], [2, 1], [1, 5]])
def _calcPolygon(self):
width = max([x for x, y in self.polygon])
height = max([y for x, y in self.polygon])
tscale = self.size / max(width, height) * self.scale
polygon = [(x * tscale, y * tscale) for x, y in self.polygon]
width = max([x for x, y in polygon])
height = max([y for x, y in polygon])
return polygon, width, height
def DrawStraight(self, dc, offset=0):
polygon, width, height = self._calcPolygon()
x = self.pos.x - width / 2.
y = self.pos.y - height / 2.
dc.DrawPolygon(polygon, x + offset, y + offset)
def DrawRotated(self, dc, offset=0):
polygon, width, height = self._calcPolygon()
angle = 360 - self.angfac * (self.idx + 1)
r = math.radians(angle)
for i in range(len(polygon)):
m, t = self._rect2pol(*polygon[i])
t -= angle
polygon[i] = self._pol2rect(m, t)
x = self.pos.x - math.cos(r) * width / 2. - math.sin(r) * height / 2.
y = self.pos.y - math.cos(r) * height / 2. + math.sin(r) * width / 2.
dc.DrawPolygon(polygon, x + offset, y + offset)
#----------------------------------------------------------------------
class TickDecimal(Element):
"""Holds info about a tick mark."""
def __init__(self, **kwargs):
Element.__init__(self, **kwargs)
self.text = "%s" % (self.idx + 1)
def DrawStraight(self, dc, offset=0):
width, height = dc.GetTextExtent(self.text)
x = self.pos.x - width / 2.
y = self.pos.y - height / 2.
dc.DrawText(self.text, x + offset, y + offset)
def DrawRotated(self, dc, offset=0):
width, height = dc.GetTextExtent(self.text)
angle = 360 - self.angfac * (self.idx + 1)
r = math.radians(angle)
x = self.pos.x - math.cos(r) * width / 2. - math.sin(r) * height / 2.
y = self.pos.y - math.cos(r) * height / 2. + math.sin(r) * width / 2.
dc.DrawRotatedText(self.text, x + offset, y + offset, angle)
#----------------------------------------------------------------------
class TickRoman(TickDecimal):
"""Holds info about a tick mark."""
def __init__(self, **kwargs):
TickDecimal.__init__(self, **kwargs)
self.text = ["I","II","III","IV","V", \
"VI","VII","VIII","IX","X", \
"XI","XII","XIII","XIV","XV", \
"XVI","XVII","XVIII","XIX","XX", \
"XXI","XXII","XXIII","XXIV","XXV", \
"XXVI","XXVII","XXVIII","XXIX","XXX", \
"XXXI","XXXII","XXXIII","XXXIV","XXXV", \
"XXXVI","XXXVII","XXXVIII","XXXIX","XL", \
"XLI","XLII","XLIII","XLIV","XLV", \
"XLVI","XLVII","XLVIII","XLIX","L", \
"LI","LII","LIII","LIV","LV", \
"LVI","LVII","LVIII","LIX","LX"][self.idx]
#----------------------------------------------------------------------
class TickBinary(TickDecimal):
"""Holds info about a tick mark."""
def __init__(self, **kwargs):
TickDecimal.__init__(self, **kwargs)
def d2b(n, b=""):
while n > 0:
b = str(n % 2) + b; n = n >> 1
return b.zfill(4)
self.text = d2b(self.idx + 1)
#----------------------------------------------------------------------
class TickHex(TickDecimal):
"""Holds info about a tick mark."""
def __init__(self, **kwargs):
TickDecimal.__init__(self, **kwargs)
self.text = hex(self.idx + 1)[2:].upper()
#----------------------------------------------------------------------
class TickNone(Element):
"""Holds info about a tick mark."""
def __init__(self, **kwargs):
Element.__init__(self, **kwargs)
def Draw(self, dc, offset=0):
pass
#----------------------------------------------------------------------
class Dyer:
"""Stores info about colours and borders of clock Elements."""
def __init__(self, border=None, width=0, fill=None, shadow=None):
"""
self.border (wx.Colour) border colour
self.width (int) border width
self.fill (wx.Colour) fill colour
self.shadow (wx.Colour) shadow colour
"""
self.border = border or \
wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
self.fill = fill or \
wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
self.shadow = shadow or \
wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW)
self.width = width
def Select(self, dc, shadow=False):
"""Selects the current settings into the dc."""
if not shadow:
dc.SetPen(wx.Pen(self.border, self.width, wx.SOLID))
dc.SetBrush(wx.Brush(self.fill, wx.SOLID))
dc.SetTextForeground(self.fill)
else:
dc.SetPen(wx.Pen(self.shadow, self.width, wx.SOLID))
dc.SetBrush(wx.Brush(self.shadow, wx.SOLID))
dc.SetTextForeground(self.shadow)
def GetFillColour(self):
return self.fill
def GetBorderColour(self):
return self.border
def GetBorderWidth(self):
return self.width
def GetShadowColour(self):
return self.shadow
def SetFillColour(self, colour):
self.fill = colour
def SetBorderColour(self, colour):
self.border = colour
def SetBorderWidth(self, width):
self.width = width
def SetShadowColour(self, colour):
self.shadow = colour
#----------------------------------------------------------------------
class HandSet:
"""Manages the set of hands."""
def __init__(self, parent, h, m, s):
self.parent = parent
self.hands = [h, m, s]
self.radius = 1
self.centre = wx.Point(1, 1)
def _draw(self, dc, shadow=False):
ends = [int(x) for x in strftime("%I %M %S", localtime()).split()]
flags = [self.parent.clockStyle & flag \
for flag in self.parent.allHandStyles]
a_hand = self.hands[0]
if shadow:
offset = self.parent.shadowOffset * a_hand.GetScale()
else:
offset = 0
for i, hand in enumerate(self.hands):
# Is this hand supposed to be drawn?
if flags[i]:
idx = ends[i]
# Is this the hours hand?
if i == 0:
idx = idx * 5 + ends[1] / 12 - 1
# else prevent exceptions on leap seconds
elif idx <= 0 or idx > 60:
idx = 59
# and adjust idx offset for minutes and non-leap seconds
else:
idx = idx - 1
angle = math.radians(180 - 6 * (idx + 1))
hand.dyer.Select(dc, shadow)
hand.Draw(dc, (self.radius, self.centre, angle), offset)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -