Help us understand the problem. What is going on with this article?

PythonのGUIツールtkinterチートシート

More than 1 year has passed since last update.

はじめに

Pythonでテニスの動画解析ツールを自作してみた に触発されて、tkinterでバスケの動画解析ツールを自作しました。
その際に調べたtkinterについてまとめます。

ウィンドウ、フレーム、ウィジェットの関連

image.png

全ソース

https://github.com/nanako-ut/tkinter-cheatSheet

基本

以下を基本とし、create_widgets中に追記をしていきます。

import sys
import os
import numpy as np
import tkinter as tk

import tkinter.ttk as ttk
from PIL import Image, ImageTk
from tkinter import messagebox as tkMessageBox
from tkinter import filedialog as tkFileDialog


# アプリケーション(GUI)クラス
class Application(tk.Frame):
    DEBUG_LOG = True
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()

        self.create_widgets()

    def create_widgets(self):
        print('DEBUG:----{}----'.format(sys._getframe().f_code.co_name)) if self.DEBUG_LOG else ""

# 実行
root = tk.Tk()        
myapp = Application(master=root)
myapp.master.title("My Application") # タイトル
myapp.master.geometry("1000x500") # ウィンドウの幅と高さピクセル単位で指定(width x height)

myapp.mainloop()

部品を配置する入れ物(コンテナ、フレーム)を作成する

PaneWindow

PanedWindowは、画面を任意の数で分割できるウィジェット。区切られた各々はサッシで区切られており、サッシはマウスで移動可能。

ウィジェット作成・配置

    def create_widgets(self):
        # ペインウィンドウ
        # PanedWindow
        ##  orient : 配置(vertical or horizontal)
        ##  bg : 枠線の色
        # pack
        ##  expand :可変(True or False(固定)
        ##  fill : スペースが空いている場合の動き(tk.BOTH 縦横に広がる)
        ##  side : 配置する際にどの方向からつめていくか(side or top ・・・)
        pw_main = tk.PanedWindow(self.master, orient='horizontal')
        pw_main.pack(expand=True, fill = tk.BOTH, side="left")

        pw_left = tk.PanedWindow(pw_main, bg="cyan", orient='vertical')
        pw_main.add(pw_left)
        pw_right = tk.PanedWindow(pw_main, bg="yellow", orient='vertical')
        pw_main.add(pw_right)

実行結果

image.png

Frame

ボタンや入力フィールド等複数の部品をまとめるもの

ウィジェット作成・配置

    def create_widgets(self):
        # 
        # ~上記記載分を省略~
        #  
        # 上記の続きに追加
        # フレーム
        ##  bd :ボーダーの幅
        ##  relief :フレームの枠の形
        fm_select = tk.Frame(pw_left, bd=2, relief="ridge")
        pw_left.add(fm_select)

実行結果

image.png

ラベル

Label

ウィジェット作成・配置

    def create_widgets(self):
        # 
        # ~上記記載分を省略~
        #  
        # 上記の続きに追加
        # ラベル
        ## padx , pady :外側の横、縦の隙間
        label_fpath = tk.Label(fm_select, text="ファイルパス(入力)", width=20)
        ## ラベルを配置
        label_fpath.grid(row=0, column=0, padx=2, pady=2)

実行結果

image.png

1行入力

Entry

ウィジェット作成・配置

    def create_widgets(self):
        # 
        # ~上記記載分を省略~
        #  
        # 上記の続きに追加
        # 1行入力
        ##  justify:文字寄せ(center or left or right)
        ##  sticky:スペースが空いている場合の動き(tk.W + tk.E 縦横に広がる)
        entry_fpath = tk.Entry(fm_select, justify="left", width=50)
        entry_fpath.grid(row=0, column=1, sticky=tk.W + tk.E,padx=2, pady=2)

削除・追加

        ## 削除
        entry_fpath.delete( 0, tk.END ) 
        ## 先頭行に値を設定
        entry_fpath.insert( 0, "input your file path..." ) 

値を取得

        ## 値を取得
        print('Entryの初期値を出力:{}'.format(entry_fpath.get()))

実行結果

image.png

複数行入力

Text

ウィジェット作成・配置

    def create_widgets(self):
        # 
        # ~上記記載分を省略~
        #  
        # 上記の続きに追加
        # ラベル
        label_fpaths = tk.Label(fm_select, text="ファイルパス(複数)", width=20)
        label_fpaths.grid(row=1, column=0, padx=2, pady=2)

        # 複数行入力
        ## witdh :入力する文字数
        ## wrap:長い行の折り返し方法(tk.CHAR: 文字単位で折り返す or tk.NONE: 折り返ししない or ・・・)
        selected_files = tk.Text(fm_select, height=10, width=50, wrap=tk.CHAR)
        selected_files.grid(row=1, column=1, padx=2, pady=2)

