python clock 时钟

image.png

--------------------------------windows.py-------------------------------
"""
###############################################################################
Classes that encapsulate top-level interfaces.
Allows same GUI to be main, pop-up, or attached; content classes may inherit
from these directly, or be mixed together with them per usage mode; may also
be called directly without a subclass; designed to be mixed in after (further
to the right than) app-specific classes: else, subclass gets methods here
(destroy, okayToQuit), instead of from app-specific classes--can't redefine.
###############################################################################
"""

import os, glob
from tkinter import Tk, Toplevel, Frame, YES, BOTH, RIDGE
from tkinter.messagebox import showinfo, askyesno

class _window:
"""
mixin shared by main and pop-up windows
"""
foundicon = None # shared by all inst
iconpatt = '*.ico' # may be reset
iconmine = 'py.ico'

def configBorders(self, app, kind, iconfile):
    if not iconfile:                                   # no icon passed?
        iconfile = self.findIcon()                     # try curr,tool dirs
    title = app
    if kind: title += ' - ' + kind
    self.title(title)                                  # on window border
    self.iconname(app)                                 # when minimized
    if iconfile:
        try:
            self.iconbitmap(iconfile)                  # window icon image
        except:                                        # bad py or platform
            pass
    self.protocol('WM_DELETE_WINDOW', self.quit)       # don't close silent

def findIcon(self):
    if _window.foundicon:                              # already found one?
        return _window.foundicon
    iconfile  = None                                   # try curr dir first
    iconshere = glob.glob(self.iconpatt)               # assume just one
    if iconshere:                                      # del icon for red Tk
        iconfile = iconshere[0]
    else:                                              # try tools dir icon
        mymod  = __import__(__name__)                  # import self for dir
        path   = __name__.split('.')                   # poss a package path
        for mod in path[1:]:                           # follow path to end
            mymod = getattr(mymod, mod)                # only have leftmost
        mydir  = os.path.dirname(mymod.__file__)
        myicon = os.path.join(mydir, self.iconmine)    # use myicon, not tk
        if os.path.exists(myicon): iconfile = myicon
    _window.foundicon = iconfile                       # don't search again
    return iconfile

class MainWindow(Tk, _window):
"""
when run in main top-level window
"""
def init(self, app, kind='', iconfile=None):
self.findIcon()
Tk.init(self)
self.__app = app
self.configBorders(app, kind, iconfile)

def quit(self):
    if self.okayToQuit():                                # threads running?
        if askyesno(self.__app, 'Verify Quit Program?'):
            self.destroy()                               # quit whole app
    else:
        showinfo(self.__app, 'Quit not allowed')         # or in okayToQuit?

def destroy(self):                                       # exit app silently
    Tk.quit(self)                                        # redef if exit ops

def okayToQuit(self):                                    # redef me if used
    return True                                          # e.g., thread busy

class PopupWindow(Toplevel, _window):
"""
when run in secondary pop-up window
"""
def init(self, app, kind='', iconfile=None):
Toplevel.init(self)
self.__app = app
self.configBorders(app, kind, iconfile)

def quit(self):                                        # redef me to change
    if askyesno(self.__app, 'Verify Quit Window?'):    # or call destroy
        self.destroy()                                 # quit this window

def destroy(self):                                     # close win silently
    Toplevel.destroy(self)                             # redef for close ops

class QuietPopupWindow(PopupWindow):
def quit(self):
self.destroy() # don't verify close

class ComponentWindow(Frame):
"""
when attached to another display
"""
def init(self, parent): # if not a frame
Frame.init(self, parent) # provide container
self.pack(expand=YES, fill=BOTH)
self.config(relief=RIDGE, border=2) # reconfig to change

def quit(self):
    showinfo('Quit', 'Not supported in attachment mode')

# destroy from Frame: erase frame silent               # redef for close ops

----------------------------------------clock.py-------------------------------
"""
###############################################################################
PyClock 2.1: a clock GUI in Python/tkinter.

With both analog and digital display modes, a pop-up date label, clock face
images, general resizing, etc. May be run both standalone, or embedded
(attached) in other GUIs that need a clock.

New in 2.0: s/m keys set seconds/minutes timer for pop-up msg; window icon.
New in 2.1: updated to run under Python 3.X (2.X no longer supported)
###############################################################################
"""

from tkinter import *
from tkinter.simpledialog import askinteger
import math, time, sys

###############################################################################

Option configuration classes

###############################################################################

class ClockConfig:
# defaults--override in instance or subclass
size = 200 # width=height
bg, fg = 'beige', 'brown' # face, tick colors
hh, mh, sh, cog = 'black', 'navy', 'blue', 'red' # clock hands, center
picture = None # face photo file

