プログラミング言語「Python」は、AIやディープラーニングなど、最先端の分野で活用されていることで有名なプログラミング基礎です。一方でPythonは、「GUI(グラフィカルユーザインタフェース)」を活用して、おしゃれなプログラムを作ることもできます。
GUIを取り入れることで、より使いやすいプログラムになり、ユーザビリティが向上します。一方、PythonでGUIプログラミングができるライブラリは複数あるので、「どれを選べばいいかわからない」こともあるでしょう。
そこで本記事では、PythonのGUIライブラリの種類や特徴に加えて、実際にGUIプログラミングを行う方法について解説します。
目次
Pythonの「GUI」とは?視覚的に操作できるUIが作れる!

Pythonの「GUI(Graphical User Interface)」とは、画像や図形などのグラフィックスを用いたインターフェースのことです。マウスや指(タッチやスワイプ)などを使って、視覚的・直感的に操作できるため、専門的な知識がなくても扱えることが魅力です。
GUIを活用したソフトウェアやアプリケーションを「GUIプログラム」と呼びます。私たちが、WindowsやMac、AndroidやiPhoneなどで使っているソフトウェア・アプリは、ほぼすべてがGUIプログラムとなっています。つまり、現在アプリを開発するのであれば、GUIの知識は必須といっても過言ではありません。
「GUI」と「CUI」の違いとは?
「CUI(Character User Interface)」は、情報が文字のみで表示され、キーボードからの入力で操作するインターフェースのことです。身近なCUIプログラムとして、以下のようなWindowsの「コマンドプロンプト」があげられます。

前述したGUIとの大きな違いは、グラフィックスが表示されず、マウスや指(タッチやスワイプ)による操作ができないことです。そのため、CUIプログラムは真っ黒な画面に白い文字が表示される形式となり、キーボードで専用のコマンドを入力して操作する必要があります。
たとえば、Windowsで別のフォルダに移動するときは、単にマウスを使ってフォルダを指定するだけです。しかし、コマンドプロンプトでは「cd」というコマンドを使って、カレントディレクトリを変更しないといけません。このように、CUIプログラムはコマンドを知らないと使えないため、視覚的・直感的に使えるGUIプログラムとは対照的です。
PythonでGUIを使う3つのメリット

PythonでGUIを活用したアプリを作ると、以下3つのメリットが得られます。
- ユーザビリティが高いプログラムを作れる
 - さまざまな情報をリアルタイムで表示できる
 - クロスプラットフォームに対応しやすい
 
ユーザビリティが高いプログラムを作れる
GUIプログラムを作る最大のメリットは、「ユーザビリティ」が向上することです。ユーザビリティとは、ユーザーにとっての「使いやすさ」を指し、顧客満足度に直結します。現在では、使いにくいプログラムを配布しても、ほとんどの人は使ってくれないでしょう。
プログラムの開発者は、その機能や使い方が手に取るようにわかるため、CUIでも十分に使いこなせるかもしれません。しかし、ほとんどのユーザーには専門知識がほとんどないため、最低限の機能もうまく使えないこともあるでしょう。
GUIプログラムでは、画面上にボタンや画像などを配置できるので、ユーザーが視覚的・直感的に使い方を理解できます。ファイルをコピーするときに、コマンドプロンプトでコマンドを入力するのと、エクスプローラーでマウスを使ってコピーするのでは、どちらが「使いやすい」でしょうか。ユーザビリティが高いプログラムは、幅広いユーザーに好まれます。
さまざまな情報をリアルタイムで表示できる
GUIプログラムでは、さまざまな情報をリアルタイムで表示できます。たとえば、ユーザーの入力や選択内容によって、画面表示を変えたいというシーンはよくあります。スライドバーの変更時に数値も連動させて変えたり、グラフの表示結果を変えたりするなど、臨機応変に情報を表示可能です。
また、グラフィカルな画面を活用した、本格的なアプリケーションを作れることも魅力です。画面がなくても、高機能なプログラムは作れます。しかし、データの登録・更新・インポートなどの作業は、画面上にわかりやすく情報が表示されているほうがスムーズです。GUIにより、プログラム開発の可能性が大幅に広がります。
クロスプラットフォームに対応しやすい
現在ではPCだけではなく、AndroidやiPhoneといったスマホ端末やタブレット端末など、プラットフォームの種類が多様化しています。これらのプラットフォームでは、もはやCUIは使われておらず、アプリはGUI形式が当然となっています。
PC上では、コマンドプロンプトに代表されるように、CUIプログラムを使うケースはゼロではありません。しかし、PCだけではなくスマホやタブレットでも使える「クロスプラットフォーム対応」のアプリを作りたいのであれば、GUIに関する知識は必須です。
Pythonの代表的なGUIライブラリ6選

実はPythonの標準機能には、GUIプログラミングに欠かせない「グラフィックスを描画する機能」がありません。そのため、PythonでGUIプログラミングを行うためには、専用の「GUIライブラリ」を使用する必要があります。
Pythonで使えるGUIライブラリは複数あり、それぞれ特徴や機能も異なるため、どれを選ぶべきか悩むこともあるでしょう。そこで本章では、Pythonの代表的なGUIライブラリを以下6つご紹介します。
- Tkinter
 - Kivy
 - PyQt
 - wxPython
 - Pyglet
 - Pygame
 
