質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Q&A

解決済

2回答

4488閲覧

pythonのtkinterのオプションメニュー更新方法

Kisssy

総合スコア2

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

0グッド

0クリップ

投稿2020/07/24 09:51

編集2020/07/25 05:26

前提・実現したいこと

初めてPythonのtkinterを利用してツール作成を行っています。
やりたいこととしては、テキストボックスにパスを入力し、ボタンを押下すると
オプションメニューの値が更新される処理を実現したいです。

発生している問題・エラーメッセージ

どのようにしてオプションメニューの値を更新すればいいでしょうか。 当然ですが、このまま実行しても変数に値が入るだけで更新はされません。 オプションメニューのインスタンス化する場所を変更する? 詰まっているためご教授頂けると嬉しいです。

該当のソースコード

python

1import sys 2import abc 3import re 4from copy import copy 5import tkinter as tk 6 7#test path 8test_path = 'xxx' 9file_contents = [] 10#---------------------------------------- 11 12class TKroot(tk.Tk): 13 def __init__(self): 14 super().__init__() 15 self.title('xxx') 16 self.geometry('1000x600') 17 self.config(bg = 'white') 18 19 20class Application(tk.Frame): 21 file_mode = 'r' 22 23 def __init__(self, master = None, **kwargs): 24 super().__init__(master, **kwargs) 25 self.opt_value = {} 26 self.entry_value = {} 27 self.create_widget() 28 self.pack() 29 30 def create_widget(self): 31 global file_contents 32 33 #create frame 1. 34 self.f1 = tk.Frame(self, bg = 'coral') 35 self.f1.place(relheight = 0.1, width = 1000, height = 10) 36 self.f1_label = tk.Label(self.f1, text = 'path : ') 37 self.f1_label.pack(side = tk.LEFT) 38 var = tk.StringVar() 39 self.f1_entry = tk.Entry(self.f1, width = 100, textvariable = var) 40 self.f1_entry.pack(side = tk.LEFT) 41 42 f1_file = self.file_new() 43 self.f1_button = tk.Button(self.f1, text = 'update', width = 30, command = lambda : f1_file.control(Read, var)) 44 self.f1_button.pack(side = tk.RIGHT, padx = 40) 45 46 #create frame 2. 47 self.f2 = tk.Frame(self, bg = 'red') 48 self.f2.place(width = 1000, y = 70) 49 self.f2_label = tk.Label(self.f2, text = 'function name : ') 50 self.f2_label.pack(side = tk.LEFT) 51 self.f2_opt_variable = tk.StringVar() 52 #self.f2_opt_variable.set('chose a function') 53 if file_contents == []: 54 file_contents.append("test") 55 file_contents.append("test") 56 self.f2_opt_variable.set(file_contents[0]) 57 self.f2_file_contents = copy(file_contents) 58 self.f2_opt = tk.OptionMenu(self.f2, self.f2_opt_variable, *self.f2_file_contents) 59 self.f2_opt.config(width = 80) 60 self.f2_opt.pack(side = tk.LEFT) 61 62 def file_new(self): 63 return File(Application.file_mode) 64 65 66class File: 67 def __init__(self, file_mode): 68 self._file_path = '' 69 self._file_mode = file_mode 70 71 def _get_items(self, file_type): 72 file_types_read = file_type(self._file_path, self._file_mode) 73 return file_types_read.lines() 74 75 def _set_items(self, function_list): 76 global file_contents 77 file_contents = [s for s in function_list if re.match(r'xxx', s)] 78 79 def control(self, file_type, path_object): 80 self._file_path = self._get_filepath(path_object) 81 self._set_items(self._get_items(file_type)) 82 83 def _get_filepath(self, path_object): 84 return path_object.get() 85 86class FileType(abc.ABC): 87 @abc.abstractclassmethod 88 def lines(self, file_path): 89 pass 90 91#------------------------------------途中------------------------------------- 92class Write(FileType): 93 def __init__(self, args : dict): 94 self._file_path = args.get('file_path', test_path) 95 self._file_mode = args.get('file_mode', 'w') 96 self._file_contens = [] 97 98 def lines(self): 99 with FileAccess(self._file_path) as file: 100 self._file_contens = file._writelines() 101 return self._file_contens 102#----------------------------------------------------------------------------- 103class Read(FileType): 104 def __init__(self, args_1, args_2): 105 self._file_path = args_1 106 self._file_mode = args_2 107 self._file_contens = [] 108 109 def lines(self): 110 with FileAccess(self._file_path) as file: 111 self._file_contens = file._readlines() 112 return self._file_contens 113 114 115class FileAccess(object): 116 def __init__(self, file_path): 117 self._file_path = file_path 118 self._file = None 119 120 def _open(self): 121 self._file = open(self._file_path) 122 #self._file = open(test_path) 123 124 def _close(self): 125 self._file.close() 126 127 def __enter__(self): 128 self._open() 129 return self 130 131 def __exit__(self, exc_type, exc_value, traceback): 132 self._close() 133 134 def _readlines(self): 135 file_read_contens = self._file.readlines() 136 return file_read_contens 137 138 def _writelines(self): 139 file_write_contens = self._file.writelines() 140 return file_write_contens 141 142def main(): 143 root = TKroot() 144 app = Application(master = root, width = 1000, height = 600) 145 app.mainloop() 146 147if __name__ == "__main__": 148 main()

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teamikl

