PySimpleGUIでスケジュール管理プログラムの作成　その1

## はじめに


- PySimpleGUIを勉強始めたので、自分で使えるツールを作ってみたい
- 自分の欲しい機能だけ入ったスケジュール管理ツールが欲しい  
ということで、作ってみました。

素人が作ったツールなので、至らない点がたくさんあると思いますが、
欲しい機能を実現するために色々調べたので記録します。
PythonでGUI作りたい方に少しでも参考になれば幸いです。

##### *2022/4/18 機能をいくつか追加して更新しました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/da3de936-2566-300a-b6c0-09abb0e1a98b.jpeg" width=400>

本記事は作ったGUIの説明です。
コードの解説はこちらのページに記載しています。

https://qiita.com/CabbageRoll/items/a60b5800bb79c33bb8ef


## 欲しい機能

- 仕事を小さな単位(Ticket)で管理できる
- 各Ticketがやるべき順に並ぶ（ガントチャート的な感じ）
- 納期や見積工数から優先順位を自動で計算してくれる   
- Ticketをポチポチすることで1日のスケジュールを組み立てられる
- 1日のスケジュールから各Ticketに費やした時間を自動集計してくれる
- 振り返って何にどれだけ時間を費やしたかを分析できる

## コード

コードは３つのファイルに分割

|ファイル名|記載している内容|
|---|---|
|a01_gui_main.py|mainプログラム<br>作成したClassの呼び出しとPySimpleGUIのwhile文|
|b01_schedule_class_base.py|PySimpleGUIのLayout設定|
|b01_schedule_class.py|ボタンを押した時の動作などは全てこのファイルに記載<br>b01_schedule_class_baseを継承|
|c01_func_priority_calculation.py|チケット優先順位を自動計算する関数|


https://github.com/CyabbageRoll/Schedule_Manager


次の実行環境で動作確認しております

- python                    3.8.12 
- pysimplegui               4.55.1
- pandas                    1.3.5

## GUI概要

GUIは大きく分割して次の3つのエリアから構成される。
1. Header
2. Left Tab Group
3. Right Tab Group

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/58b7ecfa-7d55-8aea-82cd-a1f758fa8775.png" width=600>


### Ticketについて

このツールで扱う仕事の最小単位はTicketとします。
また、Ticketの集まりをTask、Taskの集まりをProjectと呼びます。
より詳細に分析できるように、ProjectはProject1とProject2に分割します。
一つのチケットは "Project1 - Project2 - Task - Ticket名" という情報を持ちます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/505eb46b-f124-6b79-d934-33181ccff380.png" width=300>


### Header

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/69fbdf91-db82-0649-fcf7-8c0923ce6dd9.png" width=700>

- Projectのチェックボックス
    - Left Tab "Project" で表示するプロジェクトを選択します
    - Project1 - Project2　と繋げて表示されています
    - デフォルトでは担当者の所有する未完了のチケットが所属するProjectがONとなっています
- ボタン
   - All : チェックボックス全てをONにする
   - Clear : チェックボックス全てをOFFにする
   - Refresh : 画面を再計算(基本的には押さなくて良いはず)
   - Upload : 情報を保存 (自動保存されないのでこまめな保存推奨)
   - Reload : 情報をロード(保存していない情報は消える)
- チームメンバーのラジオボタン
    - ここで選択したメンバーの情報がLeft/Right Tabに表示されます


### Left Tab Group

- Project
- Status (未実装)
- Priority
- Settings

#### Project Tab

各チケットが優先度順に左から右に並んで表示されます。
Project毎にBlockが作られており、各Ticketはその中に並べて表示される。
Ticketの色はTask毎に自動で色がつきます。色の変更は現状できません。
ProjectはUserが入力して作成しますが、次の2つはデフォルトで準備しています。

