Исходный программный код PyClock

ishodnyj programmnyj kod pyclock Примеры законченных программ с графическим интерфейсом

Вся реализация PyClock находится в одном файле, за исключением предварительно подготовленных объектов с настройками стилей. Если посмотреть в конец примера 11.12, можно заметить, что объект часов можно создать, либо передав конструктору объект с настройками, либо определив параметры настройки в аргументах командной строки, как показано ниже (в этом случае сценарий просто сам создаст объект с настройками):

C:\\PP4E\Gui\Clock> clock.py -bg gold -sh brown -size 300

Вообще говоря, для запуска часов этот файл можно выполнить непосредственно, с аргументами или без; импортировать его и создать объекты, используя объекты с настройками, чтобы часы выглядели более индивидуально; или импортировать и прикрепить его объекты к другим графическим интерфейсам. Например, PyGadgets из главы 10 запускает этот файл с параметрами командной строки, управляющими внешним видом часов.

Пример 11.12. PP4E\Gui\Clock\clock.py

############################################################################## PyClock 2.1: часы с графическим интерфейсом на Python/tkinter.

В обоих режимах отображения, аналоговом и цифровом, могут выводить метку с датой, графические изображения на циферблате, изменять размеры и так далее. Могут запускаться автономно или встраиваться (прикрепляться) в другие графические интерфейсы, где требуется вывести текущее время.

Новое в версии 2.0: клавиши s/m устанавливают таймер, отсчитывающий секунды/ минуты перед выводом всплывающего сообщения; значок окна.

Новое в версии 2.1: добавлена возможность выполнения под управлением Python 3.X (2.X больше не поддерживается)

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

from tkinter import *

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

############################################################################## # Классы параметров настройки

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

class ClockConfig:

#  умолчания — переопределите в экземпляре или в подклассе

size = 200 # ширина=высота

bg, fg = beige’, ‘brown # цвет циферблата, рисок

hh, mh, sh, cog = black’, ‘navy’, ‘blue’, ‘red# стрелок, центра picture = None # файл картинки

class PhotoClockConfig(ClockConfig):

#  пример комплекта настроек

size = 320

picture = ‘../gifs/ora-pp.gif’

bg, hh, mh = ‘white’, ‘blue’, ‘orange’

############################################################################## # Объект цифрового интерфейса

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

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: при изменении размеров можно было бы # изменять размер шрифта

def onUpdate(self, hour, mins, secs, ampm, cfg):

mins = str(mins).zfill(2) # или ‘%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 # здесь ничего перерисовывать не требуется

############################################################################## # Объект аналогового интерфейса

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

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): # при запуске и изменении размеров

if cfg.picture: # рисует овалы, картинку

try:

self.image = PhotoImage(file=cfg.picture) # фон

except:

self.image = BitmapImage(file=cfg.picture) # сохранить ссылку imgx = (cfg.size self.image.width()) // 2 # центрировать

imgy = (cfg.size self.image.height()) // 2 # 3.x деление //

self.create_image(imgx+1, imgy+1, anchor=NW, image=self.image)

originX = originY = radius = cfg.size // 2 # 3.x деление //

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) # минуты 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) # часы

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): # вызывается из

if self.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 деление //

hour = hx, hy

hour + (mins / 60.0)

= 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

############################################################################## # Составной объект часов

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

ChecksPerSec = 10 # частота проверки системного времени

class Clock(Frame):

def __init__(self, config=ClockConfig, parent=None):

Frame.__init__(self, parent)

self.cfg = config

self.makeWidgets(parent) # дочерние виджеты компонуются методом pack,

self.labelOn = 0 # но клиенты могут использовать pack или grid

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() # таймер обратного отсчета