2020/07/24 18:07

>ボタンを押下すると Update ボタンを押すとNameError: name 'Read' is not definedとでるので、 肝心な部分が省略されてます。 ---- 方向性としては、f2_opt_variable を引数として渡すか、 イベントで呼び出す関数をApplicationメソッドにして、 インスタンス経由でアクセスのどちらかになると思います。
Kisssy

2020/07/25 05:37

teamikl様 ご回答ありがとうございます。省略部分を追記しました。 >>>方向性としては、f2_opt_variable を引数として渡すか、 f2_opt_variableを引数として渡す方法を行ってみましたが、オプションメニューの初期値が変更されるだけでデータとなる中身は更新されていませんでした。 >>>イベントで呼び出す関数をApplicationメソッドにして、 >>>インスタンス経由でアクセスのどちらかになると思います。 これは実施していないため憶測で記載し申し訳ありませんが、これも結局は引数で渡さないだけで、上の結果と同じようなことになると思われますがどうでしょうか。 すみません、初心者でいろいろ質問してしまいまして。。。
teamikl

2020/07/25 05:54

> 変数に値が入るだけで更新はされません。 「更新」とは具体的にどんな状態ですか? >「オプションメニューの値」 オプションメニュー自体は選択された値を持っていなくて、 変数に指定するのは、表示されているラベルの様です。 選択された値が必要な場合は、コンボボックスの方が適してます。 OptionMenuを項目選択としてつかうには、可能ですが、 項目毎にイベントハンドラを設定する必要があります。 ---- 一応、f2_opt_variable (StringVar) のかわりに f2_opt (OptionMenu)を渡すと、他にも色々出来ます。 回答にサンプルで項目を追加する例を記述しました。
Kisssy

2020/07/25 14:55

>>>「更新」とは具体的にどんな状態ですか? オプションメニューの内部データを更新することです。 回答の返信に結果を記載しました。 >>>選択された値が必要な場合は、コンボボックスの方が適してます。 わざわざイベントハンドラを設定しなくとも、textvariableで手軽に取得できますね。 今回はteamikl様がサンプルを記載くださったため、そちらを利用して実装することにします。 いろいろ答えていただきありがとうございました。本当に助かりました。
guest

回答2

0

"opt["menu"]"の部分

「Menuオブジェクト」にアクセスしてます。

通常のウィンドウに付くメニューと同じです。
ウィンドウのメニューバーではなく、
ウィジェットに紐づいたメニューといった扱いでしょうか。

root = tk.Tk() menu = root["menu"] = tk.Menu(root) menu.add_command(label="Quit", command=root.quit) root.mainloop()

コードによっては root.config(menu=menu) で
メニューバーを登録してるかもしれませんが、
アクセス方法が異なるだけで、内容は同じ。