class PhotoClockConfig(ClockConfig):
# sample configuration
size = 320
picture = '../gifs/ora-pp.gif'
bg, hh, mh = 'white', 'blue', 'orange'

###############################################################################

Digital display object

###############################################################################

class DigitalDisplay(Frame):
def init(self, parent, cfg):
Frame.init(self, parent)
self.hour = Label(self)
self.mins = Label(self)
self.secs = Label(self)
self.ampm = Label(self)
for label in self.hour, self.mins, self.secs, self.ampm:
label.config(bd=4, relief=SUNKEN, bg=cfg.bg, fg=cfg.fg)
label.pack(side=LEFT) # TBD: could expand, and scale font on resize

def onUpdate(self, hour, mins, secs, ampm, cfg):
    mins = str(mins).zfill(2)                          # or '%02d' % x
    self.hour.config(text=str(hour), width=4)
    self.mins.config(text=str(mins), width=4)
    self.secs.config(text=str(secs), width=4)
    self.ampm.config(text=str(ampm), width=4)

def onResize(self, newWidth, newHeight, cfg):
    pass  # nothing to redraw here

###############################################################################

Analog display object

###############################################################################

class AnalogDisplay(Canvas):
def init(self, parent, cfg):
Canvas.init(self, parent,
width=cfg.size, height=cfg.size, bg=cfg.bg)
self.drawClockface(cfg)
self.hourHand = self.minsHand = self.secsHand = self.cog = None

def drawClockface(self, cfg):                         # on start and resize
    if cfg.picture:                                   # draw ovals, picture
        try:
            self.image = PhotoImage(file=cfg.picture)          # bkground
        except:
            self.image = BitmapImage(file=cfg.picture)         # save ref
        imgx = (cfg.size - self.image.width())  // 2           # center it
        imgy = (cfg.size - self.image.height()) // 2           # 3.x // div
        self.create_image(imgx+1, imgy+1,  anchor=NW, image=self.image)
    originX = originY = radius = cfg.size // 2                 # 3.x // div
    for i in range(60):
        x, y = self.point(i, 60, radius-6, originX, originY)
        self.create_rectangle(x-1, y-1, x+1, y+1, fill=cfg.fg)   # mins
    for i in range(12):
        x, y = self.point(i, 12, radius-6, originX, originY)
        self.create_rectangle(x-3, y-3, x+3, y+3, fill=cfg.fg)   # hours
    self.ampm = self.create_text(3, 3, anchor=NW, fill=cfg.fg)

def point(self, tick, units, radius, originX, originY):
    angle = tick * (360.0 / units)
    radiansPerDegree = math.pi / 180
    pointX = int( round( radius * math.sin(angle * radiansPerDegree) ))
    pointY = int( round( radius * math.cos(angle * radiansPerDegree) ))
    return (pointX + originX+1), (originY+1 - pointY)

def onUpdate(self, hour, mins, secs, ampm, cfg):        # on timer callback
    if self.cog:                                        # redraw hands, cog
        self.delete(self.cog)
        self.delete(self.hourHand)
        self.delete(self.minsHand)
        self.delete(self.secsHand)
    originX = originY = radius = cfg.size // 2          # 3.x div
    hour = hour + (mins / 60.0)
    hx, hy = self.point(hour, 12, (radius * .80), originX, originY)
    mx, my = self.point(mins, 60, (radius * .90), originX, originY)
    sx, sy = self.point(secs, 60, (radius * .95), originX, originY)
    self.hourHand = self.create_line(originX, originY, hx, hy,
                         width=(cfg.size * .04),
                         arrow='last', arrowshape=(25,25,15), fill=cfg.hh)
    self.minsHand = self.create_line(originX, originY, mx, my,
                         width=(cfg.size * .03),
                         arrow='last', arrowshape=(20,20,10), fill=cfg.mh)
    self.secsHand = self.create_line(originX, originY, sx, sy,
                         width=1,
                         arrow='last', arrowshape=(5,10,5), fill=cfg.sh)
    cogsz = cfg.size * .01
    self.cog = self.create_oval(originX-cogsz, originY+cogsz,
                                originX+cogsz, originY-cogsz, fill=cfg.cog)
    self.dchars(self.ampm, 0, END)
    self.insert(self.ampm, END, ampm)

def onResize(self, newWidth, newHeight, cfg):
    newSize = min(newWidth, newHeight)
    #print('analog onResize', cfg.size+4, newSize)
    if newSize != cfg.size+4:
        cfg.size = newSize-4
        self.delete('all')
        self.drawClockface(cfg)  # onUpdate called next

###############################################################################

Clock composite object

###############################################################################

ChecksPerSec = 10 # second change timer

class Clock(Frame):
def init(self, config=ClockConfig, parent=None):
Frame.init(self, parent)
self.cfg = config
self.makeWidgets(parent) # children are packed but
self.labelOn = 0 # clients pack or grid me
self.display = self.digitalDisplay
self.lastSec = self.lastMin = -1
self.countdownSeconds = 0
self.onSwitchMode(None)
self.onTimer()

