前の章、第2章: 勤務表データモデル (Timesheet Data Models)では、プログラムが様々な形式の勤務表データを統一的に扱うための「設計図」について学びましたね。異なる形式のファイルでも、プログラム内では同じ「形」のデータとして扱える仕組みでした。
さて、今回は、いよいよユーザーである皆さんが直接触れることになる、このアプリケーションの「顔」とも言える部分、メインウィンドウ (MainWindow) について学んでいきましょう。
メインウィンドウは、この「勤務表比較ツール」を起動したときに最初に表示される画面です。まさに、アプリケーションの玄関口であり、ユーザーが様々な操作を行うための主要なインターフェースを提供します。
家で例えるなら、全ての部屋(機能)につながるリビングルームのような存在です。このメインウィンドウから、ファイルを指定したり、「比較して!」と指示を出したり、比較結果を見たりといった、様々な機能へアクセスすることになります。
皆さんがこのツールを使うとき、まず目にするのがこの画面であり、ここでの操作が全ての始まりとなります。
では、実際にメインウィンドウがどのような見た目で、どんなことができるのかを見てみましょう。(実際の見た目はバージョンによって多少異なる場合があります)
+--------------------------------------------------------------+
| 勤務表比較ツール |
+--------------------------------------------------------------+
| [社内勤務表] |
| +----------------------------------------------------------+ |
| | [フォルダ] [ ここにファイルをドロップまたはファイル選択 ] | |
| +----------------------------------------------------------+ |
| |
| [客先勤務表] |
| +----------------------------------------------------------+ |
| | [フォルダ] [ ここにファイルをドロップまたはファイル選択 ] | |
| +----------------------------------------------------------+ |
| |
| [許容時間] |
| +-------------------+ |
| | 稼働時間: [ 5分 ▼] | |
| +-------------------+ |
| |
| [実行(比較)] [キャンセル] |
+--------------------------------------------------------------+
(これは画面の構成を簡易的に表現したものです)
このメインウィンドウには、主に以下の機能エリアがあります。
このように、メインウィンドウはシンプルな見た目ながら、ファイル選択から比較実行の指示まで、ツールを使う上での基本的な操作を集約しています。
メインウィンドウは「リビングルーム」のようなものだと説明しました。つまり、他の専門的な機能を持つ「部屋」(部品/モジュール)と連携して動作します。ユーザーがメインウィンドウで行った操作は、適切な部品に伝えられ、処理が進められます。
ユーザーが「実行(比較)」ボタンを押したときの、大まかな処理の流れをシーケンス図で見てみましょう。
sequenceDiagram
participant User as ユーザー
participant MW as メインウィンドウ (MainWindow)
participant FS as ファイル選択 (FileSelector)
participant TC as 勤務表比較 (TimesheetComparator)
participant RV as 結果表示 (ResultViewer)
User->>MW: 社内勤務表ファイルを選択
MW->>FS: 社内ファイルパスを保持
User->>MW: 客先勤務表ファイルを選択
MW->>FS: 客先ファイルパスを保持
User->>MW: 許容時間を設定 (例: 5分)
User->>MW: 「実行(比較)」ボタンをクリック
MW->>FS: 選択されたファイルパスを取得
FS-->>MW: ファイルパス (社内, 客先)
MW->>TC: compare(客先データ, 社内データ, 許容時間) を呼び出す
Note right of TC: 比較処理を実行中...
TC-->>MW: 比較結果 (ComparisonResult) を返す
MW->>RV: 比較結果を表示するよう指示
RV-->>User: 比較結果を画面に表示
FileSelector
から選択されたファイルの情報を取得します。compare
メソッド呼び出し)。TimesheetComparator
は比較処理を行い、結果をメインウィンドウに返します。ResultViewer
によって整形された比較結果を見ることができます。このように、メインウィンドウはユーザーからの指示を受け付け、各専門部品へ適切に処理を依頼し、最終的な結果をユーザーに提示する、司令塔のような役割を果たしているのです。
メインウィンドウの裏側は、src/ui/main_window.py
というファイルに書かれています。ここでは、その一部を簡単に見てみましょう。(実際のコードはもっと複雑ですが、ここでは概念を理解するために簡略化しています。)
メインウィンドウが表示されるとき、まず _setup_ui
というメソッド(関数のようなもの)が呼ばれて、ボタンやラベルなどの画面部品が配置されます。
# src/ui/main_window.py より (簡略化)
from PySide6.QtWidgets import QMainWindow, QPushButton, QLabel, QComboBox
from .file_selector import FileSelector # ファイル選択部品
from .result_viewer import ResultViewer # 結果表示部品
from ..core.timesheet_comparator import TimesheetComparator # 比較ロジック
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("勤務表比較ツール") # ウィンドウのタイトル設定
# 各部品の準備
self.file_selector = FileSelector()
self.comparator = TimesheetComparator()
self.result_viewer = ResultViewer()
# UIのセットアップを実行
self._setup_ui()
def _setup_ui(self):
# --- ファイル選択エリアの部品を作成・配置 ---
# (社内用)
self.file_selector.internal_file_label = QLabel("ここにファイルをドロップ...", self)
self.file_selector.internal_file_button = QPushButton("選択", self)
# ボタンがクリックされたら self.file_selector._select_internal_file を呼ぶように接続
self.file_selector.internal_file_button.clicked.connect(self.file_selector._select_internal_file)
# (客先用)
self.file_selector.client_file_label = QLabel("ここにファイルをドロップ...", self)
self.file_selector.client_file_button = QPushButton("選択", self)
self.file_selector.client_file_button.clicked.connect(self.file_selector._select_client_file)
# --- 許容時間設定エリア ---
self.allowable_work_time_comboBox = QComboBox(self)
self.allowable_work_time_comboBox.addItems(["0分", "5分", "10分", "..."]) # 選択肢を追加
# --- 操作ボタン ---
self.compare_button = QPushButton("実行(比較)", self)
# 比較ボタンがクリックされたら self._execute_comparison を呼ぶように接続
self.compare_button.clicked.connect(self._execute_comparison)
self.cancel_button = QPushButton("キャンセル", self)
self.cancel_button.clicked.connect(self._execute_cancel)
# ... 他のUI部品の配置や設定 ...このコードでは、QLabel (文字表示) や
QPushButton (ボタン)、QComboBox
(ドロップダウンリスト)
といった部品を作成し、それぞれのボタンがクリックされたときにどの処理
(_select_internal_file や _execute_comparison
など) を呼び出すかを設定しています (.clicked.connect(...)
の部分)。
ユーザーが「実行(比較)」ボタンを押すと、_execute_comparison
メソッドが呼び出されます。
# src/ui/main_window.py より (簡略化)
from PySide6.QtWidgets import QMessageBox # メッセージ表示用
from PySide6.QtCore import QThread, Signal # 非同期処理用
# --- 時間のかかる処理を別スレッドで行うためのクラス ---
class WorkerThread(QThread):
finished_signal = Signal(object) # 処理完了を知らせるシグナル
def __init__(self, file_selector, comparator, allowable_minutes):
super().__init__()
self.file_selector = file_selector
self.comparator = comparator
self.allowable_minutes = allowable_minutes
def run(self): # このメソッドが別スレッドで実行される
try:
# ファイル選択部品からデータモデルを取得
client_data = self.file_selector.get_client_timesheet()
internal_data = self.file_selector.get_internal_timesheet()
# 比較を実行!
result = self.comparator.compare(
client_data, internal_data, self.allowable_minutes
)
self.finished_signal.emit(result) # 結果をシグナルで通知
except Exception as e:
# エラーが発生したら None を通知
self.finished_signal.emit(None)
# --- MainWindow クラスの中に戻る ---
class MainWindow(QMainWindow):
# ... ( __init__, _setup_ui などは省略 ) ...
def _execute_comparison(self):
# 1. ファイルが両方選択されているかチェック
if not self.file_selector.is_files_selected():
QMessageBox.warning(self, "警告", "両方のファイルを選択してください。")
return # 処理を中断
# 2. 許容時間を取得 (例: "5分" -> 5)
allowable_str = self.allowable_work_time_comboBox.currentText()
allowable_minutes = int(allowable_str.replace("分", ""))
# 3. 処理中ダイアログを表示し、比較処理を別スレッドで開始
self.start_progress_dialog(allowable_minutes) # 処理中... を表示
def start_progress_dialog(self, allowable_minutes):
# ... (処理中ダイアログ表示の準備) ...
# WorkerThreadを作成して開始
self.worker = WorkerThread(
self.file_selector, self.comparator, allowable_minutes
)
# 処理が終わったら on_progress_finished を呼ぶように接続
self.worker.finished_signal.connect(self.on_progress_finished)
self.worker.start() # 別スレッドで run() を実行開始
def on_progress_finished(self, result): # 比較処理が終わったときに呼ばれる
# ... (処理中ダイアログを閉じる) ...
if result is None:
QMessageBox.critical(self, "エラー", "比較中にエラーが発生しました。")
elif not result: # 結果が空リストの場合
QMessageBox.warning(self, "警告", "比較対象データが見つかりませんでした。")
else:
# 4. 結果表示部品に結果を渡して表示
self.result_viewer.show_result(result)_execute_comparison の主な流れは以下の通りです。
WorkerThread
という別の「働き手」(スレッド)に実際の比較処理
(comparator.compare(...))
を任せます。これにより、比較中もメインウィンドウは応答可能な状態を保てます。
start_progress_dialog
で「処理中…」のような表示を出し、WorkerThread
を開始します。WorkerThread
での比較処理が終わると、finished_signal
という合図が送られ、on_progress_finished
メソッドが呼び出されます。on_progress_finished
では、まず処理中ダイアログを閉じます。そして、比較結果
(result)
を受け取り、エラーがなかったか、結果が空でないかなどを確認します。show_result
メソッドを呼び出して画面に表示させます。少し複雑に見えるかもしれませんが、メインウィンドウがユーザー操作を受け付け、時間のかかる処理は別の働き手に任せ、最終的に結果を表示部品に渡す、という流れを掴んでいただければ大丈夫です。
この章では、timesheet_compare
アプリケーションの「玄関口」であり「リビングルーム」でもあるメインウィンドウ
(MainWindow) について学びました。
TimesheetComparator に渡し、比較処理を開始させます。ResultViewer に渡されて表示されます。これで、アプリケーション全体の操作の流れと、メインウィンドウがその中でどのような役割を果たしているか、イメージが湧いたのではないでしょうか。
次の章では、メインウィンドウの一部であり、ユーザーが比較したいファイルを指定するための機能である「ファイル選択」について、もう少し詳しく見ていきます。
次の章: 第4章: ファイル選択 (FileSelector)
Generated by AI Codebase Knowledge Builder