【Python/tkinter】Canvasに画像を表示する

まず、Canvasを作成し、画像ファイルを開き、Canvasに画像を表示するサンプルは以下のようになります。

import tkinter as tk
from PIL import ImageTk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("画像の表示")       # ウィンドウタイトル
        self.master.geometry("400x300")     # ウィンドウサイズ(幅x高さ)

        # Canvasの作成
        self.canvas = tk.Canvas(self.master)
        # Canvasを配置
        self.canvas.pack(expand = True, fill = tk.BOTH)

        # 画像ファイルを開く(対応しているファイルフォーマットはPGM、PPM、GIF、PNG)
        self.photo_image = ImageTk.PhotoImage(file = "Mandrill.png")

        # キャンバスのサイズを取得
        self.update() # Canvasのサイズを取得するため更新しておく
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()

        # 画像の描画
        self.canvas.create_image(
                canvas_width / 2,       # 画像表示位置(Canvasの中心)
                canvas_height / 2,                   
                image=self.photo_image  # 表示画像データ
                )

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

プログラムの説明

画像ファイルは PhotoImage 関数の file引数に ファイル名を指定します。ただし、PhotoImage 関数が対応している画像ファイルフォーマットは PGM、PPM、GIF、PNG の4つのみとなります。

PhotoImage関数で開いた画像データは戻り値で取得できる(上記のプログラムではself.photo_image)ので、このデータを create_image関数の image に渡すことで、Canvas上に画像を表示することができます。

PhotoImage関数が対応している画像ファイルフォーマットが4つのみというのは、画像処理のプログラムを作成するのには致命的(最低限 bmpは必要)なので、画像ファイルをPillowで開いてPhotoImageへ変換し、create_imageで画像を表示する例を次に示します。

Pillowで画像ファイルを開く例

import tkinter as tk
from PIL import Image, ImageTk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("画像の表示")       # ウィンドウタイトル
        self.master.geometry("400x300")     # ウィンドウサイズ(幅x高さ)

        # Canvasの作成
        self.canvas = tk.Canvas(self.master)
        # Canvasを配置
        self.canvas.pack(expand = True, fill = tk.BOTH)

        # PillowのPIL.Imageで画像ファイルを開く
        pil_image = Image.open("Mandrill.bmp")

        # PIL.ImageからPhotoImageへ変換する
        self.photo_image = ImageTk.PhotoImage(image=pil_image)

        # キャンバスのサイズを取得
        self.update() # Canvasのサイズを取得するため更新しておく
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()

        # 画像の描画
        self.canvas.create_image(
                canvas_width / 2,       # 画像表示位置(Canvasの中心)
                canvas_height / 2,                   
                image=self.photo_image  # 表示画像データ
                )

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

上記の例はPillowの Image.open関数で画像ファイルを開いています。このopen関数で開くことのできる画像ファイルフォーマットは、bmpやjepg, tiff, png など、ほとんどの画像ファイルを開くことができます。詳しくはこちらのページを参照ください。

あとは、Image.open関数で開いた画像データをPhotoImageで変換し、create_imageで画像を表示します。

応用編

これまで、ウィンドウの作成ウィジェットの配置MenuCanvasファイルを開くダイアログボックスと説明してきましたが、これらを使ってCanvasに画像を表示するプログラムを作成しました。

 

(作成したプログラムのイメージ)

(プログラム)

import tkinter as tk
from tkinter import filedialog

from PIL import Image, ImageTk, ImageOps  # 画像データ用

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)
        self.pack()

        self.master.title("画像の表示")       # ウィンドウタイトル
        self.master.geometry("400x300")     # ウィンドウサイズ(幅x高さ)
        
        # メニューの作成
        self.create_menu()

        # Canvasの作成
        self.back_color = "#008B8B" # 背景色
        self.canvas = tk.Canvas(self.master, bg = self.back_color)
        # Canvasを配置
        self.canvas.pack(expand = True, fill = tk.BOTH)

    def create_menu(self):
        # メニューバーの作成
        menubar = tk.Menu(self)

        # ファイル
        menu_file = tk.Menu(menubar, tearoff = False)
        menu_file.add_command(label = "画像ファイルを開く", command = self.menu_file_open_click, accelerator="Ctrl+O")
        menu_file.add_separator() # 仕切り線
        menu_file.add_command(label = "終了", command = self.master.destroy)
        # ショートカットキーの関連付け
        menu_file.bind_all("<Control-o>", self.menu_file_open_click)

        # メニューバーに各メニューを追加
        menubar.add_cascade(label="ファイル", menu = menu_file)

        # 親ウィンドウのメニューに、作成したメニューバーを設定
        self.master.config(menu = menubar)

    def menu_file_open_click(self, event=None):
        filename = filedialog.askopenfilename(
            title = "ファイルを開く",
            filetypes = [("Image file", ".bmp .png .jpg .tif"), ("Bitmap", ".bmp"), ("PNG", ".png"), ("JPEG", ".jpg"), ("Tiff", ".tif") ], # ファイルフィルタ
            initialdir = "./" # 自分自身のディレクトリ
            )
        # 画像の表示
        self.disp_image(filename)

    def disp_image(self, filename):
        '''画像をCanvasに表示する'''
        if not filename:
            return
        # PIL.Imageで開く
        pil_image = Image.open(filename)

        # キャンバスのサイズを取得
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()

        # 画像のアスペクト比(縦横比)を崩さずに指定したサイズ(キャンバスのサイズ)全体に画像をリサイズする
        pil_image = ImageOps.pad(pil_image, (canvas_width, canvas_height), color = self.back_color)

        #PIL.ImageからPhotoImageへ変換する
        self.photo_image = ImageTk.PhotoImage(image=pil_image)

        # 画像の描画
        self.canvas.create_image(
                canvas_width / 2,       # 画像表示位置(Canvasの中心)
                canvas_height / 2,                   
                image=self.photo_image  # 表示画像データ
                )

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

ポイント

  • Canvasへ画像を表示する際はcreate_imageメソッドを用いますが、引数のimageへ渡す事ができる画像データはPhotoImageか、BitmapImageのどちらかとなります。
    BitmapImageはXBMファイルと呼ばれる二値化された画像なので、実質的に使うのはPhotoImageのみとなります。
  • PhotoImageで開く事のできるファイルフォーマットはPGM、PPM、GIF、PNGの4つのみ。
    これでも画像処理をするには物足りない(BMPは必須)ので、Pillowで画像ファイルを開き、PhotoImageへ変換します。
    Pillowなら、たくさんのファイルフォーマットに対応しています。
    (参考)

    https://docs.python.org/ja/3/library/tkinter.html?highlight=photoimage

  • create_imageメソッドで画像を表示する位置は初期状態では画像の中心座標を指定します。
    画像の左上の座標を指定する場合には、anchor = tk.NW を指定します。
  • Canvasの大きさを取得するにはwinfo_width()、winfo_height()を用います。
  • キャンバスの大きさに合わせて縦横比を崩さずに全体に表示するにはImageOps.pad()メソッドを用いていますが、ImageOpsモジュールはexperimental(試験的)という事ですが、便利なので使っちゃってます。。
    (参考)
    https://pillow.readthedocs.io/en/stable/reference/ImageOps.html

 

さらに画像の拡大・縮小・平行移動の機能を追加したプログラムについて、以下のページにまとめましたので、参照頂けると幸いです。

【Python】画像ビューア(ズーム(拡大/縮小)、移動表示)

関連ページ

【Python/tkinter】ウィンドウの作成

【Python/tkinter】ウィジェットの配置(pack)

【Python/tkinter】Menu(メニュー)

【Python/tkinter】Canvas(キャンバス)の作成

【Python/tkinter】ファイルを開くダイアログボックスの表示