self.after(1000 // ChecksPerSec, self.onTimer) # вызывать N раз в сек.

# 3.x // целочисленное # деление с усечением 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):

#   ВНИМАНИЕ: только один активный таймер,

#   текущее состояние таймера не отображается

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() # поднять над другими окнами

if sys.platform[:3] == ‘win’: # в Windowsна полный экран win.state(‘zoomed’)

############################################################################## # Автономные часы

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

appname = ‘PyClock 2.1’

#   использовать новые окна Tk, Toplevel со своими значками и так далее from PP4E.Gui.Tools.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)

#   для обратной совместимости: рамки окна устанавливаются вручную, # передается родитель

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) # владелец=parent или окно по умолчанию self.master.protocol(‘WM_DELETE_WINDOW’, self.quit)

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

#   Запуск программы

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

if __name__ == ‘__main__’:

def getOptions(config, argv):

for attr in dir(ClockConfig): # заполнить объект с настройками

try: # из арг. ком. строки “-attr val

ix = argv.index(‘-’ + attr) # пропустит внутр. __x__ 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()) # при автономном выполнении

#myclock = ClockPopup(ClockConfig(), ‘popup’) # родителем является корневое myclock = ClockMain(config) # окно Tk

myclock.mainloop()

И наконец, в примере 11.13 приводится модуль, выполняемый сценарием PyDemos, — в нем определяется несколько стилей часов и производится запуск одновременно семи экземпляров часов, прикрепляемых к новым окнам верхнего уровня для создания демонстрационного эффекта (хотя на практике обычно достаточно иметь на экране одни часы, даже мне!).

Пример 11.13. PP4E\Gui\Clock\clockStyles.py

#   предопределенные стили часов

from clock import *

from tkinter import mainloop

gifdir = ‘../gifs/’

if __name__ == ‘__main__’: from sys import argv if len(argv) > 1:

gifdir = argv[1] + ‘/’

class PPClockBig(PhotoClockConfig):

picture, bg, fg = gifdir + ‘ora-pp.gif’, ‘navy’, ‘green’

class PPClockSmall(ClockConfig):

size = 175

picture = gifdir + ‘ora-pp.gif’

bg, fg, hh, mh = ‘white’, ‘red’, ‘blue’, ‘orange’

class GilliganClock(ClockConfig):

size = 550

picture = gifdir + ‘gilligan.gif’

bg, fg, hh, mh = ‘black’, ‘white’, ‘green’, ‘yellow’

class LP4EClock(GilliganClock):

size = 700

picture = gifdir + ‘ora-lp4e.gif’ bg = ‘navy’

class LP4EClockSmall(LP4EClock):

size, fg = 350, ‘orange’

class Pyref4EClock(ClockConfig):

size, picture = 400, gifdir + ‘ora-pyref4e.gif’

bg, fg, hh = ‘black’, ‘gold’, ‘brown’

class GreyClock(ClockConfig):

bg, fg, hh, mh, sh = ‘grey’, ‘black’, ‘black’, ‘black’, ‘white’

class PinkClock(ClockConfig):

bg, fg, hh, mh, sh = ‘pink’, ‘yellow’, ‘purple’, ‘orange’, ‘yellow’

class PythonPoweredClock(ClockConfig):

bg, size, picture = ‘white’, 175, gifdir + ‘pythonPowered.gif’

if __name__ == ‘__main__’:

root = Tk()

for configClass in [

ClockConfig,

PPClockBig,

#PPClockSmall,

LP4EClockSmall,

#GilliganClock,

Pyref4EClock,

GreyClock,

PinkClock,

PythonPoweredClock

]:

ClockPopup(configClass, configClass.__name__)

Button(root, text=’Quit Clocks’, command=root.quit).pack() root.mainloop()

При запуске этот сценарий создает множество часов различного вида, как показано на рис. 11.24. Объекты конфигурации поддерживают большое число параметров. Судя по семи парам часов, отображаемых на экране, пришло время перейти к последнему примеру.

Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011

Оцените статью
Секреты программирования
Добавить комментарий