- Other-One-off : ちょっとした事務仕事など、Projectに分類するのが難しい仕事
- Other-Regularly : 定期的に発生する仕事や会議(Right Tab Groupで1日の予定を設定するときに使います）
Regularlyのチケットは、優先順位計算されない、工数Overしても理由を聞かれないなど、特殊な扱いになります。

Regularly以外は同時並行にならないように、ガントチャート風に優先順位順にTicketが並べられます。
優先順位はPriority Tabで手動で変更できますが、自動で計算することもできます(後述)。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/2d03b924-8d13-562a-e90c-00ecf907ed58.png" width=700>


#### Status Tab (未実装)

横軸にStatus、縦軸にメンバー名としてticketが並ぶ所謂カンバンボードを作る計画。
（あまり必要性を感じておらず後回し・・・）

#### Priority Tab

- 各Ticketが優先順位順に上から並んで一覧で表示されます
- Ticketを選んだ状態で右にあるボタンをクリックすると優先順位を変更できます  
  ⬆️⬇️は1ずつ、⏫⏬は10ずつ優先度が変わる
- ProjectがOther-RegularlyのTicketは通常のチケットの下に、StatusがDoneのTicketは更にその下にまとめて表示される。
- 一般Ticket, DoneのTicket, RegulalryのTicketはその中では優先順位を変えられるが、ブロックをまたがって移動はできません(Ticket数が9999を超えるとバグる仕様になってます）
- Project TabとPriority Tabでは右クリックメニューが使えます(後述)。
- 🖥ボタンをクリックすると、優先順位が自動で計算されます。
    - 優先順位は、チケットの納期、開始可能日、見積工数、チケット同士のつながりを考慮して計算されます。
    - 後ろからトポロジカルソートして各チケットの開始しないといけない日を求め、
    その後前からトポロジカルソートして優先順位を決めています(ちゃんとできているか自信がない)。
- Auto activateにチェックを入れると、チケットを作成したり、Statusを変更したりする度に自動計算が実行されます。
    - Setting tab のauto_priority_activateをTrueにすると、起動時にチェックが入った状態となります。
    - 自動計算ONが推奨設定です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/aa3ffed4-4923-d546-ecba-4c2b44162550.png" width=700>

#### Settings

後述する”sch_m_settings.csv”で設定する項目をGUIで変更できるようにしたタブです。
左上の”Save & Restart”ボタンをクリックすると変更が反映されてwindowが再起動します。
Windowサイズを変更して再描画させるため一度windowが閉じますが仕様です(落とさないでいい方法が判明したら実装します)。
なお、読み込めない設定（例えばFont = “Meiryoyoyo”）とした場合は、setting.csvが書き換えられ、再起動時にエラーで立ち上がらなくなります。
不親切設計となっていますのでご注意ください。すみません。


### Right Tab Group

- Ticket
- Daily
- Team
- Fever(未実装）
- Plan

#### Ticket

Ticketの内容閲覧、新規作成、修正、削除をするタブ。
納期や時間の修正はできるがTicket名の修正はできません。仕様です。

このTabをアクティブにした状態で、ProjectタブのTicket上にマウスカーソルを置いたり、Priorityタブの表でTicketを選択すると、そのTicketの内容がここに表示されます。

- Edit modeチェックボックスをONにすると、Leftタブでマウスを動かしてもTicketタブの内容が変化せず、編集できるようになる。ONにしなくても編集自体は可能ですが、書いている途中でマウスを動かすと勝手に内容が変わったりするので、注意が必要です。 
- Project1, Project2, Taskが新規の場合はセルの色が黄色くなるので、既存かどうかが判断できます。また空白の場合は赤色となり、登録できません。
- Project1, Project2, Taskセルで右クリックすると、既存の物から選択ができます。直打ちもできます。
- Ready Date(着手可能日）、Due Dateはセルをクリックするとカレンダーが表示されて選択式で入力できます。直接入力もできるがyyyy/mm/dd形式である必要があります。形式が異なる場合はセルが赤色になり、登録できません。
- Estimation（見積もり工数）、Estimation(additional)（見直しで増減した工数）は数値型に変換できる形式で0.25h刻みで入力してください。こちらも形式が異なる場合は赤色になり、登録できません。
- 担当者は選択できるが、自分以外のTicketは登録できません(思案中)。  
- Applyボタンを押すことで新規作成か修正が実行されます。  
    - Project1-Project2-Task-Ticketが既存であれば修正、どこか違うなら新規作成となります。
    - 修正の場合はOK / Cancelの確認画面が出ます
- Deleteボタンを押すとTicketが削除されます。論理削除ではないため、一度消すと元に戻せないので注意してください。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/8ba86995-71ed-9baf-03a4-2d1b646ad720.png" width=700>


- 下側のPrevious, NextではそのTicketの繋がりを設定します。例えば、”木を切る” → “組み立てる” → “塗装する”という作業があった場合、Ticket”組み立てる”のPreviousに“木を切る“、Nextに”塗装する”を設定します。この繋がりを設定しておくと、この繋がりを守って優先順位が計算されます。
- 下の図のように設定すると、TicketはB->A->Cの順になるように優先順位が計算されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/cbf7fe22-5712-3025-7c08-840b01b2c082.png" width=450>

#### Daily

1日の予定を作るタブです
Ticketの活動実績を記録する機能も兼ねています。

- 表の時間帯を選択した状態で、Left TabのTicketを右クリックして出てくるメニューでSchdeuling_を押すと、そのTicketの内容が予定表に反映されます。
- activate left clickにチェックを入れておくと、チケットを左クリックしただけで予定表に反映されるようになります。
- Deleteボタンを押すと、選択した行の予定が取り消しされます。
- 1日の終わりに、予定表を実績になるように修正してからRecordボタンを押して下さい。各Ticketにどれだけ時間を掛けたかを計算して、実績工数として記録されます。
- 一番上にその日の業務開始時間、終了時間、勤務時間、休憩時間が表示されるので、正しく入力出来ているかを確認してください。
- 📧🤛ボタンを押すと、Outlookなどのスケジュールアプリから会議スケジュールなどを読み込み表示させる。。。ためのボタン。
（自宅PCにはOutlookが入っていないのでGithubにあげているコードには実装していません）
- 予定表下部に枠を設けているので、職場ルールで記録が必要な健康状態や残業理由などなどを記入できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/f8c99019-8ce0-4471-5c12-9ab1f0e4b2be.png" width=700>


#### Team

チームメンバー各々がDailyタブで入力した内容の概要が、まとめてこのページに表示されます。このTabは閲覧専用です。
自分以外のメンバーの内容を確認するときは、そのメンバーがUploadした後に、Loadする必要があります。自動保存や自動読み込みはしない仕様となっています。

#### Fever(未実装)

各Projectのフィーバーチャートを表示
横軸に進捗率、縦軸に工数

#### Plan

大きな枠1つと小さな枠4つの入力欄を用意しています。
月、週の目標などを記入するなど、メモとして利用できます。


### その他

#### 右クリックメニュー

Left Tab GroupのProjectとPriorityタブでは右クリックメニューが使用可能。
項目は以下の通り。末尾の”_”はPySimpleGUIでどのeventかを簡単に判別するための記号で使用時は無視してください(右クリックメニューが使用された要素が返って来るようにするにはどうすればいいのかが分かってません。分かったら追記します)。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/85795ef6-8cb2-2d2d-f87f-47b21ef6af5d.png" width=300>

- Scheduling_
- Edit_
- New ticket FROM this_
- New ticket TO this_
- Status
    - ToDo_ 
    - Doing_
    - Checking_
    - Done_
    - Pending_  
（ToDoとDoneだけあればいい？）

|menu|機能|
|---|---| 
|Scheduling|選択したTicketをDaily Tabの予定に設定|
|Edit|Ticket TabにそのTicketの内容を表示してEdit ModeをONに設定|
|New ticket FROM this|基本はEditと同じ。Ticket名入力欄が空白になり、選択したTicketがPreviousに設定される|
|New ticket To this| 基本はEditと同じ。Ticket名入力欄が空白になり、選択したTicketがNextに設定される|
|Status|選択したTicketのStatusを更新<Br>Doneを選択した場合、完了日や工数の実績が見積を超えている場合、理由を聞くウインドウが立ち上がります。<br>理由の入力をお願いします|


#### データの持ち方

プログラムを起動すると、prj_df, trk_df, sch_dfの3つのPandas DataFrameが読み込まれる（ない場合はからのDataFrameが生成される）

|Name|Index|Column|内容|
|---|---|---| ---| 
|prj_df|Ticket id|Ticket情報(project名や工数情報など）|各Ticketの諸情報|
|trk_df|Ticket id|日付|各Ticketの日々の工数情報|
|sch_df|時間とコメントタイトル|日付|Daily Tabの情報|

* メンバー毎にDataFrameが読み込まれ、メンバー名をkeyとした辞書(prj_dfs, trk_dfs, sch_dfs)に格納される

- TicketタブでTicketを生成すると、prj_dfに情報が保存される
- Priorityタブで優先度を変えると、prj_dfの情報が更新される
- Projectタブのticket情報はpjr_dfの情報を読み込んで、生成される
- dailyタブで予定を作成するとsch_dfに保存される
- dailyタブでrecordボタンを押すと、各ticketの日々工数がtrk_dfに渡され、
- trk_dfの工数や最初の実施日と最終実施日がprj_dfの各チケットに記録される


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1651487/a2a509b9-9591-018c-2329-a0d6dd935de7.png" width=500>

#### 設定ファイル

設定ファイルはファイル名 “sch_m_settings.csv” で実行場所に保管しておく必要があります。指定ファイルがない場合はエラーで落ちます。

|名前|説明|格納場所|
|---|---|---|
|file_save_dir|dataframeファイルの保存場所<br>この下にprj,sch,trkというフォルダが作成されて保管する<br>パスの中にnameを入れてはいけない（要プログラム修正）|self.prj_file<br>self.sch_file<br>self.trk_file|
|window_theme|PySimpleGUIのwindowテーマ|self.param|
|header_nrow|headerのprojectとメンバー名の表示行|self.param|
|user_name|使用者の名前<br>team_membersと完全一致で検索かけるのでスペルミス注意|self.param|
|team_members|チームメンバーの名前<br>自分の名前の入れる|self.param|
|hour_in_date|1日の業務時間(Regularly以外をする時間)|self.param|
|daily_begin|daily tableの１行目の時間|self.param|
|dailytable_rows|daily tableを何行表示させるか<br>スクロールして見える範囲全部|self.param|
|dailytable_disp_rows|daily tableを何行表示させるか<br>スクロールせずに見える範囲<br>この値でテーブルの高さが決まる|self.param|
|font|基本のフォントとフォントサイズ|self.param|
|window|windowのサイズ(pixel)|self.sizes|
|header_button|headerボタンの幅と高さ(半角文字数)|self.sizes|
|header_chkbox|headerチェックボックスのテキストサイズ|self.sizes|
|header_radio|headerラジオボタンのテキストサイズ|self.sizes|
|right_button|Rightタブのボタンサイズ|self.sizes|
|right_input|Rightタブのインプットサイズ|self.sizes|
|right_comment_boxies|Rightタブのコメントボックスのサイズ|self.sizes|
|right_team_box|Rightタブのteamタブのテキストボックスのサイズ|self.sizes|
|graph_top_right|グラフエリアの座標値設定（変更しない|self.sizes|
|left3_col_width|left priority tableの列幅|self.sizes|

＊ サイズはレイアウトが崩れる場合の対策として初期設定ファイルで変更できるようにしている。

## リンクと謝辞

- 公式マニュアル
基本はこちらに全部まとまっているので、要素を使うときはまずはこちらをチェック
[PySimpleGUI ELEMENT AND FUNCTION CALL REFERENCE](https://pysimplegui.readthedocs.io/en/latest/call%20reference/)

- Python画像処理のためのGUI入門（PySimpleGUI解説）
各要素の使い方などがとても理解しやすかったです。ありがとうございます

https://qiita.com/soymushroom/items/da002d628d7a28cd6e97

- 【自動化】PythonでOutlookの予定を抜き出す
職場のwindowsPCではこちらを参考に実装しました。

https://qiita.com/konitech913/items/2ec831863ad84db23558

なお、Outlookにアクセスする機能を追加してからpyinstallerでexe化する場合は、
import win32timezoneを追加する必要がありました（以下参考。--onefileはなくてもexe化できた）
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11231580286


他にも色々な記事を読んで勉強させて頂きました
全ては紹介できませんが、御礼申し上げます