【Python/tkinter】PanedWindow(C#のSplitContainer相当)

C#では、SplitContainerを使ってPictureBoxのサイズなどを変更していたのですが、tkinterでは、このSplitContainerに相当するものがPanedWindowとなります。

PanedWindowのサンプルプログラムを以下示します。

(実行画面)

仕切り線の移動

 

(プログラム)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("300x200") # ウィンドウサイズ(幅x高さ)

        # PanedWindowの作成
        paned_window = tk.PanedWindow(self.master)
        # フレームの作成
        frame1 = tk.Frame(paned_window, width = 100, bg = "red")
        frame2 = tk.Frame(paned_window, width = 100, bg = "green")
        # フレームをPanedWindowに追加
        paned_window.add(frame1)
        paned_window.add(frame2)

        paned_window.pack(expand = True, fill = tk.BOTH)


if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

PanedWindowはC#のSplitContainerと似てはいるのですが、異なる部分もあります。

  • C#のSplitContainerでは、最初からPanel1,Panel2が配置されていますが、PanedWindowには何も配置されていない。(自分でウィジェットをPanedWindowに追加する必要があります。)
  • PanedWindowにはFrame以外にもLabelやButtonなどのウィジェットが配置できる。
  • ウィジェットを追加すると、ウィジェットとウィジェットの間に仕切り線(sash)が追加される。
  • PanedWindowには3つ以上のウィジェットを追加できる。
  • ウィンドウをリサイズした時に、一番右(もしくは一番下)のウィジェットのみがリサイズされる。

 

オプション

オプション名 説明
background 通常時(クリックされていないとき)の背景色を指定します。(bgと同じ)
bd 外枠の線の太さを指定します。
ただし、初期状態では枠線が表示されていないため、reliefで枠線のスタイルを指定する必要があります。(borderwidthと同じ)
bg backgroundと同じ
borderwidth bdと同じ
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。
(参考)https://tkdocs.com/shipman/cursors.html
handlepad ハンドル(四角いマーク)の位置を指定します。
handlesize ハンドル(四角いマーク)の大きさを指定しますs。
height ウィジェットの高さを指定します。
opaqueresize 仕切り線(sash)を移動中に表示を更新する場合はTrue、更新しない場合はFalseを指定します。
【初期値】True
orient ウィジェットを横に並べる場合は tk.HORIZONTAL、縦に並べる場合は tk.VERTICALを指定します。
【初期値】tk.HORIZONTAL
sashpad 仕切り線(sash)の両側に追加する隙間の大きさを指定します。
sashrelief 仕切り線(sash)のスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
【初期値】tk.FLAT(枠線なし)
sashwidth 仕切り線(sash)の幅を指定します。
【初期値】3
showhandle ハンドルと呼ばれる四角い印を仕切り線上に表示する場合はTrue、表示しない場合はFalseを指定します。
【初期値】False
width ウィジェットの幅を指定します。

メソッド

メソッド 説明
add(child[, option=value] …) ウィジェットをPanedWindowに追加します。
forget(child) 追加したウィジェットを削除します。
identify(x, y)
panecget(child, option)
paneconfig(child, option=value, …)
panes() PanedWindowに追加されているウィジェットのリストを取得します。
remove(child)
追加したウィジェットを削除します。
sash_coord(index)
仕切り線(sash)の位置を取得します。
sash_place(index, x, y) 仕切り線(sash)の位置を指定します。
indexは仕切り線の順番0, 1, 2・・・

 

【Python/tkinter】Canvas(キャンバス)の作成

Canvasは、線や円などの図形や画像を表示するためのベースとなるものとなります。

C#でいうところのPictureBoxです。

Canvasには、図形や画像を表示する以外にも描画領域をスクロールできる機能もありますが、今回はCanvasの作成についてです。

ただ、Canvasを配置しただけの簡単なサンプルは以下のようになります。

 

(実行画面)

(プログラム)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("タイトル")    # ウィンドウタイトル
        self.master.geometry("300x200") # ウィンドウサイズ(幅x高さ)

        # Canvasの作成
        canvas = tk.Canvas(
            self.master, 
            width = 200,
            height = 100,
            bg = "cyan"
            )
        # Canvasを配置
        canvas.pack()

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

オプション

オプション名 説明
background 背景色を指定します。(bgと同じ)
bd 枠線の太さを指定します。(borderwidthと同じ)
bg 背景色を指定します。(backgroundと同じ)
borderwidth 枠線の太さを指定します。(bdと同じ)
closeenough
confine
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。
(参考)https://tkdocs.com/shipman/cursors.html
height キャンバスの高さを指定します。
highlightbackground
highlightcolor
highlightthickness
insertbackground
insertborderwidth
insertofftime
insertontime
insertwidth
offset
relief キャンバスの枠線のスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
scrollregion スクロールする領域のサイズを指定します。
selectbackground
selectborderwidth
selectforeground
state ウィジェットの有効(NORMAL)/無効(DISABLED,操作できない状態)を指定します。
【設定値】tk.NORMAL, tk.DISABLED
【初期値】tk.NORMAL
takefocus
width キャンバスの幅を指定します。
xscrollcommand 横方向にスクロールするスクロールバーの スクロールバー.set を指定します。
xscrollincrement 横方向のスクロールバーの矢印をクリックしたときの移動量を指定します。
yscrollcommand 縦方向にスクロールするスクロールバーの スクロールバー.set を指定します。
yscrollincrement 縦方向のスクロールバーの矢印をクリックしたときの移動量を指定します。

 

関連記事

Canvasに画像を表示する方法については、以下のページを参照ください。

【Python/tkinter】Canvasに画像を表示する

【Python/tkinter】名前を付けて保存ダイアログボックスの表示

名前を付けて保存ダイアログボックスを表示するには、tkinter.filedialogモジュールのasksaveasfilename()関数を使います。

以下にシンプルなサンプルプログラムを示します。

from tkinter import filedialog

filename = filedialog.asksaveasfilename()
print(filename)

(実行結果)

 

オプション

オプション名 説明
parent ダイアログを表示する親のウィンドウを指定します。
title ダイアログのタイトルを指定します。
initialdir 最初に表示されるディレクトリを指定します。
initialfile 最初に選択されているファイル名を指定します。
filetypes 選択可能なファイルの種類を設定します。
defaultextension 拡張子が入力されない場合に追加する拡張子を指定します。

(参考)

https://docs.python.org/ja/3/library/dialog.html?highlight=askopenfilename#module-tkinter.filedialog

 

名前を付けて保存ダイアログボックスはファイルを開くダイアログボックスとも似ていますが、defaultextensionオプションが異なります。

これは名前を付けて保存ダイアログの「ファイル名」の部分に拡張子を指定しなかった場合に、defaultextensionオプションで指定した拡張子が付加されます。

 

それでは、defaultextensionオプションを指定せずにfiletypesオプションを指定すると、どうなるか?というと、「ファイル名」の部分に、拡張子を付けずにファイル名を入力しても、「ファイルの種類」で指定された拡張子で、拡張子が自動で付加される事はありません。

filename = filedialog.asksaveasfilename(
    filetypes = [("Bitmap", ".bmp"), ("PNG", ".png"), ("JPEG", ".jpg"), ("Tiff", ".tif") ], # ファイルフィルタ
    )

そのため、filetypesオプションとdefaultextensionオプションは両方指定して、以下のようにします。

from tkinter import filedialog

filename = filedialog.asksaveasfilename(
    title = "名前を付けて保存",
    filetypes = [("Bitmap", ".bmp"), ("PNG", ".png"), ("JPEG", ".jpg"), ("Tiff", ".tif") ], # ファイルフィルタ
    initialdir = "./", # 自分自身のディレクトリ
    defaultextension = "bmp"
    )
print(filename)

このようにすると、「ファイルの種類」で指定された拡張子で、自動で拡張子が付加されます。

上記のプログラムではdefaultextension が “bmp”ですが、「ファイルの種類」がPNGのときは、ちゃんと.pngを付加してくれます。

もっというと、defaultextension = “” としても、「ファイルの種類」で指定された拡張子が付加されます。

(実行結果)

 

関連記事

【Python/tkinter】ファイルを開くダイアログボックスの表示

【Python/tkinter】Frame(フレーム)

Frameはいくつかのウィジェットを配置するための枠、まさにフレームです。
C#でいうところのPanelと同じです。

Frameを使うとウィジェットの配置の自由度が増します。
ウィジェットを配置するには、pack,grid,placeの3つのメソッドがありますが、一つのコンテナ(Frameなど)にはpack,grid,placeの3つの混在使用ができないのですが、複数のFrameを用いて、まずは大枠を作り、そこにウィジェットを配置すると、比較的簡単にイメージ通りの画面が作成できると思います。

Frameを使って、ツールバー、ステータスバー、右カラムを作ってみたいと思います。

(実行画面)

(プログラム)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("300x200") 

        #---------------------------------------
        #  ツールバー
        #---------------------------------------
        # ツールバー用Frame
        frame_toolbar = tk.Frame(self.master)
        # ツールボタン
        button1 = tk.Button(frame_toolbar, text = "1", width = 2)
        button2 = tk.Button(frame_toolbar, text = "2", width = 2)
        button3 = tk.Button(frame_toolbar, text = "3", width = 2)
        # ボタンをフレームに配置
        button1.pack(side = tk.LEFT)
        button2.pack(side = tk.LEFT)
        button3.pack(side = tk.LEFT)
        # ツールバーをウィンドの上に配置
        frame_toolbar.pack(fill = tk.X)

        #---------------------------------------
        #  ステータスバー
        #---------------------------------------
        # ツールバー用Frame
        frame_statusbar = tk.Frame(self.master, relief = tk.SUNKEN, bd = 2)
        # ステータスラベル
        label = tk.Label(frame_statusbar, text = "StatusLabel")
        # ラベルをフレームに配置
        label.pack(side = tk.LEFT)
        # ステータスバーをウィンドの下に配置
        frame_statusbar.pack(side = tk.BOTTOM, fill = tk.X)

        #---------------------------------------
        #  右カラム
        #---------------------------------------
        # 右カラム用Frame
        frame_column = tk.Frame(self.master, relief = tk.SUNKEN, bd = 2, width = 100)
        frame_column.propagate(False) # フーレムサイズの自動調整を無効にする
        # チェックボタン
        check1 = tk.Checkbutton(frame_column, text = "Check1")
        check2 = tk.Checkbutton(frame_column, text = "Check2")
        check3 = tk.Checkbutton(frame_column, text = "Check3")
        # チェックボタンをフレームに配置
        check1.pack()
        check2.pack()
        check3.pack()
        # 右カラムをウィンドの右に配置
        frame_column.pack(side = tk.RIGHT, fill = tk.Y)

        #---------------------------------------
        #  残りの領域
        #---------------------------------------
        frame = tk.Frame(self.master, relief = tk.SUNKEN, bd = 2, bg = 'dark cyan')
        frame.pack(expand = True, fill = tk.BOTH)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

ポイント

  • 枠線の種類(relief)を指定するときは、枠線の太さ(bd)も指定する事。(枠線の太さは初期値が0のため)
  • Frameの幅(width)もしくは高さ(height)を数値で指定したいときは、
    フレーム.propagate(False)メソッドを呼ぶこと。
    propagate()を呼ばない場合、フレーム内のウィジェットに合わせてサイズが自動調整されます。
  • Frameを残りの領域全体に広げたい場合はpack時にexpand = True, fill = tk.BOTHの両方を指定すること

オプション

オプション名 説明
bd 枠線の太さを指定します。(borderwidthと同じ)
borderwidth bdと同じ
bg 背景色を指定します。(backgroundと同じ)
background bgと同じ
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。
(参考)https://tkdocs.com/shipman/cursors.html
height フレームの高さを画素数で指定します。
highlightbackground
highlightcolor
highlightthickness
padx フレームの内側に配置するウィジェットまでの横方向の隙間を指定します。
【初期値】0
pady フレームの内側に配置するウィジェットまでの縦方向の隙間を指定します。
【初期値】0
relief ボタンのスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
takefocus
width フレームの幅を画素数で指定します。
wraplength 文字の折り返し幅を指定します。

 

【Python/tkinter】Menu(メニュー)

ウィンドウにメニューを追加するには、Menuクラスを用います。

メニューで良く使いそうな機能のサンプルプログラムを作成しました。

(実行画面)

ファイル関連のメニュー

表示メニュー(チェックボタンで作成しています)

選択メニュー(ラジオボタンで作成しています)

(サンプルプログラム)

import tkinter as tk
from tkinter import filedialog

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)
        self.pack()

        self.master.title("メニューの作成")       # ウィンドウタイトル
        self.master.geometry("300x150")     # ウィンドウサイズ(幅x高さ)
        
        # ------------------------------------------------
        # メニューの作成

        # メニューバーの作成
        menubar = tk.Menu(self)

        # ファイル
        menu_file = tk.Menu(menubar, tearoff = False)
        menu_file.add_command(label = "ファイルを開く",  command = self.menu_file_open_click,  accelerator="Ctrl+O")
        menu_file.add_command(label = "名前を付けて保存", command = self.menu_file_saveas_click, accelerator="Ctrl+S")
        menu_file.add_separator() # 仕切り線
        menu_file.add_command(label = "終了",            command = self.master.destroy)
        # ショートカットキーの関連付け
        menu_file.bind_all("<Control-o>", self.menu_file_open_click)
        menu_file.bind_all("<Control-s>", self.menu_file_saveas_click)

        # 表示(Checkbutton)
        menu_disp = tk.Menu(menubar, tearoff = False)
        self.disp1_value = tk.BooleanVar()
        self.disp2_value = tk.BooleanVar()
        self.disp3_value = tk.BooleanVar()
        menu_disp.add_checkbutton(label = "表示1", command = self.menu_disp1_click, variable = self.disp1_value)
        menu_disp.add_checkbutton(label = "表示2", command = self.menu_disp2_click, variable = self.disp2_value)
        menu_disp.add_checkbutton(label = "表示3", command = self.menu_disp3_click, variable = self.disp3_value)

        # 選択(Radiobutton)
        self.radio_val = tk.IntVar() # ラジオボタンの値
        menu_select = tk.Menu(menubar, tearoff = False)
        menu_select.add_radiobutton(label = "選択1", command = self.menu_select_click, variable = self.radio_val, value = 1)
        menu_select.add_radiobutton(label = "選択2", command = self.menu_select_click, variable = self.radio_val, value = 2)
        menu_select.add_radiobutton(label = "選択3", command = self.menu_select_click, variable = self.radio_val, value = 3)

        # メニューバーに各メニューを追加
        menubar.add_cascade(label="ファイル", menu = menu_file)
        menubar.add_cascade(label="表示",     menu = menu_disp)
        menubar.add_cascade(label="選択",     menu = menu_select)

        # 親ウィンドウのメニューに、作成したメニューバーを設定
        self.master.config(menu = menubar)

    def menu_file_open_click(self, event=None):
        print("「ファイルを開く」が選択された")
        filename = filedialog.askopenfilename(
            title = "ファイルを開く",
            initialdir = "./" # 自分自身のディレクトリ
            )
        print(filename)

    def menu_file_saveas_click(self, event=None):
        print("「名前を付けて保存」が選択された")

    def menu_disp1_click(self):
        print("「表示1」が選択された")
        print(f"チェック状態は{self.disp1_value.get()}")

    def menu_disp2_click(self):
        print("「表示2」が選択された")
        print(f"チェック状態は{self.disp2_value.get()}")

    def menu_disp3_click(self):
        print("「表示3」が選択された")
        print(f"チェック状態は{self.disp3_value.get()}")

    def menu_select_click(self):
        print(self.radio_val.get(), "番目のラジオボタンが選択されました。")

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