def makeWidgets(self, parent):
    self.digitalDisplay = DigitalDisplay(self, self.cfg)
    self.analogDisplay  = AnalogDisplay(self,  self.cfg)
    self.dateLabel      = Label(self, bd=3, bg='red', fg='blue')
    parent.bind('<ButtonPress-1>', self.onSwitchMode)
    parent.bind('<ButtonPress-3>', self.onToggleLabel)
    parent.bind('<Configure>',     self.onResize)
    parent.bind('<KeyPress-s>',    self.onCountdownSec)
    parent.bind('<KeyPress-m>',    self.onCountdownMin)

def onSwitchMode(self, event):
    self.display.pack_forget()
    if self.display == self.analogDisplay:
        self.display = self.digitalDisplay
    else:
        self.display = self.analogDisplay
    self.display.pack(side=TOP, expand=YES, fill=BOTH)

def onToggleLabel(self, event):
    self.labelOn += 1
    if self.labelOn % 2:
        self.dateLabel.pack(side=BOTTOM, fill=X)
    else:
        self.dateLabel.pack_forget()
    self.update()

def onResize(self, event):
    if event.widget == self.display:
        self.display.onResize(event.width, event.height, self.cfg)

def onTimer(self):
    secsSinceEpoch = time.time()
    timeTuple      = time.localtime(secsSinceEpoch)
    hour, min, sec = timeTuple[3:6]
    if sec != self.lastSec:
        self.lastSec = sec
        ampm = ((hour >= 12) and 'PM') or 'AM'               # 0...23
        hour = (hour % 12) or 12                             # 12..11
        self.display.onUpdate(hour, min, sec, ampm, self.cfg)
        self.dateLabel.config(text=time.ctime(secsSinceEpoch))
        self.countdownSeconds -= 1
        if self.countdownSeconds == 0:
            self.onCountdownExpire()                # countdown timer
    self.after(1000 // ChecksPerSec, self.onTimer)  # run N times per second
                                                    # 3.x // trunc int div
def onCountdownSec(self, event):
    secs = askinteger('Countdown', 'Seconds?')
    if secs: self.countdownSeconds = secs

def onCountdownMin(self, event):
    secs = askinteger('Countdown', 'Minutes')
    if secs: self.countdownSeconds = secs * 60

def onCountdownExpire(self):
    # caveat: only one active, no progress indicator
    win = Toplevel()
    msg = Button(win, text='Timer Expired!', command=win.destroy)
    msg.config(font=('courier', 80, 'normal'), fg='white', bg='navy')
    msg.config(padx=10, pady=10)
    msg.pack(expand=YES, fill=BOTH)
    win.lift()                             # raise above siblings
    if sys.platform[:3] == 'win':          # full screen on Windows
        win.state('zoomed')

###############################################################################

Standalone clocks

###############################################################################

appname = 'PyClock 2.1'

use new custom Tk, Toplevel for icons, etc.

from windows import PopupWindow, MainWindow

class ClockPopup(PopupWindow):
def init(self, config=ClockConfig, name=''):
PopupWindow.init(self, appname, name)
clock = Clock(config, self)
clock.pack(expand=YES, fill=BOTH)

class ClockMain(MainWindow):
def init(self, config=ClockConfig, name=''):
MainWindow.init(self, appname, name)
clock = Clock(config, self)
clock.pack(expand=YES, fill=BOTH)

b/w compat: manual window borders, passed-in parent

class ClockWindow(Clock):
def init(self, config=ClockConfig, parent=None, name=''):
Clock.init(self, config, parent)
self.pack(expand=YES, fill=BOTH)
title = appname
if name: title = appname + ' - ' + name
self.master.title(title) # master=parent or default
self.master.protocol('WM_DELETE_WINDOW', self.quit)

###############################################################################

Program run

###############################################################################

if name == 'main':
def getOptions(config, argv):
for attr in dir(ClockConfig): # fill default config obj,
try: # from "-attr val" cmd args
ix = argv.index('-' + attr) # will skip x internals
except:
continue
else:
if ix in range(1, len(argv)-1):
if type(getattr(ClockConfig, attr)) == int:
setattr(config, attr, int(argv[ix+1]))
else:
setattr(config, attr, argv[ix+1])

config = PhotoClockConfig()

config = ClockConfig()
if len(sys.argv) >= 2:
    getOptions(config, sys.argv)         # clock.py -size n -bg 'blue'...

myclock = ClockWindow(config, Tk()) # parent is Tk root if standalone

myclock = ClockPopup(ClockConfig(), 'popup')

myclock = ClockMain(config)
myclock.mainloop()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容