15
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Python/Tkinter】commandに引数を渡す方法

Posted at

##今回紹介すること
TkinterでButtonウィジェット等のオプション:commnadに引数を渡す方法を紹介します。

サンプルコード(成功例)

import tkinter as tk
import tkinter.ttk as ttk

class Application(ttk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.master.title("Title")
        self.master.geometry("400x300")
        self.create_widgets()
    
    def create_widgets(self):
        for i in range(1, 11):
            ttk.Button(self, text=f"ボタン{i}", command=self.show_message(i)).pack()
    
    def show_message(self, index):
        def inner():
            print(f"ボタン{index}がクリックされました")
        return inner

def main():
    root = tk.Tk()
    app = Application(master=root)
    app.mainloop()

if __name__ == "__main__":
    main()

やりがちな失敗例と解決法

なぜ失敗するのか

普通に書こうとすると,以下のように書いてしまいがちです:


import tkinter as tk
import tkinter.ttk as ttk

class Application(ttk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.master.title("Title")
        self.master.geometry("400x300")
        self.create_widgets()
    
    def create_widgets(self):
        for i in range(1, 11):
            ttk.Button(self, text=f"ボタン{i}", command=self.show_message(i)).pack()
    
    def show_message(self, index):
        print(f"ボタン{index}がクリックされました")

def main():
    root = tk.Tk()
    app = Application(master=root)
    app.mainloop()

if __name__ == "__main__":
    main()

残念ながら,これでは期待どおりの動作はしません。

動かしてみればわかりますが,ボタン生成時にself.show_message(i)が実行され,ボタン{index}がクリックされましたがすべてプリントされてしまいます。そして,肝心のボタンクリック時には,なんの動作もなされません。これは,commandオプションの挙動が下記のようになっていることに関係しています。

  • ウィジェット生成時self.show_message(i)が実行される
  • ボタンクリック時self.show_message(i)()が実行される

解決法

上記のような面倒くさい仕様のため,callback関数を定義する際にも一工夫が必要となります。必要な部分のコード(今回の場合はprint(f"ボタン{index}がクリックされました"))をself.show_message(i)で必要な部分のコードを実行させず,self.show_message(i)()で実行させる必要があるのです。

そのために,**Inner Function(内部関数)**を使用します。下記のようになっていると考えるとわかりやすいです。


def show_message(index):
    def inner():
        print(f"ボタン{index}がクリックされました")
    return inner

command = show_message(1)
command()

show_message関数にはinner関数(内部関数)が定義されており,それをリターンしています。つまり,下から2行目のcommandにはinner関数が入っています。ただし,inner関数の中身は実行されていませんので,printはされません。そして最後の行で,inner関数を実行しています。

つまり,show_message(1)では見た目上何も起こらないが,show_message(1)()とすることで目的の部分を実行できるようになるということです。

##参考

15
17
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?