ポイント

  • プルダウンで表示されるメニューが1つのMenuクラスオブジェクトとなり、各メニューの項目はadd_command()メソッドで追加します。
  • Menuクラスオブジェクトを親のMenuに追加するときは、add_cascade()メソッドを用います。

Menuクラスオプション

オプション名 説明
activebackground マウスポインタが各メニューの上にあるときの背景色を指定します。
activeborderwidth
選択されたメニューの枠の太さを指定します???
実質的にメニューの行間が広がります。
activeforeground マウスポインタが各メニューの上にあるときの文字色を指定します。
background 通常時の背景色を指定します。(bgと同じ)
bd 枠線の太さを指定します。(指定できない?)
bg backgroundと同じ
bitmap モノクロのBitmapを指定します。
(参考)https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/bitmaps.html
borderwidth bdと同じ
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。(指定できない?
disabledforeground add_command()メソッドで追加したメニューのstateオプションで無効(DISABLED)に設定している際の文字色を設定します。
fg add_command()メソッドで追加したメニューの文字色を設定します。
foreground fgと同じ
relief ボタンのスタイルを指定します。(指定できない?
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
【初期値】tk.FLAT(枠線なし)
selectcolor チェックボックス(チェックのある四角)の背景色を指定します。(指定できない?
takefocus
tearoff 必ずFalse(0)を指定します。
指定しない場合、破線が表示されます。
tearoffcommand
title
type

メソッド

メソッド 説明
add_command() メニュー項目を追加します。
add_radiobutton() ラジオボタンを追加します。(ただし、見た目はチェックボタンと同じ)
add_checkbutton() チェックボタンを追加します。
add_cascade() 親のMenuにMenuクラスオブジェクトを追加します。
add_separator() 仕切り線を追加します。
add( type, options )
delete( startindex [, endindex ]) メニューの項目をstartindex ~(endindex )まで削除します。
entryconfig( index, options ) メニューのindex番号を指定して、オプション設定値を設定、変更します。
index(item) メニューの文字列を指定してindex番号を取得します。
insert_separator ( index ) 指定した番号の位置に仕切り線を挿入します。
invoke ( index ) チェックボックスの場合、チェックの選択/非選択状態が切り替わります。
ラジオボタンの場合、選択されている項目が切り替わります。
type ( index ) 指定した番号のタイプを取得します。
cascade,  checkbutton,  command,  radiobutton,  separator, tearoff のいづれか

(補足)

メニューのindex番号ですが、私の環境では、上のメニューの番号は1から始まり、

ドロップダウンで表示されるメニューは0始まりとなっていました。

念のためindex()メソッドで、メニューの文字列指定でindex番号を取得した方が安全かも?しれません。

【Python/tkinter】Radiobutton(ラジオボタン)

ラジオボタンはRadiobuttonクラスによって作成します。

チェックボタンでは他のチェックボタンとは関係なくチェック/チェック無しの状態が選べましたが、ラジオボタンは複数のラジオボタンの中から1つだけ選択する場合に用いられます。

 

以下に簡単なサンプルを示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ラジオボタンの作成")     # ウィンドウタイトル
        self.master.geometry("300x150")             # ウィンドウサイズ(幅x高さ)

        # ラジオボタンの値
        self.radio_value = tk.IntVar(value = 1)     # 初期値を設定する場合
        #self.radio_value = tk.IntVar()             # 初期値を設定しないと0になる
        
        # ラジオボタンの作成
        radio0 = tk.Radiobutton(self.master, 
                           text = "ラジオボタン0",      # ラジオボタンの表示名
                           command = self.radio_click,  # クリックされたときに呼ばれるメソッド
                           variable = self.radio_value, # 選択の状態を設定する
                           value = 0                    # ラジオボタンに割り付ける値の設定
                           )

        radio1 = tk.Radiobutton(self.master, 
                           text = "ラジオボタン1",      # ラジオボタンの表示名
                           command = self.radio_click,  # クリックされたときに呼ばれるメソッド
                           variable = self.radio_value, # 選択の状態を設定する
                           value = 1                    # ラジオボタンに割り付ける値の設定
                           )

        radio2 = tk.Radiobutton(self.master, 
                           text = "ラジオボタン2",      # ラジオボタンの表示名
                           command = self.radio_click,  # クリックされたときに呼ばれるメソッド
                           variable = self.radio_value, # 選択の状態を設定する
                           value = 2                    # ラジオボタンに割り付ける値の設定
                           )

        # ボタンの作成
        button = tk.Button(self.master, 
                           text = "ラジオボタンの選択を次へ",  # ボタンの表示名
                           command = self.button_click  # クリックされたときに呼ばれるメソッド
                           )

        # 配置
        radio0.pack()
        radio1.pack()
        radio2.pack()
        button.pack()

    def radio_click(self):
        # ラジオボタンの値を取得
        value = self.radio_value.get()
        print(f"ラジオボタンの値は {value} です")

    def button_click(self):
        # 選択されているラジオボタンを次に移動させる
        value = self.radio_value.get()
        self.radio_value.set((value + 1) % 3)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

ポイント

  • ラジオボタンの値(選択されたときの識別番号)はIntVarクラスオブジェクトをRadiobuttonクラスのvariableオプションに設定する
  • 1つだけ選択される複数のラジオボタンには、それぞれ、同じIntVarクラスオブジェクトをvariableオプションに設定する
  • 各ラジオボタンの値(選択されたときの識別番号)はRadiobuttonクラスのvalueオプションに設定しておく
  • 選択されているラジオボタンの値(valueオプションで指定した値)は IntVarクラスオブジェクト.get() で行う
  • 選択されているラジオボタンを変更するには IntVarクラスオブジェクト.set() の引数にラジオボタンの値(valueオプションで指定した値)を指定します。

オプション

オプション名 説明
activebackground クリックされたときの背景色を指定します。
activeforeground クリックされたときの文字色(チェックマークを含む)を指定します。
anchor 文字の配置位置を指定します。
【設定値】tk.N, tk.S, tk.W, tk.E, tk.NW, tk.NE, tk.SW, tk.SE, tk.CENTER
【初期値】tk.CENTER
※width, heightを指定し、余白が生じている際に有効です。
background 通常時(クリックされていないとき)の背景色を指定します。(bgと同じ)
bd 枠線の太さを指定します。
ただし、初期状態では枠線が表示されていないため、reliefで枠線のスタイルを指定する必要があります。(borderwidthと同じ)
bg backgroundと同じ
bitmap モノクロのBitmapを指定します。
(参考)https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/bitmaps.html
borderwidth bdと同じ
command ボタンがクリックされたときに呼び出すメソッドの名前を指定します。
compound 文字と画像の両方を表示する際に、文字に対して画像の表示位置を指定します。
【設定値】tk.LEFT, tk.RIGHT, tk.BOTTOM, tk.TOP, tk.CENTER
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。
(参考)https://tkdocs.com/shipman/cursors.html
disabledforeground stateオプションで無効(DISABLED)に設定している際の文字色を設定します。
fg 表示する文字色を指定します。(foregroundと同じ)
font 表示する文字のフォントを指定します。
foreground fgと同じ
height ボタンの高さを文字数で指定します。
画像を配置した時は、画素数の指定になります。
highlightbackground
highlightcolor
highlightthickness
image 表示する画像を指定します。
indicatoron
justify 複数行の文字のときの、文字寄せ方向を指定します。
【設定値】左寄せ(tk.LEFT), 中央寄せ(tk.CENTER), 右寄せ(tk.RIGHT)
offrelief
overrelief ウィジェット上にマウスポインタがある際のスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
【初期値】tk.FLAT(枠線なし)
padx 文字の両側の隙間を指定します。
pady 文字の上下の隙間を指定します。
relief ボタンのスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
【初期値】tk.FLAT(枠線なし)
selectcolor チェックボックス(チェックのある四角)の背景色を指定します。
selectimage
state ウィジェットの有効/無効(操作できない状態)を指定します。
【設定値】tk.NORMAL, tk.DISABLED
【初期値】tk.NORMAL
takefocus
text 表示する文字を指定します。
textvariable
tristateimage
tristatevalue
underline 指定した順番(先頭から0始まり)の文字にアンダーラインを付加します。
value ラジオボタンの値(選択されたときの識別番号)
variable チェックの状態をBooleanVarクラスオブジェクトで指定します。
width ボタンの幅を文字数で指定します。
画像を配置した時は、画素数の指定になります。
wraplength 文字の折り返し幅を指定します。

メソッド

メソッド 説明
deselect() 選択状態のとき、同じvariableオプションを設定している他のラジオボタンも全て選択状態になります。(詳細不明。。)
flash() backgroundとactivebackgroundで交互に色を点滅させて表示します。
invoke() 選択状態にします。
select() 選択状態にします。

【Python/tkinter】Checkbutton(チェックボックス)

チェックボタン(チェックボックス)はCheckbuttonクラスによって作成します。

以下にサンプルを示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("チェックボックスの作成")       # ウィンドウタイトル
        self.master.geometry("300x100")                 # ウィンドウサイズ(幅x高さ)

        # チェックボックスの値
        self.check_value = tk.BooleanVar(value = True)  # 初期値を設定する場合
        #self.check_value = tk.BooleanVar()             # 初期値を設定しないとFlaseになる
        # チェックボックスの作成
        self.check = tk.Checkbutton(self.master, 
                           text = "チェックボックス",    # チェックボックスの表示名
                           command = self.check_click,  # クリックされたときに呼ばれるメソッド
                           variable = self.check_value  # チェックの状態を設定する
                           )

        # ボタンの作成
        button = tk.Button(self.master, 
                           text = "チェックを逆にする",  # ボタンの表示名
                           command = self.button_click  # クリックされたときに呼ばれるメソッド
                           )
        # 配置
        self.check.pack()
        button.pack()

    def check_click(self):
        # チェックの状態を取得(チェックされているときはTrue)
        value = self.check_value.get()
        print(f"チェックの状態は {value} です")

    def button_click(self):
        # チェックの状態を取得し逆に設定する
        value = self.check_value.get()
        self.check_value.set(not value)
        #以下のようにしても同じ
        #self.check.toggle()


if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

ポイント

  • チェックボタンの値(チェックされている(True)/されていない(False))はBooleanVarクラスオブジェクトをCheckbuttonクラスのvariableオプションに設定する
  • チェックの状態の取得は BooleanVarクラスオブジェクト.get() で行う
  • チェックの状態の設定は BooleanVarクラスオブジェクト.set() の引数にTrue もしくは False を渡す

オプション

オプション名 説明
activebackground クリックされたときの背景色を指定します。
activeforeground クリックされたときの文字色(チェックマークを含む)を指定します。
anchor 文字の配置位置を指定します。
【設定値】tk.N, tk.S, tk.W, tk.E, tk.NW, tk.NE, tk.SW, tk.SE, tk.CENTER
【初期値】tk.CENTER
※width, heightを指定し、余白が生じている際に有効です。
background 通常時(クリックされていないとき)の背景色を指定します。(bgと同じ)
bd 枠線の太さを指定します。
ただし、初期状態では枠線が表示されていないため、reliefで枠線のスタイルを指定する必要があります。(borderwidthと同じ)
bg backgroundと同じ
bitmap モノクロのBitmapを指定します。
(参考)https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/bitmaps.html
borderwidth bdと同じ
command ボタンがクリックされたときに呼び出すメソッドの名前を指定します。
compound 文字と画像の両方を表示する際に、文字に対して画像の表示位置を指定します。
【設定値】tk.LEFT, tk.RIGHT, tk.BOTTOM, tk.TOP, tk.CENTER
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。
(参考)https://tkdocs.com/shipman/cursors.html
disabledforeground stateオプションで無効(DISABLED)に設定している際の文字色を設定します。
fg 表示する文字色を指定します。(foregroundと同じ)
font 表示する文字のフォントを指定します。
foreground fgと同じ
height ボタンの高さを文字数で指定します。
画像を配置した時は、画素数の指定になります。
highlightbackground
highlightcolor
highlightthickness
image 表示する画像を指定します。
indicatoron
justify 複数行の文字のときの、文字寄せ方向を指定します。
【設定値】左寄せ(tk.LEFT), 中央寄せ(tk.CENTER), 右寄せ(tk.RIGHT)
offvalue チェックが無い時の値をTrue, Falseで指定します。
【初期値】False
onvalue チェックがある時の値をTrue, Falseで指定します。
【初期値】True
overrelief ウィジェット上にマウスポインタがある際のスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
【初期値】tk.FLAT(枠線なし)
padx 文字の両側の隙間を指定します。
pady 文字の上下の隙間を指定します。
relief ボタンのスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
【初期値】tk.FLAT(枠線なし)
selectcolor チェックボックス(チェックのある四角)の背景色を指定します。
selectimage
state ウィジェットの有効/無効(操作できない状態)を指定します。
【設定値】tk.NORMAL, tk.DISABLED
【初期値】tk.NORMAL
takefocus
text 表示する文字を指定します。
textvariable
tristateimage
tristatevalue
underline 指定した順番(先頭から0始まり)の文字にアンダーラインを付加します。
variable チェックの状態をBooleanVarクラスオブジェクトで指定します。
width ボタンの幅を文字数で指定します。
画像を配置した時は、画素数の指定になります。
wraplength 文字の折り返し幅を指定します。

 

メソッド

メソッド 説明
deselect() チェックなし状態にします。
flash() backgroundとactivebackgroundで交互に色を点滅させて表示します。
invoke()
select() チェックあり状態にします。
toggle() チェックのあり/なしを逆に設定します。

 

【Python/tkinter】ファイルを開くダイアログボックスの表示

ファイルを開くダイアログボックスを表示するには、tkinter.filedialogモジュールのaskopenfilename()関数を使います。

以下にシンプルなサンプルプログラムを示します。

from tkinter import filedialog

filename = filedialog.askopenfilename()
print(filename)

(実行結果)

オプション

オプション名 説明
parent ダイアログを表示する親のウィンドウを指定します。
title ダイアログのタイトルを指定します。
initialdir 最初に表示されるディレクトリを指定します。
initialfile 最初に選択されているファイル名を指定します。
filetypes 選択可能なファイルの種類を設定します。
multiple 複数ファイルの選択を許可する場合はTrueを、そうでない場合はFalseを指定します。

(参考)

https://docs.python.org/ja/3/library/dialog.html?highlight=askopenfilename#module-tkinter.filedialog

サンプル

実際に、このダイアログを使用する場合には、title, initialdir, filetypesの3つは指定しておいた方が良いでしょう。

以下に私がよく使うパターンのサンプルを示します。

from tkinter import filedialog

filename = filedialog.askopenfilename(
    title = "画像ファイルを開く",
    filetypes = [("Image file", ".bmp .png .jpg .tif"), ("Bitmap", ".bmp"), ("PNG", ".png"), ("JPEG", ".jpg"), ("Tiff", ".tif") ], # ファイルフィルタ
    initialdir = "./" # 自分自身のディレクトリ
    )
print(filename)

(実行結果)

関連記事

【Python/tkinter】名前を付けて保存ダイアログボックスの表示

【Python/tkinter】メッセージボックス

メッセージボックスには、OKボタンが1つのメッセージボックスやOKボタン/Cancelボタンがあるものなど、いくつかのバリエーションがありますが、これらの種類は関数名で切り替えます。

 

まずは、以下のサンプルを実行して頂くと雰囲気がつかめると思います。

from tkinter import messagebox

ret = messagebox.showinfo(
    title = "タイトル",
    message = "showinfoで表示されるメッセージ")
print("showinfoの戻り値:\t", ret)

ret = messagebox.showwarning(
    title = "タイトル",
    message = "showwarningで表示されるメッセージ")
print("showwarningの戻り値:\t", ret)

ret = messagebox.showerror(
    title = "タイトル",
    message = "showerrorで表示されるメッセージ")
print("showerrorの戻り値:\t", ret)

ret = messagebox.askokcancel(
    title = "タイトル",
    message = "askokcancelで表示されるメッセージ")
print("askokcancelの戻り値:\t", ret)

ret = messagebox.askyesno(
    title = "タイトル",
    message = "askyesnoで表示されるメッセージ")
print("askyesnoの戻り値:\t", ret)

ret = messagebox.askretrycancel(
    title = "タイトル",
    message = "askretrycancelで表示されるメッセージ")
print("askretrycancelの戻り値:", ret)

ret = messagebox.askquestion(
    title = "タイトル",
    message = "askquestionで表示されるメッセージ")
print("askquestionの戻り値:\t", ret)

ret = messagebox.askyesnocancel(
    title = "タイトル",
    message = "askyesnocancelで表示されるメッセージ")
print("askyesnocancelの戻り値:", ret)

 

関数名 メッセージボックス 戻り値
showinfo ok
showwarning ok
showerror ok
askokcancel True, False
askyesno True, False
askretrycancel True, False
askquestion yes, no
askyesnocancel True, False, None

 

【Python/tkinter】ウィジェットの配置(pack)

ウィジェットを配置するには、pack,grid,placeの3つのメソッドがありますが、ここではpackについて説明します。(個人的にはpack, grid, placeのうち、packが一番使いやすいと思っています。)

packは、下図のように、ボタンやラベルなどのウィジェットを上、下、左、右方向に積み木のように、重ねて配置します。

 

以下に簡単なサンプルを示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # ボタンの作成
        button_top = tk.Button(self.master, text = "TOP", width = 8)
        button_bottom = tk.Button(self.master, text = "BOTTOM", width = 8)
        button_left = tk.Button(self.master, text = "LEFT", width = 8)
        button_right = tk.Button(self.master, text = "RIGHT", width = 8)

        # ウィジェットの配置
        button_top.pack(side = tk.TOP)
        button_bottom.pack(side = tk.BOTTOM)
        button_left.pack(side = tk.LEFT)
        button_right.pack(side = tk.RIGHT)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行画面)

 

構文

ウィジェット.pack(オプション1 = 設定値, オプション2 = 設定値,・・・)

オプション

オプション名 説明
after すでに配置されているウィジェットの次にウィジェットを配置する
anchor sideで示した領域内のウィジェットの配置方向を指定します。
【設定値】tk.N, tk.S, tk.W, tk.E, tk.NW, tk.NE, tk.SW, tk.SE, tk.CENTER
【初期値設定値】tk.CENTER
before すでに配置されているウィジェットの前にウィジェットを配置する
expand ウィジェットを配置する領域を残りの領域全体に広げるかどうかを指定します。
【設定値】True, False
【初期値設定値】False
fill ウィジェットを領域の横、縦、または両方の方向の幅、高さに合わせてに大きくするか?設定します。
【設定値】tk.X, tk.Y, tk.BOTH, tk.NONE
【初期値設定値】tk.NONE
in_ ウィジェット作成時に親ウィジェットを指定せずに、配置時(pack時)に親ウィジェットを指定します。
ipadx ウィジェットの内側の横方向の隙間を設定します。
ipady ウィジェットの内側の縦方向の隙間を設定します。
padx ウィジェットの外側の横方向の隙間を設定します。
値で設定すると、両側の隙間を設定します。
タプルで(左, 右)のように設定すると、それぞれの隙間を指定できます。
pady ウィジェットの外側の縦方向の隙間を設定します。
値で設定すると、上下の隙間を設定します。
タプルで(上, 下)のように設定すると、それぞれの隙間を指定できます。
side ウィジェットの配置方向を、上、下、左、右の中から指定します。
【設定値】tk.TOP, tk.BOTTOM, tk.LEFT, tk.RIGHT
【初期値設定値】tk.TOP

 

side

sideではウィジェットの上下左右の配置方向を指定しますが、配置の順番が重要となります。

最初のプログラムの配置部分(packの部分)のプログラムは

# ウィジェットの配置
button_top.pack(side = tk.TOP)
button_bottom.pack(side = tk.BOTTOM)
button_left.pack(side = tk.LEFT)
button_right.pack(side = tk.RIGHT)

となっていますが、この結果は以下のような感じでした。

この配置の順番を入れ替えて、以下のようなプログラムにすると

# ウィジェットの配置
button_left.pack(side = tk.LEFT)
button_top.pack(side = tk.TOP)
button_right.pack(side = tk.RIGHT)
button_bottom.pack(side = tk.BOTTOM)

実行結果は

のようになり、バラバラな配置になります。
なぜ、このようになるのか?順を追って見ていきます。

最初にボタンを左側にpackしています。

button_left.pack(side = tk.LEFT)

この時点で、目には見えませんが、packした占有領域としては、親ウィジェットの左側の上から下までボタンの幅分、占有されています。

ボタン位置の上下方向はanchorで指定できますが、指定しない場合、初期値の中央(tk.CENTER)が適応されます。

次に配置されるウィジェットは、この残りの領域に対して配置されます。
つまり次のボタンを上側に配置以下のように配置すると、残りの領域の上側が占有されます。

button_top.pack(side = tk.TOP)

同様にして、次に右側に配置すると

button_right.pack(side = tk.RIGHT)

続いて下側

button_bottom.pack(side = tk.BOTTOM)

というように、pack()のsideでは残っている領域の上側、下側、左側、右側の上から下まで、もしくは左から右まで占有している事に注意が必要です。

 

anchor

anchorはsideとも似ているのですが、sideの説明で示した占有領域内(赤く囲った部分)の配置方向を方角を使って指定します。

これを踏まえた上で、以下のプログラムを実行してみます。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # ボタンの作成
        button_N = tk.Button(self.master, text = "N", width = 8)
        button_S = tk.Button(self.master, text = "S", width = 8)
        button_W = tk.Button(self.master, text = "W", width = 8)
        button_E = tk.Button(self.master, text = "E", width = 8)

        # ウィジェットの配置
        button_N.pack(anchor = tk.N)
        button_S.pack(anchor = tk.S)
        button_W.pack(anchor = tk.W)
        button_E.pack(anchor = tk.E)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

すると、特に、tk.Sを指定した時に違和感がありますよね。
さらに、tk.Wとtk.Eの上下方向がズレてるし。。

これについても、packで占有される領域を示すと理解しやすくなります。

pack()では、sideオプションを指定しないとtk.TOPになるので、全て上側にウィジェットが積み重なります。
anchorオプションはあくまでも、占有領域内の配置方向を指定するので、side = tk.TOP のときは、上下方向の指定(tk.N, tk.S)はtk.CENTERと同様になります。

同様にして side = tk.LEFT もしくは side = tk.RIGHT のときは、左右方向の指定(tk.W, tk.E) も tk.CENTERと同様になります。

ただし、次に示す expand = True とした時は、上下、左右、斜め方向の指定が有効になります。

 

expand

expandは expand = Trueとした時、残っている領域全てを占有領域とします。

(サンプル)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # ボタンの作成
        button_top = tk.Button(self.master, text = "TOP", width = 8)
        button_center = tk.Button(self.master, text = "CENTER", width = 8)

        # ウィジェットの配置
        button_top.pack()
        button_center.pack(expand = True)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

これも占有領域を書くと分かりやすいと思います。

2つ以上のウィジェットを expand = True にして配置すると、領域を均等分割して配置されます。

(サンプル)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # ボタンの作成
        button_top = tk.Button(self.master, text = "TOP", width = 8)
        button_center1 = tk.Button(self.master, text = "CENTER1", width = 8)
        button_center2 = tk.Button(self.master, text = "CENTER2", width = 8)

        # ウィジェットの配置
        button_top.pack()
        button_center1.pack(expand = True)
        button_center2.pack(expand = True)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

これも占有領域を書いてみます。

 

fill

fillは、これまで説明してきた占有領域に対して、ウィジェットの幅、高さ、もしくは両方を合わせます。

(サンプル)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # ボタンの作成
        button_top = tk.Button(self.master, text = "TOP", width = 8)
        button_center = tk.Button(self.master, text = "CENTER", width = 8)

        # ウィジェットの配置
        button_top.pack()
        button_center.pack(expand = True, fill = tk.X)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

fill = tk.Y のとき

fill = tk.BOTH

 

ipadx, ipady

ウィジェットの内側の隙間の量を指定します。

ipadxが横方向、ipadyが縦方向の隙間となります。

隙間を指定することで、ウィジェットの大きさも大きくなります。

(サンプル)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # ボタンの作成
        button_ipad0 = tk.Button(self.master, text = "ipadx = 0\nipady = 0")
        button_ipad  = tk.Button(self.master, text = "ipadx = 30\nipady = 10")

        # ウィジェットの配置
        button_ipad0.pack()
        button_ipad.pack(ipadx = 30, ipady = 10)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

padx, pady

ウィジェットの外側の隙間の量を指定します。

padxが横方向、padyが縦方向の隙間となります。

(サンプル)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # ボタンの作成
        button1 = tk.Button(self.master, text = "Button1")
        button2 = tk.Button(self.master, text = "Button2")
        button3 = tk.Button(self.master, text = "Button3")
        button4 = tk.Button(self.master, text = "Button4")
        button_pad  = tk.Button(self.master, text = "padx = 30\npady = 10")

        # ウィジェットの配置
        button1.pack(side = tk.TOP, expand = True, fill = tk.BOTH)
        button4.pack(side = tk.BOTTOM, expand = True, fill = tk.BOTH)
        button2.pack(side = tk.LEFT, expand = True, fill = tk.BOTH)
        button_pad.pack(side = tk.LEFT, padx = 30, pady = 10)
        button3.pack(side = tk.LEFT, expand = True, fill = tk.BOTH)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

after, before

ウィジェットの配置は順番が大事と説明してきましたが、すでに配置されているウィジェットの間に挿入して配置する場合に用います。

afterは指定したウィジェットの次にウィジェットを配置します。

beforeは指定したウィジェットの前にウィジェットを配置します。

(サンプル)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # ボタンの作成
        button1 = tk.Button(self.master, text = "Button1")
        button2 = tk.Button(self.master, text = "Button2")
        button3 = tk.Button(self.master, text = "Button3")
        button4 = tk.Button(self.master, text = "Button4")
        button5 = tk.Button(self.master, text = "Button5")

        button2_after = tk.Button(self.master, text = "Button2_After")
        button5_before = tk.Button(self.master, text = "Button5_Before")


        # ウィジェットの配置
        button1.pack()
        button2.pack()
        button3.pack()
        button4.pack()
        button5.pack()
        # すでに配置されているウィジェットの次に配置する
        button2_after.pack(after = button2)
        # すでに配置されているウィジェットの前に配置する
        button5_before.pack(before = button5)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

in_

ウィジェット作成時に親ウィジェットを指定せずに、配置時(pack時)に親ウィジェットを指定します。

(サンプル)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.geometry("250x250") 

        # 左側のフレーム
        frame_left = tk.Frame(self.master)
        # 右側のフレーム
        frame_right = tk.Frame(self.master)

        # ボタンの作成
        button1 = tk.Button(frame_left, text = "Button1")
        button2 = tk.Button(text = "Button2") # ここでは親のウィジェットを指定していない

        # ウィジェットの配置
        button1.pack()
        button2.pack(in_ = frame_right) # ここでは親のウィジェットを指定

        # フレームの配置
        frame_left.pack(side = tk.LEFT, expand = True)
        frame_right.pack(side = tk.LEFT, expand = True)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

まとめ

ここまで、ウィジェットの配置は順番が重要だと説明してきましたが、実際には1つの親ウィジェットに、様々なオプションを駆使してウィジェットを配置するのは、かなり困難です。

そのため、いくつかのフレーム(Frame)を使って大枠を作成し、各フレームにウィジェットを配置すると比較的簡単に配置できます。

そこで、私がC#では良く作っていたウィンドウのレイアウトを、フレームをいくつか使いサンプルを作成してみました。

(サンプルプログラム)

import tkinter as tk
from tkinter import filedialog
import os


class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        # ウィンドウタイトル
        self.master.title("ウィジェット配置サンプル(packを使用)")

        self.master.geometry("500x300") 

        # メニューの作成
        self.create_menu()
        # ツールバーの作成
        self.create_tool_bar()
        # ステータスバーの作成
        self.create_status_bar()
        # サイドパネル
        self.create_side_panel()

        # 残りの領域にキャンバスを作成
        canvas = tk.Canvas(self.master, background="#008080")
        canvas.pack(expand=True,  fill=tk.BOTH)

    def create_menu(self):
        ''' メニューの作成'''
        menu_bar = tk.Menu(self)
 
        file_menu = tk.Menu(menu_bar, tearoff = tk.OFF)
        menu_bar.add_cascade(label="ファイル", menu = file_menu) 

        file_menu.add_command(label = "開く", command = self.menu_open_click, accelerator = "Ctrl+O")
        file_menu.add_separator() # セパレータ
        file_menu.add_command(label = "終了", command = self.master.destroy)
        # ショートカットの設定
        menu_bar.bind_all("", self.menu_open_click)

        # 親のメニューに設定
        self.master.config(menu = menu_bar)

    def menu_open_click(self, event=None):
        ''' ファイルを開く'''

        # ファイルを開くダイアログ
        filename = tk.filedialog.askopenfilename(
            initialdir = os.getcwd() # カレントディレクトリ
            )
        print(filename)

    def create_tool_bar(self):
        ''' ツールバー'''

        frame_tool_bar = tk.Frame(self.master, borderwidth = 2, relief = tk.SUNKEN)

        button1 = tk.Button(frame_tool_bar, text = "1", width = 2)
        button2 = tk.Button(frame_tool_bar, text = "2", width = 2)
        button3 = tk.Button(frame_tool_bar, text = "3", width = 2)

        button1.pack(side = tk.LEFT)
        button2.pack(side = tk.LEFT)
        button3.pack(side = tk.LEFT)

        frame_tool_bar.pack(fill = tk.X)

    def create_status_bar(self):
        '''ステータスバー'''
        frame_status_bar = tk.Frame(self.master, borderwidth = 2, relief = tk.SUNKEN)

        self.label1 = tk.Label(frame_status_bar, text = "ステータスラベル1")
        self.label2 = tk.Label(frame_status_bar, text = "ステータスラベル2")

        self.label1.pack(side = tk.LEFT)
        self.label2.pack(side = tk.RIGHT)

        frame_status_bar.pack(side = tk.BOTTOM, fill = tk.X)

    def create_side_panel(self):
        '''サイドパネル'''
        side_panel = tk.Frame(self.master, borderwidth = 2, relief = tk.SUNKEN)

        button1 = tk.Button(side_panel, text = "ボタン1", width = 15)
        button2 = tk.Button(side_panel, text = "ボタン2", width = 15)

        button1.pack()
        button2.pack()

        side_panel.pack(side = tk.RIGHT, fill = tk.Y)


if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

【Python/tkinter】Button(ボタン)

tkinterでボタンの作成のサンプルを以下に示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ボタンの作成")     # ウィンドウタイトル
        self.master.geometry("300x100")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ボタンの作成
        button = tk.Button(self.master, 
                           text = "ボタン",             # ボタンの表示名
                           command = self.button_click  # クリックされたときに呼ばれるメソッド
                           )
        button.pack()
        #--------------------------------------------------------
    def button_click(self):
        '''クリックされたときに呼ばれるメソッド'''
        print("ボタンがクリックされた")

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行画面)

構文

ボタンオブジェクト = tk.Button(親ウィジェット, オプション1 = 設定値, オプション2 = 設定値,・・・)

オプション

オプション名 説明
activebackground クリックされたときの背景色を指定します。
activeforeground クリックされたときの文字色を指定します。
anchor 文字の配置位置を指定します。
【設定値】tk.N, tk.S, tk.W, tk.E, tk.NW, tk.NE, tk.SW, tk.SE, tk.CENTER
background 背景色を指定します。
bd 枠線の太さを指定します。
bg 通常時の背景色を指定します。
bitmap モノクロのBitmapを指定します。
(参考)https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/bitmaps.html
borderwidth bdと同じ
command ボタンがクリックされたときに呼び出すメソッドの名前を指定します。
compound 文字と画像の両方を表示する際に、文字に対して画像の表示位置を指定します。
【設定値】tk.LEFT, tk.RIGHT, tk.BOTTOM, tk.TOP, tk.CENTER
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。
(参考)https://tkdocs.com/shipman/cursors.html
default
disabledforeground stateオプションで無効(DISABLED)に設定している際の文字色を設定します。
fg 表示する文字色を指定します。(foregroundと同じ)
font 表示する文字のフォントを指定します。
foreground
height ボタンの高さを文字数で指定します。
画像を配置した時は、画素数の指定になります。
highlightbackground
highlightcolor
highlightthickness
image 表示する画像を指定します。
justify 複数行の文字のときの、文字寄せ方向を指定します。
【設定値】左寄せ(tk.LEFT), 中央寄せ(tk.CENTER), 右寄せ(tk.RIGHT)
overrelief
padx 文字の両側の隙間を指定します。
pady 文字の上下の隙間を指定します。
relief ボタンのスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
repeatdelay
repeatinterval
state
takefocus
text 表示する文字を指定します。
textvariable StringVarクラスオブジェクトを指定し、ボタンの文字列を指定します。
underline 指定した順番(先頭から0始まり)の文字にアンダーラインを付加します。
width ボタンの幅を文字数で指定します。
wraplength 文字の折り返し幅を指定します。

 

サンプルプログラム

評価に用いたプログラムを以下に示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ボタンの作成(Windowsスタイル)")     # ウィンドウタイトル
        self.master.geometry("580x580")       # ウィンドウサイズ(幅x高さ)

        # ボタンの作成
        button1 = tk.Button(self.master, text = "ボタン")
        # クリックイベントの指定
        button2 = tk.Button(self.master, text = "クリック", 
                            command = self.button_click # クリック時に呼び出されるメソッド名
                            )
        # 無効ボタン
        button3 = tk.Button(self.master, text = "無効ボタン",
                            state = tk.DISABLED # 無効:tk.DISABLED 有効:tk.NORMAL
                            )
        # ボタンサイズ
        button4 = tk.Button(self.master, text = "ボタンサイズ", 
                            width = 20, height = 2      # サイズは文字数で指定
                            )
        # フォントサイズ
        button5 = tk.Button(self.master, text = "フォントサイズ", 
                            font = ("", 18) # フォントサイズの変更
                            )
        # ボタンスタイル
        button6 = tk.Button(self.master, text = "RAISED", 
                            relief = tk.RAISED      
                            )
        button7 = tk.Button(self.master, text = "GROOVE", 
                            relief = tk.GROOVE      
                            )
        button8 = tk.Button(self.master, text = "SUNKEN", 
                            relief = tk.SUNKEN      # 凹んだ状態(クリックはできる)
                            )
        button9 = tk.Button(self.master, text = "RIDGE", 
                            relief = tk.RIDGE      
                            )
        button10 = tk.Button(self.master, text = "FLAT", 
                            relief = tk.RIDGE      
                            )

        # 文字の位置
        button11 = tk.Button(self.master, text = "NW", 
                            width = 10, height = 2,
                            anchor = tk.NW 
                            )
        button12 = tk.Button(self.master, text = "N", 
                            width = 10, height = 2,
                            anchor = tk.N
                            )
        button13 = tk.Button(self.master, text = "NE", 
                            width = 10, height = 2,
                            anchor = tk.NE
                            )
        button14 = tk.Button(self.master, text = "W", 
                            width = 10, height = 2,
                            anchor = tk.W
                            )
        button15 = tk.Button(self.master, text = "CENTER", 
                            width = 10, height = 2,
                            anchor = tk.CENTER
                            )
        button16 = tk.Button(self.master, text = "E", 
                            width = 10, height = 2,
                            anchor = tk.E
                            )
        button17 = tk.Button(self.master, text = "SW", 
                            width = 10, height = 2,
                            anchor = tk.SW
                            )
        button18 = tk.Button(self.master, text = "S", 
                            width = 10, height = 2,
                            anchor = tk.S
                            )
        button19 = tk.Button(self.master, text = "SE", 
                            width = 10, height = 2,
                            anchor = tk.SE
                            )

        # 文字の左寄せ
        button20 = tk.Button(self.master, text = "o文字左寄せo\noo文字左寄せoo\nooo文字左寄せooo", 
                            width = 25, height = 3,
                            justify = tk.LEFT
                            )       
        
        # 文字の中央寄せ
        button21 = tk.Button(self.master, text = "o文字中央寄せo\noo文字中央寄せoo\nooo文字中央寄せooo", 
                            width = 25, height = 3,
                            justify = tk.CENTER
                            )

        # 文字の右寄せ
        button22 = tk.Button(self.master, text = "o文字右寄せo\noo文字右寄せoo\nooo文字右寄せooo", 
                            width = 25, height = 3,
                            justify = tk.RIGHT
                            )

        # 枠線幅
        button23 = tk.Button(self.master, text = "枠線幅2", 
                            bd = 2
                            )
        button24 = tk.Button(self.master, text = "枠線幅4", 
                            bd = 4
                            )
        button25 = tk.Button(self.master, text = "枠線幅6", 
                            bd = 6
                            )
        # 横方向のパディング(文字列の両側の隙間)
        button26 = tk.Button(self.master, text = "padx=2", 
                            padx = 2
                            )
        button27 = tk.Button(self.master, text = "padx=10", 
                            padx = 10
                            )
        button28 = tk.Button(self.master, text = "padx=20", 
                            padx = 20
                            )
        # 縦方向のパディング(文字列の上下の隙間)
        button29 = tk.Button(self.master, text = "pady=2", 
                            pady = 2
                            )
        button30 = tk.Button(self.master, text = "pady=10", 
                            pady = 10
                            )
        button31 = tk.Button(self.master, text = "pady=20", 
                            pady = 20
                            )
        # 横、縦方向のパディング組み合わせ
        button32 = tk.Button(self.master, text = "padx=20\npady=10", 
                            padx = 20, pady = 10
                            )
        # wraplength
        button33 = tk.Button(self.master, text = "wraplength文字の折り返し幅", 
                            wraplength = 60
                            )

        # 色の変更
        button34 = tk.Button(self.master, text = "色の変更", 
                            bg = "#00ff00", # 通常時の背景色
                            fg = "#ff00ff", # 通常時の文字色
                            activebackground = "#ff0000", # クリックされた時の背景色
                            activeforeground = "#0000ff"  # クリックされた時の文字色
                            )
        # ハイライトカラー
        button35 = tk.Button(self.master, text = "ハイライトカラー",  # 無効?
                            highlightcolor = "#ffff00" # ハイライトカラー
                            )
        # アンダーライン(1文字)
        button36 = tk.Button(self.master, text = "アンダーライン(1文字)", 
                            underline = 2      # 2番目(0始まり)の文字にアンダーライン、-1で無し
                            ) 
        # アンダーライン(全文字)
        button37 = tk.Button(self.master, text = "アンダーライン(全文字)", 
                            font = ("", 9, "underline")      # 文字全体のアンダーラインはfontで指定
                            ) 

        # ボタンの配置
        button1.grid(row=0, column=0)
        button2.grid(row=0, column=1)
        button3.grid(row=0, column=2)

        button4.grid(row=1, column=0)
        button5.grid(row=1, column=1)

        button6.grid(row=2, column=0)
        button7.grid(row=2, column=1)
        button8.grid(row=2, column=2)
        button9.grid(row=3, column=0)
        button10.grid(row=3, column=1)

        button11.grid(row=4, column=0)
        button12.grid(row=4, column=1)
        button13.grid(row=4, column=2)
        button14.grid(row=5, column=0)
        button15.grid(row=5, column=1)
        button16.grid(row=5, column=2)
        button17.grid(row=6, column=0)
        button18.grid(row=6, column=1)
        button19.grid(row=6, column=2)

        button20.grid(row=7, column=0)
        button21.grid(row=7, column=1)
        button22.grid(row=7, column=2)

        button23.grid(row=8, column=0)
        button24.grid(row=8, column=1)
        button25.grid(row=8, column=2)

        button26.grid(row=9, column=0)
        button27.grid(row=9, column=1)
        button28.grid(row=9, column=2)

        button29.grid(row=10, column=0)
        button30.grid(row=10, column=1)
        button31.grid(row=10, column=2)
        button32.grid(row=11, column=0)

        button33.grid(row=12, column=0)
        button34.grid(row=12, column=1)
        button35.grid(row=12, column=2)

        button36.grid(row=13, column=0)
        button37.grid(row=13, column=1)

    def button_click(self):
        print("ボタンがクリックされた")

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

【Python/tkinter】ウィンドウの作成

tkinterでは、クラスを用いた書き方と、用いない書き方がありますが、クラスを用いたウィンドウ作成のための最小限のコードを示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

上記コードを実行すると以下のようなウィンドウが表示されます。

(実行結果)

 

もう少し肉付けをして、ウィンドウのタイトルと表示されるウィンドウのサイズを指定します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("タイトル")    # ウィンドウタイトル
        self.master.geometry("400x300") # ウィンドウサイズ(幅x高さ)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行結果)

 

geometryで指定したサイズは、ウィンドウのタイトルーバー付のサイズではなく、ウィンドウの内側のサイズとなります。