Tkinter
「Tkinter」は、Pythonで一般的に使われている、いわばスタンダードなGUIライブラリです。Pythonに付属しているライブラリなので、インストールや設定の必要なく、すぐに使えます。単純な仕様でシンプルにコーディングしやすく、ある程度はクロスプラットフォームに対応できることが魅力です。
Kivy
「Kivy」は、クロスプラットフォームに対応したGUIライブラリです。Windows・Linux・macOSに加えて、AndroidやiOSなどのモバイル端末で動くアプリも開発できます。また、「OpenGL」という3Dグラフィックフレームワークも扱えるので、ゲームを作りたい人にもおすすめのGUIライブラリです。クロスプラットフォームのGUIプログラム開発にチャレンジしてみたい人は、Kivyを選ぶといいでしょう。
PyQt
「PyQt」は、C++というプログラミング言語で開発された「Qt」というGUIライブラリを、Python用に移植したものです。Qtライブラリ自体の安定性と機能性が高いため、さまざまなソフトウェアやアプリの開発に採用された実績があります。ただし、ライセンス関連の制約が厳しい部分があるので、基本的にはTkinterのほうがおすすめです。
wxPython
「wxPython」は、C++で開発されたGUIライブラリ「wxWidgets」を、Python向けに移植したものです。クロスプラットフォームに対応していることや、洗練されたAPI・ライブラリが魅力で、安定した動作に定評があります。ハイレベルなGUIプログラムをPythonで開発したい人におすすめですが、初心者の方はまずTkinterで基礎を身につけるほうがいいでしょう。
Pyglet
「Pyglet」は、クロスプラットフォーム対応のGUIライブラリです。マルチウィンドウやマルチモニターなどの便利な機能や、さまざまな画像・動画・音楽ファイルを扱えることが魅力です。その機能性から、ゲームやマルチメディアアプリの開発に使われることが多いですが、使いやすさの点では後述するPygameに軍配が上がります。
Pygame
「Pygame」は、Pythonでゲームを作るためのGUIライブラリとして有名です。Pygameを使うと、ハードウェアに近い部分を簡単に扱えるようになるため、専門知識がなくても高度なグラフィック処理が行えます。2Dグラフィックス専用ですが、工夫次第で魅力的なゲームが作れるので、ゲームプログラミングにチャレンジしてみたい人におすすめです。
【基礎編】「Tkinter」でGUIプログラミングを行う方法

Pythonで使えるGUIライブラリを複数ご紹介しましたが、初心者の方には使いやすさ・習得しやすさなどの点から「Tkinter」がおすすめです。Tkinterでは、「ウィジェット(コンポーネント)」と呼ばれる部品を組み合わせて、グラフィカルなプログラムを構成していきます。今回は、TkinterでGUIプログラミングを行うために必要な、基本的な知識・テクニックを以下11個のステップに分けて解説します。
- ステップ1:「ウインドウ」を作成する
 - ステップ2:「ラベル」でテキストを表示する
 - ステップ3:「ボタン」でユーザーの操作を受け付ける
 - ステップ4:「テキストボックス」でユーザーの入力を受け付ける
 - ステップ5:「テキストフィールド」で自由に文章を入力する
 - ステップ6:「チェックボックス」でユーザーの選択を取得する
 - ステップ7:「ラジオボタン」でひとつだけ選べるようにする
 - ステップ8:「スライダー」でシームレスに値を変更する
 - ステップ9:「カンバス」で図形や画像を表示する
 - ステップ10:「メッセージボックス」でユーザーに情報を知らせる
 - ステップ11:「ダイアログボックス」でファイル入出力を行う
 
ステップ1:「ウインドウ」を作成する
GUIアプリの基本となるのが「ウインドウ」です。Tkinterでは、「Frame(フレーム)」コンポーネントを作成することで、自動的にウインドウの作成と表示ができます。詳細は以下のサンプルコードのとおりです。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果

本プログラムでは、「tkinter.Frameクラス」を継承した「Applicationクラス」を定義しています。そのため、main関数内でApplicationクラスのインスタンスを生成した時点で、フレームウィジェットが表示されます。
なお、ソースコードの最後で「mainloop関数」を呼び出していますが、これはTkinter上で発生するさまざまなイベントを処理するために必要なものです。たとえば、マウス移動やボタンクリックなどのときに、専用の関数が呼び出されて柔軟な処理ができます。
ステップ2:「ラベル」でテキストを表示する
GUIプログラムでユーザーに情報を伝えるためには、「テキスト」の表示が欠かせません。Tkinterでは、「Label(ラベル)」というコンポーネント(ウィジェット)を使うことで、自由自在にテキストを操作できます。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    # ラベルを作成する
    label = tkinter.Label(self.master, text = "テストラベル(ここに任意のテキストが表示されます)")
    # ラベルを配置する
    label.pack()
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果

本プログラムでは、「title関数」でウインドウのタイトル、「geometry関数」でウインドウのサイズを指定しています。geometry関数で指定するサイズは、タイトルバーを含まない、ウインドウ内側の描画部分のサイズです。
なお、Tkinterでコンポーネント(ウィジェット)を作成するときは、第1引数に親となるフレームオブジェクトを指定します。作成後は「pack関数」を呼び出すことで、簡単に配置・表示できます。
ステップ3:「ボタン」でユーザーの操作を受け付ける
GUIプログラムというと、「ボタン」を思い浮かべる人が多いことでしょう。Tkinterでは、「Button(ボタン)」コンポーネントを作成し、「コールバック関数」を指定することで、柔軟なボタン制御ができます。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    # ラベルの文字列を設定する
    self.text = tkinter.StringVar(value = "ボタンを押してください")
    # ラベルを作成する
    label = tkinter.Label(self.master, textvariable = self.text)
    # ラベルを配置する
    label.pack()
    # ボタンを作成する
    button = tkinter.Button(self.master, # フレームコンポーネント
                            text = "テストボタン", # ボタンの名前
                            command = self.button_clicked) # ボタンクリック時に呼び出されるメソッド
        
    # ラベルを配置する
    button.pack()
  def button_clicked(self):
    self.text.set("ボタンが押されました!")
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果
ボタンを押す前

ボタンを押した後