削除・追加

        ## 全行削除
        selected_files.delete( '1.0', tk.END ) 

        ## 値を設定
        selected_files.insert( tk.END, "No.1 file path\n" ) 
        selected_files.insert( tk.END, "No.2 file path\n" ) 

値を取得

        ## 全行取得        
        print(selected_files.get('1.0', tk.END ))

実行結果

image.png

ボタン

Button

ウィジェット作成・配置

ボタンを押した時に実行する処理は、「command = メソッド名」で指定

    def create_widgets(self):
        # 
        # ~上記記載分を省略~
        #  
        # 上記の続きに追加
        # ボタン
        ## columnspan : 何列に渡って配置するか
        ## rowspan : 何行に渡って配置するか
        btn_select_file = tk.Button(fm_select, text="ファイル選択", command=self.select_file) 
        btn_select_file.grid(row=2, column=1, sticky=tk.W + tk.E, padx=2, pady=2)

    def select_file(self):
        print('select_file...')

実行結果

image.png

Buttonの処理(イベントに引数を渡す)

commandで指定する関数をlambda式で記述

ウィジェット作成・配置

    def create_widgets(self):
        # 
        # ~上記記載分を省略~
        #  
        # 上記の続きに追加
        # ボタン
        # ボタンイベントに引数を渡す
        fm_btns = tk.Frame(pw_left, bd=2, relief="ridge")
        fm_btns.pack(side="top")
        pw_left.add(fm_btns)

        btn_tool_1 = tk.Button(fm_btns, text="ボタン(1)", command=lambda:self.btn_ivent("test1"), width=20) 
        btn_tool_1.grid(row=3, column=0, sticky=tk.W + tk.E, padx=2, pady=10)

        btn_tool_2 = tk.Button(fm_btns, text="ボタン(2)", command=lambda:self.btn_ivent("test2"), width=20) 
        btn_tool_2.grid(row=3, column=1, sticky=tk.W + tk.E, padx=2, pady=10)

    def btn_ivent(self, msg):
        print(msg)

実行結果

ボタン(1)を押すと、引数で渡した「test1」が出力されて、
ボタン(2)を押すと、引数で渡した「test2」が出力される。

image.png

ファイル選択

上記で記述した select_file メソッドを実装。

ウィジェット作成・配置

jpg、png形式のファイルのみ選択可能なファイル選択ダイアログを表示する

    def select_file(self):
        # 選択可能な拡張子を限定
        fTyp =[("", "*.jpg;*.png")]
        iDir = os.path.abspath(os.path.dirname("__file__"))
        files = tkFileDialog.askopenfilenames(filetypes = fTyp, initialdir = iDir)

選択したファイル名を一覧表示する

    def select_file(self):
        # 選択可能な拡張子を限定
        fTyp =[("", "*.jpg;*.png")]
        iDir = os.path.abspath(os.path.dirname("__file__"))
        files = tkFileDialog.askopenfilenames(filetypes = fTyp, initialdir = iDir)

        # 選択されたファイルパスを表示
        # 前回分を全行削除
        self.selected_files.delete( '1.0', tk.END ) 
        file_list = list(files)
        if(len(file_list) <= 10):
            for i in np.arange(0, len(file_list)):
                self.selected_files.insert("end", "{}\n".format(file_list[i]))                

実行結果

image.png

画像表示

ウィジェット作成・配置

    def create_widgets(self):
        # 
        # ~上記記載分を省略~
        #  
        # 上記の続きに追加
        # 画像表示
        # 画面の右側エリアに画像を表示する
        fm_img = tk.Frame(pw_right, bd=2, relief="ridge")
        pw_right.add(fm_img)

        self.panel_img = tk.Label(fm_img)
        self.panel_img.pack()

画像を表示

ファイル選択ダイアログで選択した画像を1枚表示する

    def select_file(self):
        # 
        # ~ファイル選択・表示部分(上記記載分)を省略~
        #  
        # 上記の続きに追加

        # 先頭の画像ファイルを表示する
        self.show_image(file_list[0])

    def show_image(self, file_path):
        w,h = 500, 500
        image = Image.open(file_path)
        image = image.resize((w, h), Image.ANTIALIAS)
        self.img = ImageTk.PhotoImage(image)
        self.panel_img.configure(image=self.img)
        self.panel_img.pack()

画像をクリックした時の処理を追加

画像表示ウィジェットにイベントをバインド

    def create_widgets(self):
        # 
        # ~上記記載分を省略~
        #  
        # 画像表示用のウィジェットに以下を追記
        self.panel_img.bind("<Button-1>", self.img_click)

    def img_click(self, ev):
        print('クリックされた位置:x {}、y {}'.format(ev.x, ev.y))

実行結果

image.png

参考(Thanks!)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away