2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

最前面で透明な時計を出しっぱなしにする

Posted at

背景:タスクバーは非表示にしたいが時計はみたい

  • エンジニアにとって縦の表示行数は最大にしたい
  • だからタスクバーは非表示の方が正しいのだ
  • とはいえ時計が非常時になるのはいやだ
  • いやWindowsキー一つで表示できるじゃないか
  • 常に表示されてこそでしょうが

という会話が社内でなされれていたので、解決策として自作しました。

完成ソース

clock.py
import tkinter as tk
import time

TRANSPARENT_COLOR = "#add123"

root = tk.Tk()
root.title("Transparent Movable Clock")

root.config(bg=TRANSPARENT_COLOR)
root.wm_attributes('-transparentcolor', TRANSPARENT_COLOR)
root.overrideredirect(True)
root.attributes('-topmost', True)

font_family = "Courier"
font_size = 40
shadow_color = "#333333"  # 濃いグレー
text_color = "white"

canvas = tk.Canvas(root, width=220, height=80, bg=TRANSPARENT_COLOR, highlightthickness=0)
canvas.pack()

show_colon = True

def tick():
    global show_colon
    now = time.strftime("%H %M")
    if show_colon:
        display_time = now[:2] + ":" + now[3:]
    else:
        display_time = now[:2] + " " + now[3:]
    show_colon = not show_colon
    
    canvas.delete("all")
    
    # 右下にずらした影(縁取り)
    canvas.create_text(
        112, 42,  # 2px右下にずらす
        text=display_time,
        font=(font_family, font_size, "bold"),
        fill=shadow_color,
    )
    
    # メインの白文字
    canvas.create_text(
        110, 40,  # 元の位置
        text=display_time,
        font=(font_family, font_size, "bold"),
        fill=text_color,
    )
    
    root.after(1000, tick)

tick()

# --- ウィンドウ移動用のイベント処理 ---
def start_move(event):
    root.x = event.x
    root.y = event.y

def do_move(event):
    deltax = event.x - root.x
    deltay = event.y - root.y
    x = root.winfo_x() + deltax
    y = root.winfo_y() + deltay
    root.geometry(f"+{x}+{y}")

canvas.bind("<Button-1>", start_move)
canvas.bind("<B1-Motion>", do_move)

# --- 右クリックコンテキストメニュー ---
menu = tk.Menu(root, tearoff=0)
menu.add_command(label="閉じる", command=root.destroy)

def show_context_menu(event):
    try:
        menu.tk_popup(event.x_root, event.y_root)
    finally:
        menu.grab_release()

canvas.bind("<Button-3>", show_context_menu)
root.bind("<Button-3>", show_context_menu)

root.mainloop()

実行方法

powershell上でpythonスクリプトとして実行

python .\clock.py

動作スクショ

image.png

解説

  • Windowsでしか動作環境してないです
    • 背景が透明というような特殊な機能なのでOS依存ありそう
  • マイクロソフトストアの純正Pythonの標準モジュールのみ使ってます
    • pipインストールとかは要らないはず
    • そのためにtkinterというプリミティブなモジュールですませてます
  • 透明+最前面です
    • じゃぁどうやってこのウィンドウを掴んで移動させるのかというと
    • 「文字が描画されてるところ」でドラッグすると移動させることができます
    • このウィンドウそのものが「文字が描画されてる形」になってると思ってください
  • 終了のさせかた
    • ウィンドウのタイトルバーさえ透明にしてしまったためウィンドウを閉じる導線がない
    • 右クリックで閉じるという機能だけつけた
    • 右クリックのイベントも反応できるのは「数字があるところだけ」という使いづらさはある(殆ど触ることないからセーフ)
  • 殆ど邪魔にはならないのでオンラインミーティング中の画面共有中にでっぱなしでも害はない

見やすさのコツ

  • 透明だから背景が白だと見えづらい
  • 濃いめの色で右下にずらして2回描画している
  • 大量に背景の文字をずらして描画するともっと袋文字になるだろうけど、ブログでさらすにはソースが余計すぎる

なぜPythonで作った?

  • 実はPythonはOS依存の機能をガンガンライブラリに取り込んでくれる
  • 背景を透明にする機能までカバーしているマルチOSなGUIフレームワークはElectronくらいだけど、それもOSごとのAPIをかき分けてる
  • ElectronかPythonかで選ぶなら「この程度ならPythonペラ1」で十分
  • ブログからコピペで動くという楽ちんさ
  • PowerShellでもウィンドウの透明化のAPIを叩けばにたことができるだろうけど、PowerShellのスクリプトを動かせるようにするためのおまじないが下手すりゃPythonより面倒

どうやって作った?

  • perplexityに指示だしながら書かせた
    • Python+GUIは自身にノウハウがなさすぎる
  • ただしテストして触ると半透明にならなかったり、クリックできなかったりとバグだらけなので、小さくはじめてテストを繰り返して少しずつ機能を足していくしかない
  • 生成AIに書かせる点でもペラ1で見通しのいいソースの方が進めやすい

心残り

  • tinker上で文字を描画するとアンチエイリアシングがうまくいってないのか、文字の縁が絶妙に指定してない色が見える
  • 解消するにはWebViewにしてブラウザベースのレンダリングにした方が文字はきれいに見えるはず
    • だけどpythonのWebViewは標準モジュールじゃない
    • k依存モジュールは少ないほうが良いという哲学に反する
  • 最初から右下に表示したいけど
    • 右下に置くというロジックを書いても複数の画面サイズでテストするのが面倒でやてない
    • 一度移動したらずっとそのままなので、さほど不便ではない
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?