ボタンの作成時に、引数で「command = self.button_clicked」と設定していることがポイントです。これは、ボタンがクリックされたときに呼び出されるコールバック関数です。button_clicked関数は自身で定義し、今回は「ボタンが押されました!」と表示します。
なお、本プログラムではボタンの状況をわかりやすく表示するために、ラベルも作成しています。必要に応じてテキストを変更するために、テキスト用の変数を用意し、ラベル作成時に「textvariable = self.text」と指定していることがポイントです。「self.text変数」を変更すれば、自動的にラベルの表示内容も変わります。
ステップ4:「テキストボックス」でユーザーの入力を受け付ける
ユーザーがテキストを入力できる「テキストボックス」も、GUIプログラムでは重要です。Tkinterでは、「Entry(エントリー)」というコンポーネントを使用すると、ユーザーの入力を受け付けられます。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    # 表示する値
    self.entry_text = tkinter.StringVar() 
    # テキストボックスを作成する
    entry = tkinter.Entry(self.master, # フレームオブジェクト
                          width = 30, # ウィジェットの横幅を文字数で指定する
                          justify = tkinter.LEFT, # 書式を「LEFT(左寄せ)」「RIGHT(右寄せ)」「CENTER(中央寄せ)」から選ぶ
                          textvariable = self.entry_text) # ユーザー入力を格納する変数
    
    # 「入力」と「消去」のボタンを作成する
    button_input = tkinter.Button(self.master, text = "入力", command = self.button_input_clicked)
    button_clear = tkinter.Button(self.master, text = "消去", command = self.button_clear_clicked)
    # ラベルの文字列を設定する
    self.label_text = tkinter.StringVar(value = "ボタンを押してください")
    # ラベルを作成する
    label = tkinter.Label(self.master, textvariable = self.label_text)
    # テキストボックスを配置する
    entry.pack()
    # 各ボタンを配置する
    button_input.pack()
    button_clear.pack()
    # ラベルを配置する
    label.pack()
  # 「入力」ボタンが押されたときの挙動を制御する
  def button_input_clicked(self):
    self.label_text.set("入力されたテキストは「" + self.entry_text.get() + "」です")
  
  # 「消去」ボタンが押されたときの挙動を制御する
  def button_clear_clicked(self):
    self.label_text.set("テキストが消去されました")
    self.entry_text.set("")
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果
「入力」ボタンを押したとき

「消去」ボタンを押したとき

テキストボックスの作成時は、書式や横幅などを指定することが大切です。重要なポイントは「textvariable = self.entry_text」という部分で、こうすることでユーザーの入力値が「self.entry_text変数」に格納されます。
入力値を取得する処理は、「button_input_clicked関数」および「button_clear_clicked関数」で行っています。「get」はテキストボックスの入力値を取得し、「set」はテキストボックスの初期値を設定するための関数です。
ステップ5:「テキストフィールド」で自由に文章を入力する
先ほどの「テキストボックス」は、限られた文字数しか入力できませんでした。Windowsの「メモ帳」のような感じで、自由にテキストを入力できるようにしたい場合は、「Text(テキスト)」を使いましょう。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    # テキストフィールドを作成する
    self.text = tkinter.Text()
    # テキストフィールドを配置する
    self.text.pack()
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果

テキストフィールドの作成は単純です。ユーザーの入力値を取得したい場合は、get関数を「get(0.0, tkinter.END)」という構文で呼び出します。このテキストフィールドを活用したプログラム例については、のちほどあらためて紹介します。
ステップ6:「チェックボックス」でユーザーの選択を取得する
特定の項目についてユーザーのチェックや選択を求めたい場合は、「チェックボックス」が便利です。Tkinterでは、「Checkbutton(チェックボタン)」ウィジェットを作成し、以下のように使います。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # リストのクリック関数を定義するリストを辞書形式で作成する
    self.checkbox_functions = {"checked1": self.checkbox_checked_1,
                               "checked2": self.checkbox_checked_2,
                               "checked3": self.checkbox_checked_3}
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    
    # チェックボックスリストを作成する
    self.is_checked = []
    # チェックボックスの値を設定する
    for i in range(3):
      self.is_checked.append(tkinter.BooleanVar(value = True)) # 事前に初期値を設定しない場合は「Flase」になる
    
    # チェックボックス配列を作成する
    self.checkboxes = []
    
    # チェックボックスを作成する
    for i in range(3):
      self.checkboxes.append(tkinter.Checkbutton(self.master, # フレームオブジェクト
                                                 text = "チェックボックス" + str(i + 1), # チェックボックスの名前
                                                 command = self.checkbox_functions["checked" + str(i + 1)], # クリックされたときに呼び出される関数
                                                 variable = self.is_checked[i])) # チェック状態を格納する変数
      
    # 各チェックボックスを配置する
    for i in range(3):
      self.checkboxes[i].pack()
    # ラベルの文字列を設定する
    self.label_text = tkinter.StringVar(value = "チェックしてください")
    # ラベルを作成する
    label = tkinter.Label(self.master, textvariable = self.label_text)
    # ラベルを配置する
    label.pack()
    
  # 「チェックボックス1」が押されたときの挙動を制御する
  def checkbox_checked_1(self):
    self.label_text.set("チェックボックス1が「" + str(self.is_checked[0].get()) + "」になりました")
    
  # 「チェックボックス2」が押されたときの挙動を制御する
  def checkbox_checked_2(self):
    self.label_text.set("チェックボックス2が「" + str(self.is_checked[1].get()) + "」になりました")
    
  # 「チェックボックス3」が押されたときの挙動を制御する
  def checkbox_checked_3(self):
    self.label_text.set("チェックボックス3が「" + str(self.is_checked[2].get()) + "」になりました")
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果
チェックボックスをクリックする前

チェックボックスをクリックした後

