SWELL公式サイトへ 詳しくはこちら

【第7回】状態を“クラス”にするという選択|tkinterを構造化する第一歩

  • URLをコピーしました!

これまでの回で、私は何度も「状態」という言葉を使ってきました。

第3回では「状態を持つと、tkinterはアプリになる」と書きました。
第4回では増えてきた状態を整理する必要性を考えました。
第5回では、状態が増えたときに突然難しくなる理由を整理しました。
第6回では、状態管理という設計の考え方に触れました。

では、ここで一つの疑問が生まれます。

その状態は、どこに置けばいいのでしょうか?

変数として書けば動きます。
グローバル変数でも、とりあえずは問題なく動作します。
小さなスクリプトなら、それでも十分かもしれません。

しかし、ボタンが増え、処理が増え、画面が増えたとき――
「どこで何が変わったのか分からない」という感覚が生まれます。

それは技術的な難しさというより、
構造が曖昧であることの難しさです。

今回は、その曖昧さに一つの選択肢を提示します。

状態を「クラス」にまとめるという選択です。

状態を意識するだけでは足りない。
状態を“置く場所”を決める必要がある。

ここから、tkinterは少しだけ構造を持ちはじめます。

目次

🧠 第1章:なぜ変数のままでは限界が来るのか

tkinterでアプリを作り始めたとき、多くの人はこう書きます。

current_code = None
is_running = False
processed_count = 0

とりあえず必要な状態を、上に並べていく。

小さなスクリプトなら、それで十分です。
ボタンが1つ、処理が1つ程度なら問題は起きません。

しかし、少しずつ機能が増えていくと状況が変わります。

  • ボタンが3つになる
  • 処理モードが増える
  • エラー状態を持ちたくなる
  • 処理件数を画面に表示したくなる

すると、状態を表す変数も増えていきます。

current_code = None
is_running = False
processed_count = 0
error_flag = False
mode = "normal"
selected_item = None

ここまではまだ大丈夫です。

問題はその次です。

🔎 どこで変更されたのか分からなくなる

たとえば processed_count が増えている。

でも、

  • どの関数で更新されたのか?
  • 二重にカウントされていないか?
  • エラー時にも増えていないか?

これを追うのが、だんだん難しくなっていきます。

状態が散らばると、責任も散らばります。

🔗 関数同士が密結合になる

ボタンAの処理が mode を変更し、
ボタンBの処理が mode を前提に動く。

気づかないうちに、

「暗黙の前提」がコードのあちこちに埋め込まれていきます。

そしてある日、

なぜかこの順番で押さないと動かない

という現象が起きます。

これはバグというより、
構造が曖昧なことの結果です。

🧩 スクリプトは動く。でも構造がない。

ここが重要です。

コードは動きます。
エラーも出ません。
一応完成しています。

でも、どこか落ち着かない。

なぜかというと、

状態の居場所が決まっていないからです。

変数として存在しているだけで、
「まとまり」を持っていない。

それは、部屋に物が散らばっている状態に少し似ています。

動くことと、整理されていることは別です。

🧠 小さな違和感が、成長の入口になる

tkinterが難しく感じる瞬間は、
実は機能が増えたからではありません。

状態が増えたからでもありません。

状態の管理方法が曖昧なまま拡張したことが原因です。

この違和感に気づいたとき、
アプリは一段階進化する準備ができています。

では、その状態に“居場所”を与えるとどうなるのか。

次章で、状態を一つの箱にまとめてみます。

🏗 第2章:状態を“クラス”にまとめる

前章では、状態が散らばることで構造が曖昧になる話をしました。

では、どうすればよいのでしょうか。

一つの答えは、とてもシンプルです。

状態を、ひとつの箱にまとめる。

その箱として使えるのが「クラス」です。

📦 状態専用のクラスを作る

たとえば、これまでバラバラに置いていた変数を、
次のようにまとめます。

class AppState:
    def __init__(self):
        self.current_code = None
        self.is_running = False
        self.processed_count = 0
        self.error_flag = False
        self.mode = "normal"
        self.selected_item = None

そしてアプリの起動時に、1つだけインスタンスを作ります。

state = AppState()

これだけです。

やっていることは単純です。

でも、この一手で構造が生まれます。

🔎 何が変わったのか?

見た目は大きく変わっていません。

しかし、意味が変わっています。

以前は、

processed_count += 1

と直接書いていたものが、

state.processed_count += 1

になります。

この違いは小さいようで大きい。

processed_count はただの変数ではなく、
「アプリの状態の一部」になりました。

🧠 状態に“居場所”ができる

クラスにまとめることで、

  • 状態はここにある、と明示できる
  • 何がアプリの状態なのか一目で分かる
  • 新しい状態を追加する場所も決まる

