🔰

【ポートフォリオ】マウスリスナーを使った、ボタン不要のストップウォッチ

2024/07/06に公開
自己紹介

1993年11月1日生まれ(30)
小中高とサッカーをし、大学ではトライアスロンを経験したものの、ロードバイクを盗まれて引退。
宇宙のような未知なものごとが好きで、大学は理学部物理学科に進学するも、家庭の事情で3年次に引退(中退)
独学でPythonを学習し、未知のものごとに触れたくてIT業界に入ったものの、
実務でプログラミングに触れることなく5年が過ぎ、行動しなきゃ変われないと危機感を感じ、現在に至る。

はじめに

Pythonの学習も兼ねて、実務で不便だったことを解消できるようなツールを作成しました。
本記事は、そのツールについての説明も兼ねたポートフォリオです。

ツール作成の背景

業務で、PCとタブレットで同時に同じURLを開き、ページが開くまでの時間を測定する負荷テストを複数人で行っていました。
右手にPC用のマウスを持ち、左手でタブレットを操作しながら、右手の親指でiPhoneのストップウォッチを押してラップタイムまで計測するというアナログな方法でしたが、しばしば押し忘れなどのミスが発生しました。そこで、PC画面をマウスでクリックすると同時にストップウォッチが計測を開始するツールを探しましたが見つからず、自分で作ることにしました。
正直、業務の負荷テスト以外では使い道がないストップウォッチです。

使用技術

  • Windows10
  • Python 3.11.1
  • pynput 1.7.7
  • tkinter
  • Pyinstaller
  • GitHub

実際のGUIアプリ

以下のリポジトリからdist.zipをダウンロードして、
zipファイルを解凍してからOnehand_StopWatch.exeを起動してください。
GitHubリポジトリへ

使い方

左クリックでスタート/ストップ
右クリックでLapを取得
ストップ状態でマウスホイールを下にスクロールすると、リセット

アプリ画面
(左クリックでスタートし、右クリックでラップを取得、マウスホイールを↓にしてリセット)

全コード

https://github.com/NaoJ-practice/Onehand_StopWatch/blob/main/Onehand_stopwatch.py

こだわり

良くも悪くも、画面のどこを左クリックしてもスタートします。
ボタンやチェックボックスによるマウスリスナーのオンオフ機能は付けていません。
最初はスタート/ストップ/ラップのボタンを設けていましたが、何も伝えずに友人にテストしてもらったら、迷わずボタンを押していました。
それだと本来の、どこを触ってもストップウォッチが操作できるという利点が失われます。
そのため、余計な機能を全て排除した、時間とラップのみが表示されるシステムにしています。

チームメンバーに配布するために

このGUIアプリをチームメンバーみんなが使えば、計測ミスが大幅に減ります。
ただ、Python環境を構築してもらって、コードを配布するのはハードルが上がります。
そこで、Pythonスクリプトをexe化してみんなに配ることで、Python環境の構築をしなくてもプログラムを実行できるようにしました。

Pyinstallerのインストール

まずはPythonインストール済みの環境にPyinstallerをインストールします。

pip install pyinstaller

Pyinstallerを使ったexe化

基本的には以下でexe化できます。

pyinstaller Pythonスクリプト名

Pyinstallerのオプション

デフォルトだと、起動するとコマンドプロンプトが背景にずっと映っていたり、
exeファイルのアイコンがデフォルトで区別がつきにくかったりと、なにかと不便です。
そのため以下のオプションを用いました。

■--noconsole :コマンドプロンプトを表示しない

pyinstaller Onehand_StopWatch.py --noconsole

■--icon:exeファイルのアイコンを変更できる
画像ファイルをicoファイルに変換するとき

pyinstaller Onehand_StopWatch.py --icon=test.ico

最終的には以下でexe化しました。

pyinstaller Onehand_StopWatch.py --noconsole --icon=test.ico

つまづいた点

開発にあたりつまづいた点が2点ありました。

つまづきポイント1

マウスリスナーを開始するとき、以下の様にjoinメソッドでテストしていたため、
1.左クリックを押したとき
2.左クリックを離したとき
でそれぞれ反応してしまい、1クリックで2回反応していた点

開発中のテスト画面
(1クリックでメッセージが2つ出力されていた)

これは、マウスボタンがクリックされたときに呼び出されるコールバックのon_clickにある
pressed 変数が、ボタンが押された時に True になり、離された時に False になるそうです。
それを踏まえて、

if pressed and button == mouse.Button.left:

のように条件を追加することで、左ボタンが押された時(True)だけに反応できるようになりました。

つまづきポイント2

参考にしていたサイトでは、withブロックでマウスリスナーを実行していました。
ただ、withブロックにはブロッキング動作があり、マウスリスナーが動作している間は他のプログラミングが実行されませんでした。

そこで、pynputのドキュメントを読むと、startメソッドが書いてあり、非ブロッキング動作があったので、そちらを採用しました。

以下ドキュメントから引用

# Collect events until released
with mouse.Listener(
        on_move=on_move,
        on_click=on_click,
        on_scroll=on_scroll) as listener:
    listener.join()

# ...or, in a non-blocking fashion:
listener = mouse.Listener(
    on_move=on_move,
    on_click=on_click,
    on_scroll=on_scroll)
listener.start()

上記の非ブロッキング バージョンを使用すると、現在のスレッドは実行を継続します。
これは、メイン ループを組み込んだ他の GUI フレームワークと統合するときに必要に
なる場合がありますが、スクリプトから実行すると、プログラムが直ちに終了します。

改善点

初めて作ったツールなので、何が良くて、何が悪いのかの判断ができません。
ただ、勉強したことをもう少し入れられたなーと思いました。(実務の観点から可読性が良くなるのかは不明ですが…)
具体的には、
1.ラップ取得部分において空のリストに追加していく箇所を、zip関数を使ってコードを減らす
2.例外処理を追加してみる
など

また、GitHubに関しては知識がないので、実務では必須になるであろう、こちらも勉強をしていきたいと思いました。

これから

ハードルはかなり高いですが、機械学習を勉強して、音声合成技術を使ってAIによる声の生成などをやってみたいです。
八代亜紀さんのお別れ会で、声をAIの音声合成技術によるナレーションと過去のライブ音声などを使い、コンサート形式で開催したというニュースを見て、自身な大切な人たちの声を残し続けられたらいいなーと思ったのが理由です。
ゼロつく読んで勉強頑張ります。

参考サイト

以下のサイトを軸として参考にしながら、追加情報を適宜調べていました。

ストップウォッチを参考にしたサイト

https://python-muda.com/python/python-stopwatch/

tkinterを参考にしたサイト

https://note.com/npaka/n/nfa8d16c3624e

pynputについて最初に見てたサイト

https://chocottopro.com/?p=438

つまづいて見たドキュメント(大事)

https://pynput.readthedocs.io/en/latest/mouse.html#monitoring-the-mouse

exe化するときに見たサイト

https://techplay.jp/column/1646

Discussion