本プログラムでは、辞書やリストを使用していますが、これは単に複数のチェックボックスを効率的に管理するためのものです。チェックボタンの作成時は、「command引数」でクリック時に呼び出される関数を、「variable引数」でチェック状態を格納する変数を指定します。これまでのサンプルプログラムどおり、ウィジェットが操作されたときにラベルの表示を変更しています。
ステップ7:「ラジオボタン」でひとつだけ選べるようにする
先ほどの「チェックボックス」は、複数の選択肢を自由に選べましたが、ユーザーの選択をひとつに限定したい場合は「ラジオボタン」を使いましょう。Tkinterでは、「Radiobutton(ラジオボタン)」ウィジェットを作成することで、ユーザーに選択肢を提供できます。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    
    # ラジオボタンの初期値を設定する
    self.radio_value = tkinter.IntVar(value = 1) # 事前に初期値を設定しない場合は「0」になる
    
    # ラジオボタン配列を作成する
    self.radio_buttons = []
    
    # ラジオボタンを作成する
    for i in range(3):
      self.radio_buttons.append(tkinter.Radiobutton(self.master, # フレームオブジェクト
                                                    text = "ラジオボタン" + str(i + 1), # ラジオボタンの名前
                                                    command = self.radio_clicked, # クリックされたときに呼び出される関数
                                                    variable = self.radio_value, # 選択の状態を設定する変数
                                                    value = i )) # ラジオボタンに割り当てる値
    # 各ラジオボタンを配置する
    for i in range(3):
      self.radio_buttons[i].pack()
    # ラベルの文字列を設定する
    self.label_text = tkinter.StringVar(value = "選択してください")
    # ラベルを作成する
    label = tkinter.Label(self.master, textvariable = self.label_text)
    # ラベルを配置する
    label.pack()
    
  # 「ラジオボタン」が押されたときの挙動を制御する
  def radio_clicked(self):
    self.label_text.set("ラジオボタン" + str(self.radio_value.get() + 1) + "が選択されました")
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果
ラジオボタンを選択する前

ラジオボタンを選択した後

ラジオボタンの生成時は、チェックボタンと同じく引数で「command」と「variable」に、関数と変数を指定します。ユーザーの選択肢は、0から始まる整数値で格納されるので、「get関数」で取得しましょう。
ステップ8:「スライダー」でシームレスに値を変更する
GUIプログラムでは、ユーザーが自由に数値を選択できると便利な場面があるので、「スライダー」が役立ちます。Tkinterでは、「Scale(スケール)」コンポーネントを使うと、ユーザーがシームレスに値を変更できる機能を提供できます。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    # スライダーの初期値を設定する
    self.slider_value = tkinter.DoubleVar()
    
    # スライダーを作成する
    slider = tkinter.Scale(self.master, # フレームコンポーネント
                           variable = self.slider_value, # スライダーの値を格納する変数
                           command = self.slider_scrolled, # スライダー変更時に呼び出される関数
                           orient = tkinter.HORIZONTAL, # 配置の向きを「HORIZONTAL(水平)」もしくは「VERTICAL(垂直)」から選ぶ
                           length = 500, # スライダー全体の長さ
                           width = 30, # スライダー全体の太さ
                           sliderlength = 50, # スライダーの「つまみ」の幅
                           from_ = 0, # スライダーの最小値
                           to = 500, # スライダーの最大値
                           resolution = 0.5, # スライダーの値の分解能(初期値は「1」)
                           tickinterval = 50) # スライダーの目盛りの分解能(初期値は「0」で表示なし)
    
    # スライダーを配置する    
    slider.pack()
    # ラベルの文字列を設定する
    self.label_text = tkinter.StringVar(value = "スライダー値:" + str(self.slider_value.get()))
    # ラベルを作成する
    label = tkinter.Label(self.master, textvariable = self.label_text)
    # ラベルを配置する
    label.pack()
  # スライダーを変更したときの挙動を制御する
  def slider_scrolled(self, event = None):
    # ラベルの文字列を設定する
    self.label_text.set("スライダー値:" + str(self.slider_value.get()))
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果

スライダーの作成時は、サイズや向きなどを細かく設定できます。「command引数」や「variable引数」を設定する点は、ほかのウィジェットと同じです。ユーザーがスライダーを操作すると、commandで指定した関数が呼び出され、画面情報を更新できるようになります。
ステップ9:「カンバス」で図形や画像を表示する
GUIプログラムに欠かせないのが、図形や画像を表示する機能です。これらの要素は、フレーム(ウインドウ)には直接表示できないので、「Canvas(カンバス)」を配置することで、簡単に図形などを表示できる。なお、画像を表示する方法については「応用編」で詳しく解説します。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    # カンバスを作成する
    canvas = tkinter.Canvas(self.master, # フレームコンポーネント
                            width = 320, # カンバスの幅
                            height = 240, # カンバスの高さ
                            bg = "black") # カンバスの背景色
    # カンバスを配置する
    canvas.pack()
    
    # カンバスを配置する
    canvas.create_oval(60, 60, 260, 180, fill = "blue")
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果

カンバスの作成時は、カンバスの背景色を指定できます。直線や楕円などを描画したいときは、カンバス変数の「Create_line」「Create_rectangle」「Create_oval」などの関数を呼び出すと簡単でしょう。「fill引数」に色を指定すると、図形を塗りつぶすこともできます。
ステップ10:「メッセージボックス」でユーザーに情報を知らせる
GUIプログラムでは、ユーザーにメッセージボックスで情報を伝えたいときがあるでしょう。たとえば、Windowsのアプリケーションでは、処理完了時やエラー発生時などに、小さなボックスにメッセージが表示されることがあります。Tkinterでは、「messagebox」に備わっている関数・メソッドを呼び出すと、さまざまな種類のメッセージボックスを呼び出せます。
//サンプルプログラム
# coding: Shift-JIS # Tkinterライブラリを使用する import tkinter # messageboxの機能を使用する from tkinter import messagebox # 「showinfo」タイプのメッセージボックスを表示する messagebox.showinfo(title = "showinfo", message = "メッセージ") # 「showwarning」タイプのメッセージボックスを表示する messagebox.showwarning(title = "showwarning", message = "メッセージ") # 「showerror」タイプのメッセージボックスを表示する messagebox.showerror(title = "showerror", message = "メッセージ") # 「askquestion」タイプのメッセージボックスを表示する messagebox.askquestion(title = "askquestion", message = "メッセージ") # 「askokcancel」タイプのメッセージボックスを表示する messagebox.askokcancel(title = "askokcancel", message = "メッセージ") # 「askyesno」タイプのメッセージボックスを表示する messagebox.askyesno(title = "askyesno", message = "メッセージ") # 「askretrycancel」タイプのメッセージボックスを表示する messagebox.askretrycancel(title = "askretrycancel", message = "メッセージ") # 「askyesnocancel」タイプのメッセージボックスを表示する messagebox.askyesnocancel(title = "askyesnocancel", message = "メッセージ")
//実行結果
「showinfo」タイプのメッセージボックス