※ 全てのウィジェットに["menu"]オプションが有るわけではありません。

  • [tkinter - 簡易的なリファレンス オプション設定

](https://docs.python.org/ja/3/library/tkinter.html)

# class OptionMenu __init__ # Menuオブジェクトを作成 menu = self.__menu = Menu(self, name="menu", tearoff=0) self.menuname = menu._w # command はキーワード引数でのみ指定可 -> callback変数へ # 'command' is the only supported keyword callback = kwargs.get('command') if 'command' in kwargs: del kwargs['command'] if kwargs: raise TclError('unknown option -'+kwargs.keys()[0]) # 一番目の要素をメニューに追加 menu.add_command(label=value, command=_setit(variable, value, callback)) # 残りの要素をメニューに追加 for v in values: menu.add_command(label=v, command=_setit(variable, v, callback)) # オプションに設定 self["menu"] = menu

OptionMenuをメニューバーと(右クリック時の)コンテキストメニューで再利用

opt["menu"] は Menu オブジェクトなので、
メニューバーや右クリックメニューで再利用できます。

(他のオプションが全く指定できないので、
実用性は高くありません。実演のみ)

python

1#!/usr/bin/env python3.8 2 3import tkinter as tk 4 5root = tk.Tk() 6root.geometry("400x400") 7 8# ウィンドウのメニュー 9menu = tk.Menu(root) 10root["menu"] = menu 11# root.config(menu=menu) 12 13menu.add_command(label="Quit", command=root.quit) 14 15 16var = tk.StringVar() 17def fileMenu(cmd): 18 print("FILE", cmd) 19 if cmd == "Open": 20 pass 21 elif cmd == "Save": 22 pass 23 elif cmd == "Quit": 24 root.quit() 25 26opt = tk.OptionMenu(root, var, *["Open", "Save", "Quit"], command=fileMenu) 27opt.pack() 28 29# ウィンドウのメニューに OptionMenuを追加 30menu.add_cascade(label="File", menu=opt["menu"]) 31 32# 右クリックでメニューを表示 33def popup(e): 34 try: 35 opt["menu"].tk_popup(e.x_root, e.y_root, 0) 36 finally: 37 opt["menu"].grab_release() 38 39root.bind("<Button-3>", popup) 40 41root.mainloop()

投稿2020/07/25 17:00

teamikl

総合スコア8681

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

OptionMenu に項目を追加するサンプル

python

1#!/usr/bin/env python3.8 2 3import tkinter as tk 4 5 6class Application(tk.Frame): 7 def __init__(self, master=None, **kwargs): 8 super().__init__(master, **kwargs) 9 10 var = tk.StringVar() 11 var2 = tk.StringVar() 12 entry = tk.Entry(self, textvar=var) 13 button = tk.Button(self, text="Update", command=self.onUpdateButton) 14 menu = tk.OptionMenu(self, var2, *["AAA", "BBB"], command=self.onMenuCommand) 15 16 var.set("CCC") 17 18 entry.pack(side=tk.LEFT) 19 button.pack(side=tk.RIGHT) 20 menu.pack() 21 22 self.var = var 23 self.var2 = var2 24 self.opt = menu 25 self.pack() 26 27 def onMenuCommand(self, cmd): 28 print(cmd) 29 30 def onUpdateButton(self): 31 try: 32 text = self.var.get() 33 self.opt["menu"].add_command( 34 label=text, command=tk._setit(self.var2, text, self.onMenuCommand)) 35 finally: 36 self.var.set("") 37 38 39if __name__ == '__main__': 40 Application(master=tk.Tk()).mainloop() 41

OptionMenu には、通常のメニューと同じように項目を追加出来ます。

…が、内部関数(_setit) が必要になる事から、恐らく想定されてない用途なので、
(項目がネストしないなら、)Comboboxを入力禁止にして使う方が良いかもしれないです。


追記: OptionMenuの要素を更新する例

python

1 2## class Application create_widgets 3 4 self.f1_button = tk.Button(self.f1, text = 'update', width = 30, command = lambda : f1_file.control(Read, var, self)) 5 6 7## class File 8 9 def control(self, file_type, path_object, app): 10 self._file_path = self._get_filepath(path_object) 11 self._set_items(self._get_items(file_type)) 12 13 # ※ このコードはここには書かず Applicationクラスのメソッドにした方がよい。 14 opt = app.f2_opt["menu"] 15 opt.delete(0, "last") 16 for line in file_contents: 17 opt.add_command(label=line.strip()) # TODO: add command= 18 app.f2_opt_variable.set("")

投稿2020/07/24 18:57

編集2020/07/25 06:27
teamikl

総合スコア8681

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kisssy

2020/07/25 05:46

OptionMenu に項目を追加するサンプルの見本コードありがとうございます。(他で参考にさせていただきます) 今回の場合はメニューにデータの追加を行いたいわけではなくデータ全てを更新したいです。 こちらの情報提示不足でした。 やりたいことは、 1 入力ボックスにパス(ソースコードのファイル)を入力する 2 ボタン(更新)を押下 3 指定したソースコードのデータを解析(正規表現)し、抽出 4 オプションメニューにその抽出データを格納、表示する です。
teamikl

2020/07/25 14:46 編集

add_command の他にもメニューに対する操作は可能ですが、 更新となると、delete してから add という流れになりますね。 OptionMenu に対する操作は示せたと思いますので、 手順としては 1. StringVar と OptionMenu 両方を渡す 2. 要素を全削除 opt["menu"].delete(0, "last") 3. add_command で追加 4. 注意点: command引数は全要素に追加する必要あり。  OptionMenu は選択された値を持たないので、  どの値が選ばれたかの情報は、イベントハンドラに関連付けておく必要があります。 上で提示したサンプルのようにtk._setitを使うと、選択された値を変数に格納できます。 ※ OptionMenu がコンストラクタ内部でやってる処理と同じです。 レイアウトを固定できるなら、OptionMenuのインスタンスを作り直すのも手です。
Kisssy

2020/07/25 15:07

teamiklさんのアドバイス通りに実装し、期待通りの動作ができることを確認できました。 >>>レイアウトを固定できるなら、OptionMenuのインスタンスを作り直すのも手です。 固定できます。一通り終わればリファクタをするつもりですので、その際に作り直す予定です。 ご丁寧なアドバイスありがとうございました。これで続きが作れそうです。 すみません、最後にもう一つ質問してもよろしいでしょうか。 一箇所わからない箇所がありまして、"opt["menu"]"の部分です。 これはなにをやっているのでしょうか。いまいちピンときていません。
teamikl

2020/07/25 17:00

>最後にもう一つ質問 コードを載せたため、回答にして返信しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問