tklib.tkgui.tksimple_gui のソースコード
"""
Tkinter GUIの構築と管理のためのヘルパー関数とウィジェットを提供します。
このモジュールは、Tkinterベースのグラフィカルユーザーインターフェースを簡単に作成するための
ユーティリティ関数とクラスを含んでいます。
ウィジェットの追加、設定の管理、ダイアログの生成など、一般的なGUIタスクを簡素化します。
関連リンク:
:doc:`tksimple_gui_usage`
"""
import tklib.tkimport as imp
tk = imp.import_lib("tkinter", stop_by_error = False)
from tkinter import ttk
from tkinter import simpledialog
imp.messages(stop_by_error = True)
from tklib.tkobject import tkObject
from tklib.tkutils import print_data, pint, pfloat, index2val, pconv_by_type
from tklib.tkparams import tkParams
from tklib.tkgui.tktkinter import PathFrame
[ドキュメント]
def get_window_from_plt(plt):
"""
matplotlibのFigureManagerからTkinterウィンドウオブジェクトを取得します。
:param plt: object: matplotlib.pyplotモジュールまたはFigureManagerオブジェクト。
:returns: tk.Tk または tk.Toplevel: 関連するTkinterウィンドウオブジェクト。
"""
return plt.get_current_fig_manager().window
[ドキュメント]
def show_varibles(config):
"""
指定されたオブジェクトの非callableかつ非リスト/タプルのメンバー変数を表示します。
主にデバッグ目的で使用され、オブジェクトの現在の状態をコンソールに出力します。
アンダースコアで始まる変数、callableな変数、リストやタプル型の変数はスキップされます。
:param config: object: メンバー変数を表示するオブジェクト。
:returns: None
"""
config.print_parameters(heading = "config:")
for key in dir(config):
if key.startswith("_"): continue
val = getattr(config, key, None)
if val is None or callable(val): continue
t = type(val)
if t is list or t is tuple: continue
print(f"key={key} val={val} type(val)={type(val)}")
[ドキュメント]
def update_variables(config, vars):
"""
add_widgetsで設定されたTkinterのVariableオブジェクトから値を取得し、別の設定オブジェクトに更新します。
configオブジェクト内の`var_`プレフィックスを持つ属性(`tk.StringVar`などのTkinter Variableインスタンス)を検索し、
その`get()`メソッドで取得した値を`vars`オブジェクトの対応する属性に設定します。
:param config: tkParams: `var_`プレフィックスを持つTkinter Variableインスタンスを保持する設定オブジェクト。
:param vars: tkParams: 更新される値を保持するターゲットオブジェクト。
:returns: None
"""
for key in dir(config):
if not key.startswith("var_"): continue
widget = getattr(config, key, None)
if widget is None or callable(widget): continue
var_key = key[4:]
var = getattr(vars, var_key, None)
if var is None: continue
setattr(vars, key[4:], widget.get())
[ドキュメント]
class tkScrolledListbox(tk.Listbox):
"""
垂直スクロールバー付きのTkinter Listboxウィジェットです。
標準のtk.Listboxに垂直スクロールバーを統合し、長いリストでも内容を簡単に操作できるようにします。
"""
def __init__(self, master, **key):
"""
tkScrolledListboxの新しいインスタンスを初期化します。
:param master: tk.Widget: 親ウィジェット。
:param **key: dict: `tk.Listbox`に渡される追加のキーワード引数。
"""
self.frame = tk.Frame(master)
self.yscroll = tk.Scrollbar(self.frame, orient = tk.VERTICAL)
self.yscroll.pack(side = tk.RIGHT, fill = tk.Y, expand = 1)
key['yscrollcommand'] = self.yscroll.set
tk.Listbox.__init__(self, self.frame, **key)
self.yscroll.config(command = self.yview)
self.pack(side = tk.LEFT, fill = tk.BOTH, expand = 1)
[ドキュメント]
def add_widgets(parent, widgets, vars = None, config = None, **kwargs):
"""
指定された定義に基づいてTkinterウィジェットを親フレームに追加します。
`widgets`という多次元リストの辞書定義に従って、ボタン、ラベル、入力フィールド、リストボックスなどの
さまざまなウィジェットを作成し、それらを配置します。
ウィジェットに関連付けられたTkinter Variableオブジェクトは`config`オブジェクトに格納され、
初期値は`vars`オブジェクトからロードされます。
:param parent: tk.Widget: ウィジェットが追加される親Tkinterウィジェット。
:param widgets: list[list[dict]]: ウィジェットの定義を含む辞書の多次元リスト。
各辞書はウィジェットのタイプ、名前、変数名、初期値などを指定します。
:param vars: tkParams, optional: ウィジェットの初期値を提供するオブジェクト。デフォルトは`config`。
:param config: tkParams, optional: 作成されたウィジェットオブジェクトと関連するTkinter Variableを格納する`tkParams`オブジェクト。
デフォルトは新しい`tkParams`インスタンス。
:param **kwargs: dict: `top_frame.pack()`に渡される追加のキーワード引数。
:returns: tuple:
- tk.Frame: 全てのウィジェットを格納する最上位フレーム。
- tkParams: 作成されたウィジェットと関連するTkinter Variableを含む設定オブジェクト。
"""
if config is None:
config = tkParams()
if vars is None:
vars = config
config.frames = []
top_frame = tk.Frame(parent)
top_frame.pack(**kwargs)
for widget_list in widgets:
frame = tk.Frame(top_frame)
frame.pack(**kwargs)
config.frames.append(frame)
for inf in widget_list:
# print("type(inf)=", type(inf))
if type(inf) is not dict:
print("Error in tksimple_gui.add_widgets: Contens of widget_list (inf) must be dict:", inf)
exit()
t = inf["type"]
wt = inf.get("widget_type", "widget")
varname = inf.get("varname", None)
variable = None
if varname:
variable = getattr(config, f"var_{varname}", None)
vartype = inf.get("vartype", "str")
val = None
if varname:
val = getattr(vars, varname, None)
if val is None:
val = inf.get("value", None)
label = inf.get("label", "no text")
text = inf.get("text", "no text")
width = inf.get("width", None)
height = inf.get("height", None)
command = inf.get("command", None)
side = inf.get("side", "left")
anchor = inf.get("anchor", None)
expand = inf.get("expand", None)
fill = inf.get("fill", None)
w = None
if t == "widget":
name = inf.get("name", "widget")
w = inf.get("widget", None)
if w is None: continue
setattr(config, f"{wt}_{name}", w)
elif t == "button":
name = inf.get("name", "button")
w = tk.Button(frame, text = text, command = command)
setattr(config, f"button_{name}", w)
elif t == "label":
name = inf.get("name", "label")
if varname is not None and variable is None:
# NOTE: この行の `sel` は未定義変数です。既存ロジックは変更できないため、そのままにしています。
# 実行時にはNameErrorが発生する可能性があります。
variable = tk.StringVar(value = sel)
elif variable:
# NOTE: この行の `sel` は未定義変数です。既存ロジックは変更できないため、そのままにしています。
# 実行時にはNameErrorが発生する可能性があります。
variable.set(sel)
w = tk.Label(frame, text = label, textvariable = variable)
setattr(config, f"label_{name}", w)
if variable:
setattr(config, f"var_{varname}", variable)
elif t == "entry":
# name = inf.get("name", "entry")
if variable is None:
if vartype == "str":
variable = tk.StringVar(value = val, name = varname)
elif vartype == "float":
variable = tk.DoubleVar(value = val, name = varname)
elif vartype == "int":
variable = tk.IntVar(value = val, name = varname)
elif vartype == "bool":
variable = tk.BooleanVar(value = val, name = varname)
else:
print(f"Error in tksimple_gui::add_widgets(): Invalid variable type [{vartype}]")
exit()
else:
variable.set(val)
w = tk.Entry(frame, textvariable = variable, width = width, state = inf.get("state", None))
# if val is not None:
# w.insert(0, str(val))
setattr(config, f"entry_{varname}", w)
setattr(config, f"var_{varname}", variable)
elif t == "listbox":
options = inf.get("options", [])
sel = inf.get("sel", None)
if variable is None:
# tk.StringVarのvalueには通常文字列を渡しますが、ここではリストが渡されています。
# これにより、StringVarはリストの文字列表現を内部に保持します。
variable = tk.StringVar(value = options)
# w = tk.Listbox(frame, listvariable = variable, width = width, height = height)
w = tkScrolledListbox(frame, listvariable = variable, width = width, height = height)
w.frame.pack(side = side, anchor = anchor, expand = expand, fill = fill)
if sel is not None:
w.selection_set(sel)
w.see(sel)
setattr(config, f"listbox_{varname}", w)
setattr(config, f"var_{varname}", variable)
elif t == "combobox":
options = inf.get("options", [])
sel = inf.get("sel", None)
w = ttk.Combobox(frame, values = options, width = width)
if sel is not None:
idx = pint(sel, None)
if idx is not None:
w.set(options[idx])
else:
w.set(sel)
setattr(config, f"combobox_{varname}", w)
# NOTE: ここでは variable オブジェクトではなく、ウィジェットそのもの (`w`) が格納されます。
setattr(config, f"var_{varname}", w)#variable)
elif t == "checkbox":
value = inf.get("value", None)
text = inf.get("text", "no text")
if variable is None:
variable = tk.BooleanVar(value = value)
else:
variable.set(value)
w = tk.Frame(frame)
cb = tk.Checkbutton(w, variable = variable, width = width, height = height, onvalue = True, offvalue = False)
cb.pack(side = tk.LEFT, anchor = 'w')
label = tk.Label(w, text = text)
label.pack(side = tk.LEFT, anchor = 'w')
# NOTE: ここでは "combobox_{varname}" という名前でCheckbuttonウィジェットが格納されます。
setattr(config, f"combobox_{varname}", cb)
setattr(config, f"var_{varname}", variable)
elif t == "radiobutton":
options = inf.get("options", [])
sel = inf.get("sel", None)
if variable is None:
variable = tk.IntVar(value = sel)
else:
variable.set(sel)
w = tk.Frame(frame)
for idx, s in enumerate(options):
rb = tk.Radiobutton(frame, text = s, value = idx, variable = variable)
rb.pack(side = "top", anchor = 'w')
# NOTE: ここでは "combobox_{varname}" という名前でフレームウィジェットが格納されます。
setattr(config, f"combobox_{varname}", w)
setattr(config, f"var_{varname}", variable)
elif t == "path":
save_dialog = inf.get("save_dialog", False)
file_types = inf.get("file_types", [("all", "*")])
initial_dir = inf.get("initial_dir", ".")
title = inf.get("title", "Choose file")
def path_button_click(entry_variable, save_dialog = False, file_types = [("all", "*")], initial_dir = None, title = "Choose file"):
# NOTE: この内部関数では `file_type` という未定義の変数が使用されています。
# 既存のロジックは変更できないため、そのままにしています。
# 実行時にはNameErrorが発生する可能性があります。
if save_dialog:
selpath = tk.filedialog.asksaveasfilename(filetypes = file_type, initialdir = initial_dir, title = title)
else:
selpath = tk.filedialog.askopenfilename(filetypes = file_types, initialdir = initial_dir, title = title)
if selpath is not None and selpath != '':
entry_variable.set(selpath)
entry_variable.prev_val = selpath
if command is None:
command = lambda: path_button_click(variable, save_dialog = save_dialog,
file_types = file_types, initial_dir = initial_dir, title = title)
w = tk.Frame(frame)
lbl = tk.Label(w, text = label)
lbl.pack(side = 'left', anchor = 'w')
if variable is None:
variable = tk.StringVar(value = val, name = varname)
else:
variable.set(val)
et = tk.Entry(w, textvariable = variable, width = width)
et.pack(side = 'left', anchor = 'w', expand = True, fill = 'x')
btn = tk.Button(w, text = "path", command = command)
btn.pack(side = 'left', anchor = 'w')
setattr(config, f"entry_{varname}", et)
setattr(config, f"var_{varname}", variable)
else:
print(f"Error in tksimple_gui::add_widgets(): Invalid widget type [{t}]")
exit()
if w:
w.pack(side = side, anchor = anchor, expand = expand, fill = fill)
return top_frame, config
[ドキュメント]
class CustomDialog_with_config(simpledialog.Dialog):
"""
`tkParams`オブジェクトで定義されたウィジェットを使用して、カスタムダイアログを生成します。
`simpledialog.Dialog`を継承し、`add_widgets`関数を使ってダイアログの本文に複雑なウィジェットのセットを配置できます。
ユーザーの入力は、ダイアログが適用されると`vars`オブジェクトに更新されます。
"""
def __init__(self, master, config, vars, title = None):
"""
CustomDialog_with_configの新しいインスタンスを初期化します。
:param master: tk.Widget: ダイアログの親ウィジェット。
:param config: tkParams: ダイアログに表示するウィジェットの定義(`widgets`属性)と、
作成されたウィジェットや変数を格納するための`tkParams`オブジェクト。
:param vars: tkParams: ウィジェットの初期値を提供し、ユーザーの入力で更新される変数を保持するオブジェクト。
:param title: str, optional: ダイアログのタイトル。デフォルトはNone。
"""
self.config = config
self.vars = vars
config.ret = None
super().__init__(parent=master, title=title)
[ドキュメント]
def body(self, master):
"""
ダイアログのメインコンテンツ領域を作成します。
`add_widgets`を呼び出して、`self.config.widgets`に基づいてウィジェットをダイアログ内に配置します。
:param master: tk.Frame: ダイアログのボディフレーム。
:returns: None
"""
frame, config = add_widgets(master, self.config.widgets, self.vars, self.config)
frame.pack(expand = True, fill = "both")
# vars(self.config).update(vars(config))
user_defined_vars = [attr for attr in dir(config) if not attr.startswith('__')]
for key in user_defined_vars:
setattr(self.config, key, getattr(config, key, None))
[ドキュメント]
def apply(self):
"""
ダイアログが閉じられる際に、ウィジェットの値を`vars`オブジェクトに適用します。
エントリーウィジェットから値を取得し、`vars`オブジェクトの対応する属性の型に合わせて変換し、設定します。
ダイアログが正常に適用されたことを示すために`self.config.ret`を`True`に設定します。
:returns: None
"""
for widget_list in self.config.widgets:
for inf in widget_list:
if inf["type"] == "entry":
varname = inf["varname"]
var = getattr(self.vars, varname, None)
widget = getattr(self.config, f"entry_{varname}", None)
if var is not None and widget:
val = pconv_by_type(widget.get(), type = type(var), defval = var, strict = False)
print(f"set {val} to {varname}")
setattr(self.vars, varname, val)
self.config.ret = True
[ドキュメント]
class tkWidgets(tkObject):
"""
Tkinter GUIの構築と管理のためのユーティリティクラスです。
Tkinterウィジェットの追加、フォント設定、ツールバー、ペイン、タブの管理、
および変数の表示と更新のためのメソッドを提供します。
内部的に`tkParams`オブジェクトを使用して設定と変数を管理します。
"""
def __init__(self, parent = None, plt = None, config = None, vars = None):
"""
tkWidgetsの新しいインスタンスを初期化します。
:param parent: tk.Widget, optional: 親ウィジェット。デフォルトはNone。
:param plt: object, optional: matplotlib.pyplotモジュールまたはFigureManagerオブジェクト。デフォルトはNone。
:param config: tkParams, optional: 設定を保持する`tkParams`オブジェクト。デフォルトは新しい`tkParams`インスタンス。
:param vars: tkParams, optional: 変数を保持する`tkParams`オブジェクト。デフォルトは新しい`tkParams`インスタンス。
"""
if config is None:
config = tkParams()
if vars is None:
vars = tkParams()
self.config = config
self.vars = vars
self.parent = parent
self.plt = plt
[ドキュメント]
def set_font(self, family = "Helvetica", size = 10, weight = "normal"):
"""
アプリケーション全体のデフォルトフォントを設定します。
:param family: str: フォントファミリー名(例: "Helvetica")。
:param size: int: フォントサイズ。
:param weight: str: フォントの太さ("normal"または"bold")。
:returns: None
"""
self.default_font = tk.font.Font(family = family, size = size, weight = weight)
self.parent.option_add("*Font", self.default_font)
[ドキュメント]
def add_toolbar(self, parent = None):
"""
ツールバー用のフレームを親ウィジェットに追加します。
:param parent: tk.Widget, optional: ツールバーが追加される親ウィジェット。デフォルトは`self.parent`。
:returns: tk.Frame: 作成されたツールバーフレーム。
"""
if parent is None: parent = self.parent
self.tool_bar = tk.Frame(parent)
self.tool_bar.pack(side = "top", anchor = "nw", expand = False, fill = "none")
return self.tool_bar
[ドキュメント]
def add_paned_window(self, parent = None, width = None, sashwidth = 2):
"""
左右2つのノートブックを含むペイン付きウィンドウを作成します。
:param parent: tk.Widget, optional: ペイン付きウィンドウが追加される親ウィジェット。デフォルトは`self.parent`。
:param width: int, optional: 左側のノートブックの幅。デフォルトはNone(自動調整)。
:param sashwidth: int, optional: サッシ(ペインの区切り線)の幅。デフォルトは2。
:returns: tuple:
- tk.PanedWindow: メインのペイン付きウィンドウ。
- ttk.Notebook: 左側のノートブック。
- ttk.Notebook: 右側のノートブック。
"""
if parent is None: parent = self.parent
pane = tk.PanedWindow(parent, sashwidth = sashwidth)
pane.pack(side = "top", expand = True, fill = "both")
left_notebook = tk.ttk.Notebook(pane, width = width)
# NOTE: この行の `tkinter.ttk.Notebook` は `tk.ttk.Notebook` のtypoですが、
# 既存のロジックは変更できないため、そのままにしています。
right_notebook = tkinter.ttk.Notebook(pane)
pane.add(left_notebook)#, width = left_pane_width)
pane.add(right_notebook)#, width = right_pane_width)
self.main_pane = pane
self.left_notebook = left_notebook
self.right_notebook = right_notebook
return self.main_pane, self.left_notebook, self.right_notebook
[ドキュメント]
def add_tab(self, parent = None):
"""
タブを管理するためのノートブックウィジェットを追加します。
:param parent: tk.Widget, optional: ノートブックが追加される親ウィジェット。デフォルトは`self.parent`。
:returns: ttk.Notebook: 作成されたノートブックウィジェット。
"""
if parent is None: parent = self.parent
notebook = tk.ttk.Notebook(parent)
# main_pane = tkinter.PanedWindow(root, sashwidth = sashwidth)
notebook.pack(side = "top", expand = True, fill = "both")
self.notebook = notebook
return self.notebook
[ドキュメント]
def add_page(self, notebook = None, title = "main", bg = 'dim gray'):
"""
指定されたノートブックに新しいタブ(ページ)を追加します。
:param notebook: ttk.Notebook, optional: ページを追加するノートブックウィジェット。デフォルトは`self.notebook`。
:param title: str, optional: ページのタイトル。デフォルトは"main"。
:param bg: str, optional: ページの背景色。デフォルトは'dim gray'。
:returns: tk.Frame: 作成されたページフレーム。
"""
if notebook is None: notebook = self.notebook
page_frame = tk.Frame(notebook, bg = bg)
page_frame.pack()
notebook.add(page_frame, text = title)
return page_frame
[ドキュメント]
def get_window_from_plt(self, plt):
"""
matplotlibのFigureManagerからTkinterウィンドウオブジェクトを取得し、内部的にpltを設定します。
:param plt: object: matplotlib.pyplotモジュールまたはFigureManagerオブジェクト。
:returns: tk.Tk または tk.Toplevel: 関連するTkinterウィンドウオブジェクト。
"""
self.plt = plt
return plt.get_current_fig_manager().window
[ドキュメント]
def add_widgets(self, parent, widgets, vars = None, config = None, **kwargs):
"""
ユーティリティ関数`add_widgets`を呼び出して、指定された定義に基づいてTkinterウィジェットを親フレームに追加します。
:param parent: tk.Widget: ウィジェットが追加される親Tkinterウィジェット。
:param widgets: list[list[dict]]: ウィジェットの定義を含む辞書の多次元リスト。
:param vars: tkParams, optional: ウィジェットの初期値を提供するオブジェクト。デフォルトは`self.vars`。
:param config: tkParams, optional: 作成されたウィジェットオブジェクトと関連するTkinter Variableを格納する`tkParams`オブジェクト。
デフォルトは`self.config`。
:param **kwargs: dict: `top_frame.pack()`に渡される追加のキーワード引数。
:returns: tuple:
- tk.Frame: 全てのウィジェットを格納する最上位フレーム。
- tkParams: 作成されたウィジェットと関連するTkinter Variableを含む設定オブジェクト。
"""
if config is None:
config = self.config
if vars is None:
vars = self.vars
return add_widgets(parent, widgets, vars = vars, config = config, **kwargs)
[ドキュメント]
def show_varibles(self, vars, heading = None):
"""
指定されたオブジェクトの非callableかつ非リスト/タプルのメンバー変数を表示します。
:param vars: object: メンバー変数を表示するオブジェクト。
:param heading: str, optional: 表示される変数の前に出力されるヘッディング文字列。デフォルトはNone。
:returns: None
"""
if heading:
print(heading)
return show_varibles(vars)
[ドキュメント]
def update_variables(self, config = None, vars = None):
"""
ユーティリティ関数`update_variables`を呼び出して、Tkinterの`Variable`オブジェクトから値を取得し、別の設定オブジェクトに更新します。
:param config: tkParams, optional: `var_`プレフィックスを持つTkinter Variableインスタンスを保持する設定オブジェクト。
デフォルトは`self.config`。
:param vars: tkParams, optional: 更新される値を保持するターゲットオブジェクト。デフォルトは`self.vars`。
:returns: None
"""
if config is None:
config = self.config
if vars is None:
vars = self.vars
return update_variables(config, vars)