「showwarning」タイプのメッセージボックス

「showerror」タイプのメッセージボックス

「askquestion」タイプのメッセージボックス

「askokcancel」タイプのメッセージボックス

「askyesno」タイプのメッセージボックス

「askretrycancel」タイプのメッセージボックス

「askyesnocancel」タイプのメッセージボックス

Tkinterで利用できる代表的なメッセージボックスは以下のとおりです。
| メッセージボックスの種類 | 概要 | 戻り値 | 
|---|---|---|
| showinfo | 情報を表示する | OK | 
| showwarning | 警告を表示する | OK | 
| showerror | エラーを表示する | OK | 
| askquestion | 「はい」「いいえ」 2択の質問を表示する  | 
Yes, No | 
| askokcancel | 「OK」「キャンセル」 2択の質問を表示する  | 
True, False | 
| askyesno | 「はい」「いいえ」 2択の質問を表示する  | 
True, False | 
| askretrycancel | 「再試行」「キャンセル」 2択の質問を表示する  | 
True, False | 
| askyesnocancel | 「はい」「いいえ」「キャンセル」 3択の質問を表示する  | 
True, False, None | 
ステップ11:「ダイアログボックス」でファイル入出力を行う
GUIプログラミングを行う際は、ファイルを扱うことも多いです。ファイル操作時は、ダイアログボックスを開いて、オープン・セーブするファイルをユーザーに選んでもらう必要があります。Tkinterでは、filedialogの「askopenfilename関数」で開くダイアログを、「asksaveasfilename関数」で保存するダイアログを表示できます。
今回は、先ほど解説した「テキストフィールド」と「ダイアログボックス」を組み合わせて、簡易的なメモ帳を作ってみましょう。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# filedialogの機能を使用する
from tkinter import filedialog
# osライブラリを使用する
import os
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    # テキストフィールドを作成する
    self.text = tkinter.Text()
    # テキストフィールドを配置する
    self.text.pack()
    
    # 「開く」と「保存する」のボタンを作成する
    button_open = tkinter.Button(self.master, text = "開く", command = self.button_open_clicked)
    button_save = tkinter.Button(self.master, text = "保存する", command = self.button_save_clicked)
    # 各ボタンを配置する
    button_open.pack()
    button_save.pack()
  # 「開く」ボタンが押されたときの挙動を制御する
  def button_open_clicked(self):
    # ファイルオープン用のダイアログボックスを開く
    filename = filedialog.askopenfilename(title = "ファイルを開く", # ダイアログのタイトル
                                          filetypes = [("テキストファイル", ".txt")], # 有効な拡張子
                                          initialdir = ".\\") # 自身のディレクトリ(今回はカレントディレクトリに設定)
    
    # テキストファイルを読み込む
    if os.path.exists(filename):
      file = open(filename, "r")
      data = file.read()
      file.close()
      # テキストフィールドにデータを表示する
      self.text.delete(0.0, tkinter.END)
      self.text.insert(0.0, data)
  
  # 「保存する」ボタンが押されたときの挙動を制御する
  def button_save_clicked(self):
    # ファイルセーブ用のダイアログボックスを開く
    filename = filedialog.asksaveasfilename(title = "ファイルを保存する", # ダイアログのタイトル
                                            filetypes = [("テキストファイル", ".txt")], # 有効な拡張子
                                            initialdir = ".\\") # 自身のディレクトリ(今回はカレントディレクトリに設定)
    # テキストファイルに書き込む
    if filename:
      file = open(filename, "w")
      file.write(self.text.get(0.0, tkinter.END))
      file.close()
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果
プログラムで作成したテキスト

ファイルを保存するダイアログボックス

保存したファイルはWindowsの「メモ帳」などでも使用可能

ダイアログボックスの作成時は、「filetypes」でユーザーが選択できるファイルの拡張子を、「initialdir」で初期ディレクトリを指定できます。ファイル読み込み・保存時は、指定されたファイルをopen関数で開き、「read関数」や「write関数」で処理します。
なお、テキストフィールドは「delete(0.0, tkinter.END)」ですべて削除し、ファイルから読み込んだデータは「insert(0.0, data)」で反映することが可能です。
Tkinterで画像データを扱う方法

Tkinterで画像データを扱うためには、「画像ライブラリ」を使用する必要があります。Pythonで最も広く活用されている画像ライブラリが、「Pillow(PIL)」です。Pillowがあれば、ハイレベルな画像処理ができますが、Pythonに標準的に備わっている機能ではないので、個別にインストールする必要があります。
PythonのIDEとして「Visual Studio」を使用している場合は、以下の手順でPillowを導入できます。まずは管理者権限で「コマンドプロンプト」を開き、以下のコマンドを入力しましょう。
このコマンドは、Pythonのパッケージ「PIP」を最新バージョンにアップデートするためのものです。アップデートが完了したら、Visual StudioのIDEを開き、Pythonで以下のソースコードを実行します。
print(sys.executable)
実行結果として、Python本体の実行ファイルがある場所が表示されるので、メモ帳などにコピー&ペーストしておきましょう。

もう一度管理者権限でコマンドプロンプトを開き、先ほど確認した「Python.exe」実行ファイルのディレクトリに、「cd」コマンドを使って移動します。以下のように、ファイルパスから最後の「\python.exe」を除くことが重要です。
次のコマンドを入力すると、Visual Studioに「Pillow(PIL)」をインストールできます。
以下のような画面が表示されたら、Pillowのインストールの完了です。あとはさまざまな画像処理にチャレンジしてみましょう。

【応用編】「Tkinter」の発展的なGUIプログラミング

先ほど解説した基礎知識を応用して、より発展的なGUIプログラミングにチャレンジしてみましょう。今回は以下のサンプルプログラムを紹介します。
- Tkinterと「Pillow(PIL)」を組み合わせて画像を表示する
 - 「画像ビューア」を作成して画像を閲覧できるようにする
 - 「PDF結合プログラム」でPDFファイルを便利に扱う
 