状態の居場所が決まると、
コード全体の見通しが一気に良くなります。

散らばっていた責任が、ひとつに集まります。

🧩 まだロジックは入れない

ここで大事なのは、

このクラスは「状態を入れる箱」だということです。

まだ処理ロジックは入れません。

まずは、

  • データをまとめる
  • 状態を可視化する
  • アプリの“現在”をひとつに集約する

それだけで十分です。

🌱 構造が生まれる瞬間

状態をクラスにするというのは、

単なる文法の話ではありません。

それは、

アプリの中に、ひとつの中心を作るということです。

状態が中心にあり、
UIやイベントはその周りを回る。

この構造ができた瞬間、
tkinterはスクリプトから一歩進みます。

次章では、この状態クラスと
tkinterの「イベントドリブン」との関係を整理します。

ボタンが押されたとき、
本当に起きていることは何なのか。

そこを一段、抽象化してみましょう。

🔁 第3章:イベントドリブンと状態の関係

tkinterは「イベントドリブン」で動いています。

ボタンが押される。
キーが入力される。
ウィンドウが閉じられる。

何かが起きたときに、関数が呼ばれる。

それがイベントドリブンです。

しかし、ここで一度立ち止まって考えてみます。

ボタンが押されたとき、
本当に起きていることは何でしょうか。

🎯 イベントは“きっかけ”にすぎない

たとえば、こんなコードがあります。

def on_start():
    state.is_running = True
    state.processed_count = 0

ボタンを押すと、この関数が呼ばれます。

でも本質は「ボタンが押されたこと」ではありません。

本質は、

  • is_running が True になった
  • processed_count が 0 にリセットされた

という 状態の変化 です。

イベントは“きっかけ”。
アプリの実体は“状態の変化”です。

🧠 アプリとは状態の遷移である

少し抽象化してみましょう。

アプリは、

「状態A」

イベント

「状態B」

という遷移を繰り返しています。

たとえば:

  • 停止中 → ボタン押下 → 実行中
  • 実行中 → 処理完了 → 停止中
  • 実行中 → エラー発生 → エラー状態

UIはこの変化を“表示”しているにすぎません。

本体は、常に状態です。

🔄 状態クラスがあると何が起きるか

状態をクラスにまとめたことで、

アプリの「現在地」が常に一箇所にあります。

print(state.is_running)
print(state.processed_count)

どんなイベントが起きても、
最終的に行き着くのはこの state です。

イベントが増えても、
処理が増えても、

アプリの中心は変わりません。

これが「構造」です。

🧩 UIは状態を映す鏡になる

たとえば、ラベルの表示を更新する場合。

label.config(text=str(state.processed_count))

ラベルは、状態を表示しているだけです。

ボタンは、状態を変更するきっかけ。
ラベルは、状態を映す鏡。

この視点を持つと、

  • どこで何を変更しているか
  • どこで表示しているか

がはっきり分かれるようになります。

🌌 少し視点を上げる

tkinterが難しく感じるのは、
イベントが多いからではありません。

イベントに振り回されるからです。

しかし、

「アプリは状態の変化である」

と理解すると、

イベントはただのトリガーになります。

主役は常に状態です。

ここまで来ると、

「UI」と「状態」を分けて考えたくなります。

次章では、
UIと状態の責務を分離する発想に触れてみましょう。

🧩 第4章:UIと状態を分けるという発想

第3章では、アプリの本質は「状態の変化」であると整理しました。

では、UIの役割は何でしょうか。

ボタンは状態を変更する“きっかけ”。
ラベルは状態を表示する“鏡”。

そう考えると、UIは状態そのものではありません。

UIは、状態を操作し、状態を映す存在です。

🏗 役割を分けて考えてみる

ここで一度、整理してみましょう。

  • State:アプリの現在地を保持する
  • UI:表示と入力を担当する
  • 処理ロジック:状態をどう変えるかを決める

今までは、これらが混ざっていました。

ボタンの関数の中に、

  • 状態変更
  • 計算処理
  • 画面更新

すべてが書かれていたはずです。

それでも動きます。

しかし、分けて考えると見通しが変わります。

🔎 例:少しだけ整理してみる

たとえば、ボタン処理をこう考えます。

def on_start():
    start_process(state)
    update_view()

ここで、

def start_process(state):
    state.is_running = True
    state.processed_count = 0

UI側は「呼び出す」だけ。

状態の変更は、状態を扱う関数に任せます。

ラベル更新も、

def update_view():
    label.config(text=str(state.processed_count))

と分離できます。

🧠 分けるだけで、何が変わるのか?

まだ構造は小さいです。

