デスクトップにおいておけるアナログ時計を作る
デジタル時計の方が作るのがめんどくさいのだけど簡単に作れるアナログ時計はドヤれると聞いてコードを引っ張り出してみました。
デジタル時計も文字を表示するだけなら簡単だけどきれいに表示しようとすると、アナログ時計のほうが線描くだけで超簡単じゃんって具合に本当にデジタルめんどくさいので一度試してみてほしい。
そんなことはさておいて、何気に便利なアナログ時計行ってみましょう。
今回も機種依存の部分を含むので Windows 限定とさせていただきます。
時計カスタムウィジットを作る
qiita向けに簡略化していますがおおよそカスタムウィジットの体裁は保てているかと。
この Clockウィジット は set
で "12:59" などと文字列を渡すと
時間を表示するというものになっています。(元は業務でリクエストされて作ったものなのでr)
大きさや色が決め打ちになっていますが興味があれば直して使ってみると面白いと思います。
折角なので前回勢いで作った AAImage を投入して滑らかな線を実現しています。
コード
clock.py
mport tkinter as tk
import aaimage
import math
class Clock(tk.Canvas):
def __init__(self, master, cnf={}, **kw):
self.time = "99:99" #初期値、未設定状態
self.hour = 0
self.min = 0
super().__init__(master, cnf={}, **kw)
self.config(bg = master["bg"])
self["highlightthickness"] = 0
self["bd"] = 0
self.bind("<Configure>", self._update)
self.image = aaimage.AAImage(width=300, height=300)
self.image.fill((255, 255, 0))
# 時間を"HH:MM"形式の文字列で渡す
def set(self, time=None):
if time != None:
self.time = time
if self.time != "99:99":
#アングルを計算する
st = self.time.split(":")
self.min = (int(st[1]) * 6 + 180) % 360
self.hour = ((int(st[0]) * 30 + 180) % 360) + (int(st[1]) * 0.5)
self._draw()
def _update(self, e=None):
self["width"] = self["height"]
self._draw()
# 時計の表示
def _draw(self, e=None):
self.delete("all")
self.create_image(0, 0, image=self.image, anchor="nw")
self.image.fill(self.image.hextocolor("#2c2d2d"))
center = (int(self.winfo_height()) - 1) // 2
size = center * 2
long = center * 0.9
short = center * 0.7
# 文字盤のマーク
for i in range(12):
lx = math.sin(math.radians(i * 30)) * -long + center
ly = math.cos(math.radians(i * 30)) * long + center
sx = math.sin(math.radians(i * 30)) * -short + center
sy = math.cos(math.radians(i * 30)) * short + center
self.image.line(lx, ly, sx, sy, color=self.image.hextocolor("#4e4f61"))
# 時針
hx = math.sin(math.radians(self.hour)) * -short + center
hy = math.cos(math.radians(self.hour)) * short + center
self.image.line(hx, hy, center, center, color=self.image.hextocolor("#b84f7f"))
# 分針
mx = math.sin(math.radians(self.min)) * -long + center
my = math.cos(math.radians(self.min)) * long + center
self.image.line(mx, my, center, center, color=self.image.hextocolor("#abacc2"))
一応解説
カスタムウィジットにする理由は諸々ですが一つは再利用しやすくするため、一つは手を抜くため()
毎回毎回同じコードを書くわずらわしさを省くためにカスタムウィジットを作ります。
今回は簡易版なのでいろいろ決め打ちになっちゃっていますがパラメータを渡せるようにしたり自由度を上げてみたり
もっと便利な機能をつけたり等、いろいろ考えられます。
いずれ機会があればカスタムウィジットについては掘り下げたい気もしますが…。
とりあえず大切なのは、正しい正しくないではなく、何をしたいのか、それを実現できているか。です。
デスクトップクロック
clockウィジットを使ったデスクトップアクセサリを作ります。
コード
desktop_clock.pyw
import tkinter as tk
import clock
import datetime
class AnalogClock:
"""
events
"""
# These events handle dragging the window.
def mouseDown(self, e):
if e.num == 1:
self.origin = (e.x, e.y)
self.isMouseDown = True
def mouseRelease(self, e):
self.isMouseDown = False
def mouseMove(self, e):
if self.isMouseDown:
buf = self.root.geometry().split("+")
self.setPos(e.x - self.origin[0] + int(buf[1]),
e.y - self.origin[1] + int(buf[2]),
)
# The application is terminated by pressing the "ESC" key.
def keyReleease(self, e):
if e.keycode == 27:
self.root.destroy()
"""
set geometry, Position
"""
def setPos(self, x, y):
self.root.geometry("+%s+%s" % (x, y))
def now(self, e=None):
global c0
dt = datetime.datetime.now()
time = "%02d:%02d" % (dt.hour, dt.minute)
self.c0.set(time)
self.c0.after(1000, self.now, 1000)
"""
create main window
"""
def screenInit(self):
# Set window size
self.root.geometry("150x150")
self.c0 = clock.Clock(self.root)
self.c0["height"] = 150
self.c0.grid(row=0, column=0)
"""
Various settings
"""
# Transparency setting
self.root.wm_attributes("-transparentcolor", self.c0["bg"])
# Disable window decoration
self.root.overrideredirect(True)
# Fix the screen to the front
self.root.attributes("-topmost", True)
# alpha blend
self.root.wm_attributes('-alpha',0.9)
"""
Bind events
"""
self.c0.bind("<Button>", self.mouseDown)
self.c0.bind("<ButtonRelease>", self.mouseRelease)
self.c0.bind("<Motion>", self.mouseMove)
self.root.bind_all("<KeyRelease>", self.keyReleease)
self.now()
def __init__(self, root):
self.root = root
self.origin = (0, 0)
self.isMouseDown = False
self.screenInit()
def main():
root = tk.Tk()
AnalogClock(root)
root.mainloop()
if __name__ == "__main__":
main()
解説
大部分は以前の Tkinterでデスクトップマスコット的なものを作る と同じですが時計を更新するための
定期処理が含まれています。
また最前面にいると後ろ側が見えないのでウィンドウの半透明にしています。
定期処理
def now(self, e=None):
global c0
dt = datetime.datetime.now()
time = "%02d:%02d" % (dt.hour, dt.minute)
self.c0.set(time)
self.c0.after(1000, self.now)
まずは定期処理ここを一度呼ぶとひたすら 1000ミリ秒(1秒)毎にここが再度呼ばれます。
インスタンスの c0(クロックウィジット)に時間を渡すとあとは諸々のことをClockウィジットがやってくれるというわけです。
ウィンドウ半透過指定
# alpha blend
self.root.wm_attributes('-alpha',0.9)
これ。 0~1 (0%~100%)までの数字で指定します。
動作確認
今回は3つのファイルを利用します。
アンチエイリアシング対応PhotoImage、 aaimage.py は 前回の記事 を参照のこと。
- aaimage.py
- clock.py
- desktop_clock.pyw
これらを一か所にまとめて、desiktop_clock.pyw をダブルクリックなどで実行すると時計が起動します。
例のごとく時計がアクティブな時に ESC キーを押下すると終了します。
動作イメージ
ついでなので、Canvasに普通に描画したもの(右)との比較も出してみました。
アンチエイリアシング、重要じゃろう!?(ギザギザもローファイでかっこいい部分があるのは認めますが)
元が現在時刻を表示するために作ったものではないので秒とか処理していないのですが、時計と割り切って
秒まで対応して滑らかに時間が動く(クォーツ的にかくかく動かない)アクセサリなんかにすると悦に浸れそうです。