Tkinterと「Pillow(PIL)」を組み合わせて画像を表示する
TkinterとPillow(PIL)を組み合わせると、以下のように画像を表示できます。手順は単純で、前述したようにカンバスを配置して、そこに画像データを配置するだけです。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# PILライブラリを使用する
from PIL import Image, ImageTk
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    
    # カンバスを作成する
    self.canvas = tkinter.Canvas(self.master)
    
    # カンバスを配置する
    self.canvas.pack(expand = True, fill = tkinter.BOTH) # 画像に合わせてカンバスのサイズを調整する
    # PillowのImage.open関数で画像ファイルを開く
    self.image_file = Image.open(".\\Sample.png")
    # PhotoImage関数で画像データを変換する
    self.photo_image = ImageTk.PhotoImage(image = self.image_file)
    # カンバスのサイズを取得する
    self.update() # まずはウインドウを更新する
    width = self.canvas.winfo_width()
    height = self.canvas.winfo_height()
    # 画像を描画する
    self.canvas.create_image(width / 2, # 画像表示X座標
                             height / 2, # 画像表示Y座標      
                             image = self.photo_image) # 画像データ
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果

カンバスを「pack関数」で配置するときは、「expand = True」「fill = tkinter.BOTH」を指定すると、画像に合わせてカンバスサイズが自動的に調整されるので便利です。
画像の読み込みは、まずPillowの「Image.open関数」でファイルパスを指定し、「ImageTk.PhotoImage関数」でピクセルデータに変換します。そのうえで、カンバスの「create_image関数」の「image引数」に、ピクセルデータを指定すると画像を表示できます。
「画像ビューア」を作成して画像を閲覧できるようにする
先ほどの内容を踏まえて、画像を読み込んで表示するための「画像ビューア」を作ってみましょう。簡易的なものではありますが、画像の表示サイズを拡大縮小したり、表示位置を移動したりする機能も搭載しているので便利です。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# filedialogの機能を使用する
from tkinter import filedialog
# PILライブラリを使用する
from PIL import Image, ImageTk
# numpyライブラリを使用する
import numpy
# osライブラリを使用する
import os
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
     
    self.image_data = None # 表示する画像データを格納するための変数
    self.background_color = "#000000" # 背景色を設定するための変数
    # ウインドウのタイトルを設定する
    self.master.title("画像ビューア")
    # ウインドウのサイズを設定する
    self.master.geometry("640x480")
    # メニューを作成する
    self.create_menu()
    # ウィジェットを作成する
    self.create_widget()
    
  # メニューバーを作成する関数
  def create_menu(self):
    # Menuクラスからmenuインスタンスを生成する
    self.menu = tkinter.Menu(self)
 
    # 「ファイル」項目を追加する
    self.file = tkinter.Menu(self.menu, tearoff = tkinter.OFF) # 切り取り線は非表示にする
    self.menu.add_cascade(label = "ファイル", menu = self.file)
    # 「ファイル」配下に「開く」項目を追加し、セパレーターを付加する
    self.file.add_command(label = "開く", command = self.menu_open_clicked)
    self.file.add_separator()
    
    # 「ファイル」配下に「終了する」項目を追加する
    self.file.add_command(label = "終了する", command = self.menu_quit_clicked)
    # メニューバーを配置する
    self.master.config(menu = self.menu)
 
  # ウィジェットを作成する関数
  def create_widget(self):
    # ステータスバーを生成する
    self.status = tkinter.Frame(self.master)
    # 画像情報を表示するためのラベルを作成する
    self.image_info = tkinter.Label(self.status, relief = tkinter.SUNKEN, text = "ピクセル情報")
    # ラベルを配置する
    self.image_info.pack()
    # ステータスバーを配置する
    self.status.pack(side = tkinter.BOTTOM, fill = tkinter.X)
    # カンバスの作成と配置を行う
    self.canvas = tkinter.Canvas(self.master, background = self.background_color)
    self.canvas.pack(expand = True, fill = tkinter.BOTH)
    # マウスイベントを設定する
    self.master.bind("", self.mouse_move) # マウスを動かしたとき
    self.master.bind("", self.mouse_drag) # マウスをドラッグしたとき
    self.master.bind("", self.mouse_down) # マウスの左ボタンをクリックしたとき
    self.master.bind("", self.mouse_double) # マウスの左ボタンをダブルクリックしたとき
    self.master.bind("", self.mouse_wheel) # マウスホイールを動かしたとき
  # 「開く」メニューがクリックされたときの動作
  def menu_open_clicked(self, event = None):
    # ファイルを開く
    filename = tkinter.filedialog.askopenfilename(filetypes = [("画像ファイルImage file", ".bmp .png .jpg .tif")], # 拡張子を指定する
                                                  initialdir = os.getcwd()) # 初期ディレクトリは「カレントディレクトリ」にする
    # 画像ファイルを開く
    self.open_image(filename)
    
  # 「終了する」メニューがクリックされたときの動作
  def menu_quit_clicked(self):
    # ウインドウを閉じてプログラムを終了する
    self.master.destroy() 
  # 画像ファイルを開いて画面に表示する
  def open_image(self, filename):
    # ファイルパスが不正であれば何もしない
    if not filename:
      return;
    # カレントディレクトリを設定する
    os.chdir(os.path.dirname(filename))
    # 画像ファイルを開く
    self.image_data = Image.open(filename)
    # 画面全体に画像を表示するために、「アフィン変換行列」を設定する
    self.adjust_image(self.image_data.width, self.image_data.height)
    # 画像を表示する
    self.render_image()
  # 画像の表示位置とサイズをウィジェットに合わせる
  def adjust_image(self, image_width, image_height):
    # 現在のカンバスサイズを取得する
    canvas_width = self.canvas.winfo_width()
    canvas_height = self.canvas.winfo_height()
    # 変換行列を初期化する
    self.reset_transform()
    # 行列算出用の変数を初期化する
    scale = 1.0
    offset_x = 0.0
    offset_y = 0.0
    if (canvas_width * image_height) > (image_width * canvas_height):
      # ウィジェットが横長の場合は、縦方向を合わせて、横方向は中央位置に合わせる
      scale = canvas_height / image_height
      offset_x = (canvas_width - image_width * scale) / 2
    else:
      # ウィジェットが縦長の場合は、横方向を合わせて、縦方向は中央位置に合わせる
      scale = canvas_width / image_width
      offset_y = (canvas_height - image_height * scale) / 2
    # 拡大縮小と平行移動の行列を算出する
    self.scaling(scale)
    self.translate(offset_x, offset_y)
    
  def render_image(self):
    # 画像を開いていないときは何もしない
    if not self.image_data:
      return
    
    # 画面をすべて消去する
    self.canvas.delete("all")
    # カンバスサイズを取得する
    canvas_width = self.canvas.winfo_width()
    canvas_height = self.canvas.winfo_height()
    # 現在のアフィン変換行列の逆行列を算出する
    inverse_matrix = numpy.linalg.inv(self.matrix)
    # アフィン変換済みの画像データを作成する
    converted = self.image_data.transform((canvas_width, canvas_height), # 変換後の画像サイズを指定する
                                          Image.AFFINE, # アフィン変換を行う
                                          tuple(inverse_matrix.flatten()), # アフィン変換行列を一次元タプルへ変換する
                                          Image.BILINEAR, # 補間方法は「バイリニア」に設定する 
                                          fillcolor = self.background_color) # 背景色を指定する
    # 変換済みのデータを画像形式で保持する
    self.image = ImageTk.PhotoImage(image = converted)
    # カンバスに画像を描画する
    # カンバス(0,0)の位置に画像の左上を合わせる
    self.canvas.create_image(0, 0, anchor='nw', image=self.image)            
  # 変換行列を初期化する
  def reset_transform(self):
    # 変換行列を3x3の「単位行列」に戻す
    self.matrix = numpy.eye(3)
  # 平行移動の行列を算出する
  def translate(self, offset_x, offset_y):
    # まず3x3の「単位行列」を作成する
    translation = numpy.eye(3)
    
    # 平行移動の行列を算出する
    translation[0, 2] = float(offset_x)
    translation[1, 2] = float(offset_y)
    # 現在の変換行列と合成する
    self.matrix = numpy.dot(translation, self.matrix)
  # 拡大縮小の行列を算出する
  def scaling(self, scale):
    # まず3x3の「単位行列」を作成する
    scaling = numpy.eye(3)
    
    # 拡大縮小の行列を算出する
    scaling[0, 0] = scale
    scaling[1, 1] = scale
    
    # 現在の変換行列と合成する
    self.matrix = numpy.dot(scaling, self.matrix)
  # 座標値(x, y)を中心に画像の表示サイズを拡大縮小する
  def scaling_center(self, scale, x, y):
    # 平行移動行列を原点に戻す
    self.translate(-x, -y)
    # 拡大縮小と平行移動の行列を作り直す
    self.scaling(scale)
    self.translate(x, y)
  # マウスを動かしたときの処理
  def mouse_move(self, event):
    # 画像を開いていないときは何もしない
    if not self.image_data:
      return
    # マウス座標を取得して、numpyベクトルに格納する
    mouse_pos = numpy.array([event.x, event.y, 1])
    # 現在のアフィン変換行列の逆行列を算出する
    inverse_matrix = numpy.linalg.inv(self.matrix)
    # 画像内のマウス座標値を行列演算で算出する
    image_pos = numpy.dot(inverse_matrix, mouse_pos)
    # 座標値の小数点以下を切り捨てる
    x = int(numpy.floor(image_pos[0]))
    y = int(numpy.floor(image_pos[1]))
    # マウス座標が画像内であれば、ピクセル座標値と色情報を表示する
    if (x >= 0 and x < self.image_data.width) and (y >= 0 and y < self.image_data.height): # ピクセル情報を取得して表示する pixel = self.image_data.getpixel((x, y)) self.image_info["text"] = f"ピクセル({x}, {y}) = {pixel}" else: # 範囲外の場合は初期文字列を表示する self.image_info["text"] = "ピクセル情報" # マウスをドラッグしたときの処理 def mouse_drag(self, event): # 画像を開いていないときは何もしない if not self.image_data: return # マウス移動の差分値だけアフィン変換行列を平行移動する self.translate(event.x - self.__old_event.x, event.y - self.__old_event.y) # 画像を描画する self.render_image() # 現在のイベント内容を保持する self.__old_event = event def mouse_down(self, event): # 現在のイベント内容を保持する self.__old_event = event def mouse_double(self, event): # 画像を開いていないときは何もしない if not self.image_data: return # 画像の描画位置をリセットする self.adjust_image(self.image_data.width, self.image_data.height) self.render_image() def mouse_wheel(self, event): # 画像を開いていないときは何もしない if not self.image_data: return if (event.delta >= 0):
      # 上回転の場合は拡大する
      self.scaling_center(1.25, event.x, event.y)
    else:
      # 下回転の場合は縮小する
      self.scaling_center(0.75, event.x, event.y)
        
    # 画像を描画する
    self.render_image()
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果

