Tkinterでデスクトップクロックを作る。

# デスクトップにおいておけるアナログ時計を作る

デジタル時計の方が作るのがめんどくさいのだけど簡単に作れるアナログ時計はドヤれると聞いてコードを引っ張り出してみました。<br>
デジタル時計も文字を表示するだけなら簡単だけどきれいに表示しようとすると、アナログ時計のほうが線描くだけで超簡単じゃんって具合に本当にデジタルめんどくさいので一度試してみてほしい。

そんなことはさておいて、何気に便利なアナログ時計行ってみましょう。<br>
今回も機種依存の部分を含むので Windows 限定とさせていただきます。

## 時計カスタムウィジットを作る

qiita向けに簡略化していますがおおよそカスタムウィジットの体裁は保てているかと。

この **Clockウィジット** は ```set``` で **"12:59"** などと文字列を渡すと
時間を表示するというものになっています。（元は業務でリクエストされて作ったものなのでr）

大きさや色が決め打ちになっていますが興味があれば直して使ってみると面白いと思います。<br>
折角なので前回勢いで作った **AAImage** を投入して滑らかな線を実現しています。



### コード

*clock.py*

```python
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"))
```

### 一応解説

カスタムウィジットにする理由は諸々ですが一つは再利用しやすくするため、一つは手を抜くため（）<br>
毎回毎回同じコードを書くわずらわしさを省くためにカスタムウィジットを作ります。<br>
今回は簡易版なのでいろいろ決め打ちになっちゃっていますがパラメータを渡せるようにしたり自由度を上げてみたり
もっと便利な機能をつけたり等、いろいろ考えられます。

いずれ機会があればカスタムウィジットについては掘り下げたい気もしますが…。

とりあえず大切なのは、正しい正しくないではなく、**何をしたいのか、それを実現できているか**。です。

## デスクトップクロック

clockウィジットを使ったデスクトップアクセサリを作ります。

### コード

*desktop_clock.pyw*

```python
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でデスクトップマスコット的なものを作る](https://qiita.com/sugarflower/items/74281bed307a6e31ae3b) と同じですが時計を更新するための
定期処理が含まれています。<br>
また最前面にいると後ろ側が見えないのでウィンドウの半透明にしています。

#### 定期処理

```python
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秒）毎にここが再度呼ばれます。<br>
インスタンスの c0（クロックウィジット）に時間を渡すとあとは諸々のことをClockウィジットがやってくれるというわけです。

#### ウィンドウ半透過指定

```python
# alpha blend
self.root.wm_attributes('-alpha',0.9)
```

これ。 0～1 (0%～100%)までの数字で指定します。

## 動作確認

今回は3つのファイルを利用します。<br>
アンチエイリアシング対応PhotoImage、 **aaimage.py** は [前回の記事](https://qiita.com/sugarflower/items/3dc3efee6c4a62711d61) を参照のこと。

- aaimage.py
- clock.py
- desktop_clock.pyw

これらを一か所にまとめて、**desiktop_clock.pyw** をダブルクリックなどで実行すると時計が起動します。
例のごとく時計がアクティブな時に **ESC** キーを押下すると終了します。

### 動作イメージ

![clock](https://user-images.githubusercontent.com/12671694/144835517-bcdcca85-72f0-421c-8f02-ff8639ab4703.png)

ついでなので、Canvasに普通に描画したもの（右）との比較も出してみました。<br>
アンチエイリアシング、重要じゃろう！？(ギザギザもローファイでかっこいい部分があるのは認めますが)

元が現在時刻を表示するために作ったものではないので秒とか処理していないのですが、時計と割り切って
秒まで対応して滑らかに時間が動く（クォーツ的にかくかく動かない）アクセサリなんかにすると悦に浸れそうです。