でも、

  • UIはUIの役割に集中する
  • 状態は状態の場所に集まる
  • 処理は処理として整理される

この分離ができると、

アプリが“読みやすく”なります。

そして、拡張が怖くなくなります。

🔁 小さな分離が、大きな拡張を支える

ボタンが10個になっても、

  • 状態は AppState にある
  • UIはイベントを受け取るだけ
  • ロジックは関数で管理される

この枠組みが崩れません。

これが「構造化」の意味です。

コードが増えても、秩序は崩れない。

🌱 まだ難しい設計は必要ない

ここで無理にMVCやデザインパターンを持ち込む必要はありません。

大事なのは、

役割を分けるという感覚を持つこと。

状態をクラスにしたことが、
その第一歩でした。

次章では、この考え方が
実務RPAや業務アプリでどのように活きるのかを見ていきます。

tkinterは学習用GUIではなく、
構造を持った実用アプリに変わります。

💼 第5章:実務RPAでどう活きるか

ここまで、状態をクラスにまとめ、
UIと役割を分ける話をしてきました。

では、この構造は実務で役に立つのでしょうか。

答えは、はっきりと Yes です。

むしろ、実務こそ構造が必要になります。

📊 実務では“状態の数”が多い

業務RPAや業務アプリでは、
扱う情報が一気に増えます。

たとえば:

  • 現在処理中のレコードID
  • 処理件数
  • エラー発生フラグ
  • 実行モード(通常/検証/テスト)
  • 処理済みかどうかの状態
  • 一時的に保持するデータ

これらをグローバル変数で管理すると、
ほぼ確実に混乱します。

しかし、状態クラスがあると、

class AppState:
    def __init__(self):
        self.current_record_id = None
        self.processed_count = 0
        self.error_flag = False
        self.mode = "normal"
        self.temp_data = None

アプリの“現在地”が常に一箇所にまとまります。

🔁 RPAは状態遷移の連続

RPA処理は、実は単純です。

  1. レコードを取得
  2. データを保持
  3. 外部画面へ入力
  4. 結果を確認
  5. 次のレコードへ

これを繰り返します。

しかし実際には、

  • エラーが起きる
  • スキップするケースがある
  • 再実行が必要になる

つまり、常に状態が変化しています。

この変化を管理するのが state です。

🧠 エラー処理が整理される

たとえば、途中でエラーが発生した場合。

state.error_flag = True

その瞬間にアプリ全体が

「今はエラー状態である」

と認識できます。

UIはそれを見て表示を変える。

ロジックはそれを見て処理を止める。

エラーが“散らばらない”。

これは実務では非常に重要です。

📈 拡張が怖くなくなる

新しい機能を追加したいとき。

  • 処理モードを増やす
  • ログ保存機能を追加する
  • 再実行機能を入れる

状態クラスがあれば、

「追加する場所」が明確です。

構造があると、拡張は怖くありません。

構造がないと、拡張は恐怖になります。

🌱 tkinterは“業務アプリ”になる

tkinterは学習用GUIと思われがちです。

しかし、状態を持ち、
構造を持ち、
役割を分けると、

それは立派な業務アプリになります。

派手なフレームワークでなくてもいい。

まずは、

状態に居場所を与える。

それだけで十分です。

ここまでで、

  • 状態をクラスにする
  • イベントとの関係を理解する
  • UIと分離する
  • 実務で活かす

という流れが完成しました。

最後に、この第7回をまとめましょう。

🌌 まとめ:状態に居場所を与えるということ

これまで、状態という言葉を何度も使ってきました。

状態を持つ。
状態を整理する。
状態管理を考える。

そして今回、
状態を「クラス」にまとめました。

やっていることは、決して難しくありません。

変数をひとつの箱に入れただけです。

しかし、その一手で
アプリの構造は大きく変わります。

状態をクラスにするというのは、

単なる書き方の違いではありません。

それは、

アプリの中心を明確にすることです。

イベントはきっかけにすぎない。
UIは状態を映す鏡にすぎない。

本体は常に、状態の変化です。

状態に居場所ができると、

  • 何がアプリの現在地なのか分かる
  • どこに追加すればよいか分かる
  • 拡張が怖くなくなる

コードは動くだけでなく、
落ち着きを持ちはじめます。

tkinterは小さなGUIライブラリです。

しかし、状態を構造化した瞬間、
それは「アプリ」になります。

スクリプトから、構造へ。
思いつきから、設計へ。

その第一歩が、

状態をクラスにするという選択でした。

次回は、
UIとロジックをさらに整理し、
より読みやすく拡張しやすい形へ進みます。

小さな分離が、
大きな安定を生みます。

よかったらシェアしてね!
  • URLをコピーしました!
目次