Visual StudioをIDEとして使用していると、実行時に「No module named ‘numpy’」というエラーが出る場合があります。そのような場合は、「コマンドプロンプト」を管理者権限で実行し、以下のコマンドを入力しましょう。
今回はメニューバーも作成しています。「create_menu関数」に記載しているとおり、大元のメニューバーを「tkinter.Menu関数」で生成し、それを親として配下の項目を追加していきます。メニュー項目を追加するときは「add_cascade関数」、処理を行うコマンドを追加するときは「add_command関数」を使用しましょう。最後に、「master.config関数」でメニューバーを配置すれば、実際にGUIプログラムで表示可能です。
マウスイベントは「self.master.bind関数」で追加でき、第1引数に対象となる処理、第2引数にコールバック関数を指定します。画像表示サイズや位置を調整するためには、「アフィン変換行列」を作成します。ユーザーのマウス操作時のコールバック関数で、拡大縮小・平行移動の行列を算出し、transform関数で指定することで画面に反映可能です。
「PDF結合プログラム」でPDFファイルを便利に扱う
PDFファイルを取り扱うときは、複数のファイルを結合させたいときがあります。ただし、PythonでPDFファイルを操作するときは、「PyPDF4ライブラリ」が必要です。標準では備わっていない機能なので、以下の手順で「PyPDF4」をインストールしましょう。

以下のサンプルコードは、複数のPDFファイルをひとつに結合するためのものです。複数のPDFファイルをまとめて選択でき、昇順・降順も選択できるので便利です。
//サンプルプログラム
# coding: Shift-JIS
# Tkinterライブラリを使用する
import tkinter
# ttkの機能を利用する
from tkinter import ttk
# filedialogの機能を利用する
from tkinter import filedialog
# messageboxの機能を利用する
from tkinter import messagebox
# PyPDF4ライブラリを使用する
import PyPDF4
# pathlibの機能を利用する
from pathlib import Path
# アプリケーションクラスを定義する
# 利便性を高めるために、「tkinter.Frameクラス」を継承する
class Application(tkinter.Frame):
  # コンストラクタを定義する
  def __init__(self, master = None):
    # 親クラスのコンストラクタを呼び出す
    super().__init__(master)
    # ウインドウのタイトルを設定する
    self.master.title("GUIプログラミング")
    # ウインドウのサイズを設定する
    self.master.geometry("600x100")
    
    # ウインドウのリサイズを禁止する
    self.master.resizable(0, 0)
    
    # ディレクトリを格納する変数
    self.directory = tkinter.StringVar()
    # 「フォルダ」のウィジェットを作成する
    self.folder_label = ttk.Label(self.master, text = "フォルダ")
    self.folder_box = ttk.Entry(self.master, textvariable = self.directory)
    self.folder_button = ttk.Button(self.master, text = "参照", command = self.set_directory)
    # 「結合順」のウィジェットを作成する
    self.order_label = ttk.Label(self.master, text = "結合順")
    self.order_select = ttk.Combobox(self.master, values = ["昇順", "降順"])
    self.order_select.current(0)
    # 「実行」のウィジェットを作成する
    self.execute_button = ttk.Button(self.master, text = "実行", command = self.execute)
    # ウィジェットを配置する
    self.folder_label.grid(column = 0, row = 0)
    self.folder_box.grid(column = 1, row = 0, ipadx = 100)
    self.folder_button.grid(column = 2, row = 0, padx = 30)
    self.order_label.grid(column = 0, row = 1)
    self.order_select.grid(column = 1, row = 1, padx = 100)
    self.execute_button.grid(column = 2, row = 1, padx = 30)
    # ウィジェットの伸縮を設定する
    self.master.columnconfigure(0, weight = 1)
    self.master.columnconfigure(1, weight = 1)
    self.master.rowconfigure(1, weight = 1)
  # ディレクトリを設定する
  def set_directory(self):
    # ファイルダイアログを開いてディレクトリを設定する
    path = filedialog.askdirectory()
    self.directory.set(path)
    
  # PDFファイルの結合処理を実行する
  def execute(self):
    # 読み込み元のPDFディレクトリを取得する
    source_directory = self.directory.get()
    # 保存先のPDFディレクトリを指定する
    destination_directory = filedialog.asksaveasfilename(filetypes=[("PDFファイル", "*.pdf")], defaultextension=".pdf")
    
    # いずれも有効な場合のみ結合処理を行う
    if source_directory and destination_directory:
      # 結合処理が完了したらメッセージを表示する
      self.merge_pdf_files(source_directory, destination_directory)
      messagebox.showinfo("完了", "PDFファイルを結合しました!")
    
  def merge_pdf_files(self, source_directory, destination_directory):
    # フォルダ内のPDFファイルの一覧を作成する
    directory = Path(source_directory)
    files = sorted(directory.glob("*.pdf"), reverse = (self.order_select.get() == "降順"))
    # 「PdfFileWriter」オブジェクトを作成する
    writer = PyPDF4.PdfFileWriter()
    
    # すべてのPDFファイルを結合する
    for file in files:
      reader = PyPDF4.PdfFileReader(str(file), strict = False)
      # すべてのページを結合する
      for i in range(reader.getNumPages()):
        writer.addPage(reader.getPage(i))
    # 結合したPDFファイルを保存する
    with open(destination_directory, "wb") as output:
      writer.write(output)
    
# main関数を定義する
if __name__ == "__main__":
  # 新しいウインドウを作成する
  root = tkinter.Tk()
  # アプリケーションクラスのインスタンスを生成する
  app = Application(root)
  # メインループ関数を呼び出す
  # 内部ではTkinter上のイベントを捕捉し、自動的に適切な処理を呼び出している
  app.mainloop()
//実行結果
PDFファイルをまとめたフォルダを指定する

出力先のファイル名を指定する

メッセージが表示されてすべてのPDFファイルが結合される

本プログラムでは、ごく小さなウインドウを表示します。これまで解説したテクニックを駆使して、ボタンやダイアログなどを設定しましょう。ユーザーが読み込み元・保存先のディレクトリを選んだら、フォルダ内のPDFファイルのパスを「sorted関数」で並べ替えます。
そのうえで、「PyPDF4.PdfFileWriter関数」でPyPDFオブジェクトを生成し、すべてのページを順番どおりに結合します。最後に、PyPDFオブジェクトの「write関数」を使用して、バイナリ形式でファイルを出力しましょう。以上でPDFファイル結合プログラムの完成です。
PythonのGUIライブラリで便利なGUIプログラムを作ろう!

PythonのGUIライブラリを活用すると、グラフィカルで扱いやすいGUIプログラムが作れます。初心者でも扱いやすい「Tkinter」は、ウィジェットやコンポーネントを配置して画面を構成するため、それぞれの機能や使い方を知っておく必要があります。今回ご紹介した知識・テクニックを活用して、便利なGUIプログラムを作ってみましょう。