"""
Launcher.py - アプリケーションランチャー
このスクリプトは、様々なアプリケーションやスクリプトを起動するためのGUIランチャーを提供します。
設定ファイル(.ini)からパスや設定を読み込み、ユーザーインターフェースを通じて簡単にプログラムを実行できるようにします。
また、Pythonpathの管理や環境変数の設定も行います。
関連リンク: :doc:`Launcher_usage`
"""
# For future implement: TkinterDnD2:
# https://juu7g.hatenablog.com/entry/Python/csv/viewer#%E3%83%89%E3%83%A9%E3%83%83%E3%82%B0%E3%82%A2%E3%83%B3%E3%83%89%E3%83%89%E3%83%AD%E3%83%83%E3%83%97%E3%81%AE%E5%AE%9F%E8%A3%85TkinterDnD2%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9
import os
import sys
v = os.environ.get('tklibPath', None)
if v:
pythonlib_path = v
else:
script_path = sys.argv[0]
tkProg_Root = os.path.join(script_path, '../../..')
pythonlib_path = os.path.abspath(os.path.join(tkProg_Root, 'tklib/python'))
#print(f"Add [{pythonlib_path}] to PYTHONPATH by sys.path.append()")
sys.path.append(pythonlib_path)
import tklib.tkimport as imp
tk = imp.import_lib("tkinter", stop_by_error = False)
imp.messages(stop_by_error = True)
#tkdnd = imp.import_lib("tkinterdnd2", stop_by_error = False)
#numba = imp.import_lib("numba", stop_by_error = False)
from sys import exit
import time
import platform
import glob
import re
from numpy import exp, log, log10, sin, cos, tan, arcsin, arccos, arctan, sqrt
#import tkinter as tk
import tkinter.font as font
from tklib.tkutils import getarg, getintarg, getfloatarg, pint, pfloat, terminate, desktop_name
from tklib.tkutils import get_os, get_last_directory, add_path, split_file_path, find_executable_path, search_executable_path, convert_by_vars
from tklib.tkutils import get_user_inifile_dir
from tklib.tkparams import tkParams
from tklib.tkfile import open_chardet
from tklib.tkgui.tkapplication_gui import tkApplication_GUI
import tklib.tkgui.tktkinter as tktkinter
from tkLauncherApp import tkLauncherApp
from tklib.tkscript_macro import tkScript
from tklib.tksci.tksci import h, h_bar, hbar, e, kB, NA, c, pi, pi2, torad, todeg, basee
from tklib.tksci.tksci import me, mp, mn, u0, e0, e2_4pie0, a0, R, F, g
from tklib.tksci.tksci import acos, asin, atan, cosh, sinh, tanh
from tklib.tksci.tksci import degcos, degsin, degtan, degacos, degasin, degatan
from tklib.tksci.tksci import eVTonm, nmToeV
from tklib.tksci.tksci import factorial, Factorize, Gaussian, Lorentzian, combination, gamma
from tklib.tkgui.tktooltip import tkTooltip
usage_str = "\n" \
+ "(i) usage: python Launcher.py [-s=script_list_name]\n" \
+ " ex: python Launcher.py -s={script_list_name}\n"
#=============================
# global variables
#=============================
prog_name = 'Launcher'
version = [1, 4, 0]
#=============================
# Treat argments
#=============================
[ドキュメント]
def usage(app):
"""
概要: アプリケーションの利用方法を表示します。
詳細説明: アプリケーションの `usage_str` を使用して、コマンドラインでの利用方法をコンソールに出力します。
:param app: tkApplication_GUI: アプリケーションのインスタンス。
"""
# cparams = app.get_params()
# app.usage(infile = cparams.infile)
for s in app.usage_str.split('\n'):
cmd = 'print({})'.format(s.rstrip())
eval(cmd)
[ドキュメント]
def initialize(app, config):
"""
概要: アプリケーションの初期設定とグローバル変数を設定します。
詳細説明: `tkLauncherApp` インスタンスの主要な設定(ウィンドウタイトル、ディレクトリパス、外部アプリケーションパス、GUIの表示設定など)を初期化します。
環境変数もここで設定されます。
:param app: tkLauncherApp: アプリケーションのインスタンス。
:param config: tkParams: 設定パラメータを格納するオブジェクト。
"""
#================================
# Global variables
#================================
cparams = app.get_params() # 計算エンジンのパラメータオブジェクト
tkvars = app.tkvars.get_param_dict()
app.window_title = f'{prog_name} {version}'
app.cur_sel = 0
app.last_menu = ''
app.last_ibutton = 0
app.last_button_type = ''
app.last_button_action = ''
# tkApplicationで定義済み
# app.user = getpass.getuser()
# app.user_nospace = self.user.replace(' ', '')
# app.argv = sys.argv
# app.script_path = sys.argv[0]
# app.script_fullpath = os.path.abspath(self.script_path)
# app.script_dir = dirname
# app.script_filebody = filebody
# app.ini_dir = self.get_inifile_dir(use_user_inifile = use_user_inifile,
# app.inifile = self.get_inifile_path(self.ini_dir)
# dirname, basename, filebody, ext = split_file_path(app.script_fullpath)
app.resource_dir = os.path.join(app.script_dir, 'resource')
app.extra_scripts = []
app.remove_scripts = []
app.env = os.environ
v = app.env.get('tkProg_Root', None)
if v:
app.tkProg_Root = v
else:
app.tkProg_Root = os.path.abspath(os.path.join(app.script_dir, '../..'))
os_name = platform.system()
if os_name == 'Linux':
app.use_tooltip = False
else:
app.use_tooltip = True
app.bin_path = os.path.join(app.tkProg_Root, 'bin')
app.tklib_Root = os.path.join(app.tkProg_Root, 'tklib')
app.pythonlib_path = pythonlib_path
app.perllib_path = os.path.join(app.tklib_Root, 'Perl', 'lib')
app.tkprog_path = os.path.join(app.tkProg_Root, 'tkprog')
app.tkprog_X_path = os.path.abspath(os.path.join(app.script_dir, '..'))
if os_name == 'Windows':
app.tkapp_path = os.path.join(app.tkProg_Root, 'tkapp_Win')
elif os_name == 'Darwin':
app.tkapp_path = os.path.join(app.tkProg_Root, 'tkapp_Mac')
else:
app.tkapp_path = os.path.join(app.tkProg_Root, 'tkapp_Linux')
app.tkapp_open_path = os.path.join(app.tkProg_Root, 'tkapp_open')
app.tkapp_etc_path = os.path.join(app.tkProg_Root, 'tkapp_etc')
app.launcher_script_dir = os.path.join(app.script_dir, 'scripts')
app.internal_editor_path = os.path.join(app.tkprog_X_path, 'editor/editor.py')
add_path(app.bin_path)
"""
print("")
print(f"OS: {os_name}")
print("")
print("Directories:")
print(" tkProg_Root :", app.tkProg_Root)
print(" tklib_Root :", app.tklib_Root)
print(" pythonlib_path :", app.pythonlib_path)
print(" perllib_path :", app.perllib_path)
print(" tkprog_X_path :", app.tkprog_X_path)
print(" internal_editor_path:", app.internal_editor_path)
print(" tkapp_path :", app.tkapp_path)
print(" tkapp_open_path :", app.tkapp_open_path)
print(" tkapp_etc_path :", app.tkapp_etc_path)
print(" tkprog_path :", app.tkprog_path)
print(" launcher_script_dir :", app.launcher_script_dir)
"""
config.start_path = config.get('start_path', '')
if config.start_path == '':
files = app.get_start_apps(filename_only = True)
if len(files) > 0:
config.start_path = files[0]
config.shell_path = config.get('shell_path', '')
if config.shell_path == '':
files = app.get_shells(filename_only = True)
if len(files) > 0:
config.shell_path = files[0]
config.filer_path = config.get('filer_path', '')
if config.filer_path == '':
files = app.get_filers(filename_only = True)
if len(files) > 0:
config.filer_path = files[0]
config.editor_path = config.get('editor_path', '')
if config.editor_path == '':
files = app.get_editors(filename_only = True)
if len(files) > 0:
config.editor_path = files[0]
config.python3_path = config.get('python3_path', '')
if config.python3_path == '':
config.python3_path = search_executable_path(['python3.exe', 'python3', 'python.exe', 'python'], filename_only = True)
config.python_path = config.python3_path
config.pythonw_path = config.get('pythonw_path', '')
if config.pythonw_path == '':
config.pythonw_path = search_executable_path(['pythonw.exe', 'pythonw'], filename_only = True)
config.perl_path = config.get('perl_path', '')
if config.perl_path == '':
config.perl_path = search_executable_path(['perl', 'perl.exe'], filename_only = True)
config.vesta_path = config.get('vesta_path', '')
if config.vesta_path == '':
dir = os.path.join(app.tkapp_path, 'VESTA')
if os.path.exists(dir):
config.vesta_path = os.path.join(dir, 'VESTA')
"""
print("")
print("External applications:")
print(" editor_path :", config.editor_path)
print(" python_path :", config.python_path)
print(" python3_path:", config.python3_path)
print(" pythonw_path:", config.pythonw_path)
print(" perl_path :", config.perl_path)
print(" vesta_path :", config.vesta_path)
"""
for key in ["tkProg_Root", "tklib_Root", "tklib_path", "pythonlib_path", "perllib_path", "tkprog_X_path", "internal_editor_path",
"tkapp_path", "tkapp_open_path", "tkapp_etc_path", "tkprog_path",
"script_dir", "script_path", "script_fullpath", "launcher_script_dir"]:
app.env[key] = app.get(key, '')
# Non-configurable configuration
# app.prm_file_type = [('prm file', '*.prm')]
# app.data_file_type = [('data file', '*.csv')]
# Configurable configuration
config = app.config
config.lang = 'en'
config.debug = False
config.print_level = 0
config.confirm_on_exit = False
app.show_log = False
config.show_log = app.show_log
config.minsize = [200, 200]
config.geometry = "470x580"
config.left_pane_width = 500
if os_name == 'Windows':
config.geometry = "470x580"
config.default_font_size = 14
config.font_size = 10
config.help_button_font_size = 8
config.path_frame_font_size = 8
config.width_listbox = 40
config.height_listbox = 15
elif os_name == 'Darwin':
config.geometry = "470x580"
config.default_font_size = 14
config.font_size = 10
config.help_button_font_size = 8
config.path_frame_font_size = 8
config.width_listbox = 40
config.height_listbox = 15
else:
config.geometry = "512x664"
config.default_font_size = 10
config.font_size = 8
config.help_button_font_size = 6
config.path_frame_font_size = 8
config.width_listbox = 40
config.height_listbox = 12
config.head_label_width = 10
config.width_input_editbox = 80
config.height_input_editbox = 15
config.message_box_width = 40
config.message_box_height = 4
config.width_rbutton = 10
# config.height_rbutton = 7
config.n_tbutton = 5
config.n_rbutton = 7
config.nx_button = 3
config.ny_button = 7
config.n_buttons = config.nx_button * config.ny_button
config.width_submenu_button = 18
# config.height_submenu_button = 3
config.width_help_button = 3
config.height_help_button = 1
# Define var types in config for auto type conversion in config.read_parameters()
config.editor_path = ''
# config.infile = ''
# config.outfile = ''
# config.score_mode = 'EI'
config.program_path = ''
[ドキュメント]
def update_vars(app, cparams):
"""
概要: コマンドライン引数を解析し、アプリケーションの変数を更新します。
詳細説明: `app.add_argument` を使用して期待されるコマンドライン引数を定義し、`app.read_args` で実際の引数を解析して `cparams` オブジェクトを更新します。
不正な引数があった場合はエラーメッセージを表示し終了します。
:param app: tkLauncherApp: アプリケーションのインスタンス。
:param cparams: tkParams: 計算エンジンのパラメータオブジェクト。
"""
app.add_argument(opt = "-s", type = "str", var_name = 'script_list_name', opt_str = "-s=script_list_name", desc = 'Script list file',
defval = "", optional = True);
# app.add_argument(opt = "-i", type = "float", var_name = 'idx', opt_str = "-i=integer", desc = 'Index to speify alpha',
# defval = 5, optional = False);
# app.add_argument(opt = None, type = "str", var_name = 'infile', opt_str = "infile", desc = 'Input file',
# defval = "input.txt", optional = False);
# app.add_argument(opt = None, type = "str", var_name = 'nmax', opt_str = "nmax", desc = 'Max number of interation',
# defval = 5, optional = False);
args_opt, args_idx, args_vars = app.read_args(vars = cparams.__dict__, check_allowed_args = True)
if args_opt is None:
error_no = args_idx
error_message = args_vars
app.terminate("\n\n"
+ "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
+ f"! {error_message}\n"
+ "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
usage = app.usage)
app.set_usage(usage_str)
"""
print("cparams.__dict__=", cparams.__dict__)
app.usage()
exit()
"""
# tkvars[parameterpath_varname].set(cparams.parameter_path)
[ドキュメント]
def copy_tkvars(app, cparams, tkvars):
"""
概要: Tkinter変数の値をアプリケーションのパラメータオブジェクトにコピーします。
詳細説明: 現在の実装では、この関数は何も処理せずreturnします。
将来的にTkinterウィジェットの値を `cparams` や `config` に反映させるためのプレースホルダです。
:param app: tkLauncherApp: アプリケーションのインスタンス。
:param cparams: tkParams: 計算エンジンのパラメータオブジェクト。
:param tkvars: dict: GUIウィジェットの変数辞書。
"""
config = app.configparams
return
for key in ["fontsize", "legend_fontsize"]:
cparams.set_attr(key, tkvars[key].get())
for key in ["fontsize", "legend_fontsize"]:
config.set_attr(key, tkvars[key].get())
cparams.set_attr('figsize', [tkvars['fig_width'].get(), tkvars['fig_height'].get()])
[ドキュメント]
def copy_config(app, cparams, config):
"""
概要: `config` オブジェクトのパラメータを `cparams` オブジェクトにコピーします。
詳細説明: `config` オブジェクトと `cparams` オブジェクトで共通するキーについて、`cparams` の値を `config` の値で上書きします。
:param app: tkLauncherApp: アプリケーションのインスタンス。
:param cparams: tkParams: 計算エンジンのパラメータオブジェクト。
:param config: tkParams: 設定パラメータを格納するオブジェクト。
"""
keys0 = config.__dict__.keys()
for key in cparams.__dict__.keys():
if key in keys0:
cparams.set_attr(key, cparams.get(key, None))
[ドキュメント]
def listbox_dbl_clicked(app, cparams, event):
"""
概要: リストボックス項目がダブルクリックされた際のイベントハンドラです。
詳細説明: 現在の実装では、この関数は何も処理しません。
:param app: tkLauncherApp: アプリケーションのインスタンス。
:param cparams: tkParams: 計算エンジンのパラメータオブジェクト。
:param event: tk.Event: イベントオブジェクト。
"""
ctkvars = app.tkvars
tkvars = ctkvars.get_param_dict()
# print("double clicked")
# tkvars["rbuttons"][i]["text"] = app.p(caption)
[ドキュメント]
def listbox_selected(app, cparams, event):
"""
概要: メインメニューリストボックスの項目が選択された際のイベントハンドラです。
詳細説明: 選択されたメインメニューに対応するサブメニューボタンのキャプションと状態(有効/無効)を更新します。
また、ツールチップも設定します。スクリプトエンジンに 'select' アクションを通知します。
:param app: tkLauncherApp: アプリケーションのインスタンス。
:param cparams: tkParams: 計算エンジンのパラメータオブジェクト。
:param event: tk.Event: イベントオブジェクト。
"""
# print("selected")
# print("e=", type(event), event, event.widget)
tkvars = app.tkvars.get_param_dict()
sel = tkvars["menu_listbox"].curselection()
# print("343 sel=", sel)
if len(sel) == 0:
return
app.cur_sel = sel[0]
# print("348 cur_sel=", app.cur_sel)
menu = tkvars["menu_listbox"].get(sel)
nbutton = len(tkvars["submenu_buttons"])
os_name = platform.system()
if app.use_tooltip:
for idx in range(nbutton):
button = tkvars["submenu_buttons"][idx]
tip = tkTooltip(widget = button, text = f'button {idx+1}', bg = '#dcfbfe', fg = '#0000ff')
app.s_engine.execute(app, app.get_params(), menu, idx = None, action = 'select', i_mouse_button = 1, max_num = 1, event = event)
submenus = app.s_engine.get_submenus(app, menu)
for i in range(len(tkvars["submenu_buttons"])):
tkvars["submenu_buttons"][i]["state"] = tk.DISABLED
tkvars["submenu_buttons"][i]["text"] = ''
tkvars["help_buttons"][i]["state"] = tk.DISABLED
tkvars["help_buttons"][i].set_text('x')
# tkvars["help_buttons"][i]["text"] = 'x'
# print("submenus=", submenus)
for m in submenus:
idx = m['idx']
task = m['task']
caption = m['caption']
if task == '':
# print(f"button {idx}={menu}")
try:
button = tkvars["submenu_buttons"][idx-1]
except:
print("")
print(f"Error in Launcher.listbox_selected(): Menu idx {idx} for {menu} exceeds [{app.config.n_buttons}]")
continue
button.set_text(caption)
# button.configure(text = app.p(caption))
# button["text"] = caption
button["state"] = tk.NORMAL
elif task == 'help':
button = tkvars["help_buttons"][idx-1]
button.set_text('?')
# button["text"] = '?'
button["state"] = tk.NORMAL
# button_type: menu, help
# action: 1, 2, d1 for left button, right button, double click
[ドキュメント]
def create_frame(app, config, cparams, parent_notebook):
"""
概要: ランチャーのメインGUIフレームを作成し、様々なウィジェットを配置します。
詳細説明: メニューリストボックス、右サイドバーボタン、ファイルパス入力フィールド、コマンドライン入力フィールド、
サブメニューボタン、メッセージボックスなどを含むフレームを作成し、親のノートブックに追加します。
:param app: tkLauncherApp: アプリケーションのインスタンス。
:param config: tkParams: 設定パラメータを格納するオブジェクト。
:param cparams: tkParams: 計算エンジンのパラメータオブジェクト。
:param parent_notebook: tk.ttk.Notebook: ウィジェットを追加する親のノートブックウィジェット。
:returns: tktkinter.Frame: 作成されたメインGUIフレーム。
"""
config = app.configparams
ctkvars = app.tkvars
tkvars = ctkvars.get_param_dict()
page1_frame = tktkinter.Frame(app, parent_notebook, bg = 'dim gray')
page1_frame.pack(fill = tk.BOTH, expand = True)
parent_notebook.add(page1_frame, text = app.p('Menu'))
notebook_frame = page1_frame
applications = app.get_external_apps(config.get("editor_path", None))
# Listbox
# https://www.shido.info/py/tkinter6.html
top_frame = tk.Frame(page1_frame, bg = 'white')
listbox_frame = tk.Frame(top_frame)
listbox = tktkinter.tkScrolledListbox(listbox_frame, width = config.width_listbox, height = config.height_listbox,
font = ("", config.font_size), borderwidth = 2, relief = "groove", cursor = 'arrow', fg = 'black',
selectmode = 'browse', takefocus = True, state = 'normal', justify = 'left')
listbox.frame.pack(side = tk.TOP, anchor = 'w', padx = 2, pady = 2)
# listbox.frame.pack(side = tk.TOP, anchor = 'w', padx = 2, pady = 2, fill = tk.BOTH, expand = True)
# listbox.bind("<<Button-1>>", listbox_selected)
# listbox.bind("<<Button-2>>", listbox_selected)
listbox.bind("<<ListboxSelect>>", lambda e: listbox_selected(app, cparams, e))
listbox.bind("<Double-Button-1>", lambda e: listbox_dbl_clicked(app, cparams, e))
tkvars["menu_listbox"] = listbox
listbox_frame.pack(side = tk.LEFT, padx = 2, pady = 2, fill = tk.BOTH, expand = True)
# Right buttons
rbutton_frame = tk.Frame(top_frame)
tkvars['rbuttons'] = []
for i in range(1, config.n_rbutton+1):
rbutton = tktkinter.Button(app, rbutton_frame, text = '', width = config.width_rbutton, font = ("", config.font_size))
rbutton.pack(side = tk.TOP, fill = tk.BOTH, expand = True)
tkvars['rbuttons'].append(rbutton)
def rbutton_clicked2(e, app, menu, i_mouse_button):
app.s_engine.execute(app, cparams, menu, idx = None, action = None, i_mouse_button = i_mouse_button, event = 3)
def rbutton_clicked(e, app, menu, i_mouse_button):
app.root_window.after(1,
lambda: rbutton_clicked2(e, app, menu, i_mouse_button))
def bind_rbutton(i):
b = tkvars['rbuttons'][i]
b.bind("<Button-1>", lambda e: rbutton_clicked(e, app, f'[RButton{i+1}]', '1'))
b.bind("<Button-2>", lambda e: rbutton_clicked(e, app, f'[RButton{i+1}]', '2'))
b.bind("<Button-3>", lambda e: rbutton_clicked(e, app, f'[RButton{i+1}]', '3'))
b.bind("<Double-Button-1>", lambda e: rbutton_clicked(e, app, f'[RButton{i+1}]', 'd'))
for i in range(config.n_rbutton):
bind_rbutton(i)
rbutton_frame.pack(side = tk.LEFT, anchor = 'nw', padx = 2, pady = 2, fill = tk.BOTH, expand = True)
top_frame.pack(side = tk.TOP, anchor = 'nw', padx = 2, pady = 2, fill = tk.BOTH, expand = True)
# Input path frame
def open_file(input_entry, output_entry = None, file_type = None, ini_dir = '.'):
tktkinter.path_button_click(app, input_entry,
file_type = file_type, ini_dir = ini_dir)
if output_entry:
header, ext = os.path.splitext(input_entry.get())
filebody = os.path.basename(header)
outfile = header + f'-predict.xlsx'
output_entry.set(outfile)
path_grid_frame = tk.Frame(page1_frame)
tktkinter.make_path_frame(app, cparams, page1_frame, path_grid_frame,
tkvars, varname = 'sel_file',
file_type = [("All", "*.*")],
working_dir = '.',
head_label_width = 6,
entry_width = 35,
button_width = 4,
edit_button_width = 0,
shell_button_width = 0,
optionmenu_width = 4,
open_command = lambda app, cparams, file_type, ini_dir: \
open_file(tkvars["sel_file"], None, file_type, ini_dir),
# post_open_command = lambda: post_open(tkvars["infile"], tkvars["outfile"]),
font_size = config.path_frame_font_size,
is_print = True,
igridrow = 0)
path_grid_frame = tk.Frame(page1_frame)
tktkinter.make_path_frame(app, cparams, page1_frame, path_grid_frame,
tkvars, varname = 'argument',
head_label_width = 5,
entry_width = 40,
button_width = 0,
edit_button_width = 0,
shell_button_width = 0,
optionmenu_width = 0,
font_size = config.path_frame_font_size,
igridrow = 1)
# Menu buttons
def add_buttons(parent, idx):
frame = tk.Frame(parent, bg = 'white')
button = tktkinter.Button(app, frame, text = '', #f'Button {c}',
width = config.width_submenu_button,
font = ("", config.font_size),
# command = lambda: analyze(app, cparams, tkvars)
)
# button.grid(row = iy, column = ix)
button.pack(side = tk.LEFT, anchor = 'w', padx = 0, pady = 0, fill = tk.X, expand = True)
hbutton = tktkinter.Button(app, frame, text = '?',
# width = config.width_help_button, height = config.height_help_button,
font = ("", config.help_button_font_size),
# command = lambda: analyze(app, cparams, tkvars),
)
hbutton.pack(side = tk.LEFT, anchor = 'nw', padx = 0, pady = 0)
frame.grid(row = iy, column = ix)
button.bind("<Button-1>", lambda e: button_clicked(app, cparams, idx, 'submenu', '1', event = e))
button.bind("<Button-2>", lambda e: button_clicked(app, cparams, idx, 'submenu', '2', event = e))
button.bind("<Button-3>", lambda e: button_clicked(app, cparams, idx, 'submenu', '3', event = e))
button.bind("<Double-Button-1>", lambda e: button_clicked(app, cparams, idx, 'submenu', 'd1', event = e))
hbutton.bind("<Button-1>", lambda e: button_clicked(app, cparams, idx, 'help', '1', event = e))
hbutton.bind("<Button-2>", lambda e: button_clicked(app, cparams, idx, 'help', '2', event = e))
hbutton.bind("<Button-3>", lambda e: button_clicked(app, cparams, idx, 'help', '3', event = e))
hbutton.bind("<Double-Button-1>", lambda e: button_clicked(app, cparams, idx, 'help', 'd1', event = e))
tkvars["submenu_buttons"].append(button)
tkvars["help_buttons"].append(hbutton)
return button, hbutton
button_grid_frame = tk.Frame(page1_frame, bg = 'white')
tkvars["submenu_buttons"] = []
tkvars["help_buttons"] = []
c = 0
for iy in range(config.ny_button):
for ix in range(config.nx_button):
button, hbutton = add_buttons(button_grid_frame, c)
c += 1
# button_grid_frame.grid_propagate(True)
button_grid_frame.pack(side = tk.TOP, anchor = 'w', padx = 2, pady = 2, fill = tk.X, expand = True)
# message box grid
message_grid_frame = tk.Frame(page1_frame)
tktkinter.make_path_frame(app, cparams, page1_frame, message_grid_frame,
tkvars, varname = 'command_line_org',
head_label_width = 10,
entry_width = config.message_box_width,
button_width = 0,
edit_button_width = 0,
shell_button_width = 0,
optionmenu_width = 0,
font_size = config.path_frame_font_size,
igridrow = 0)
def copy_to_clipboard(parent, entry_variable):
print(f"copy [{entry_variable.get()}] to clipboard")
parent.clipboard_clear()
parent.clipboard_append(entry_variable.get())
parent.update_idletasks()
tktkinter.make_path_frame(app, cparams, page1_frame, message_grid_frame,
tkvars, varname = 'command_line',
head_label_width = config.head_label_width,
entry_width = config.message_box_width,
button_width = 5,
button_args = {'text': 'run', "width": 4, 'font': ("", config.font_size),
'command': lambda: app.s_engine.execute_a_command(app, cparams, tkvars['command_line'].get())
},
edit_button_width = 0,
shell_button_width = 0,
optionmenu_width = 0,
copy_button_args = {'text': 'copy', "width": 4, 'font': ("", config.font_size),
# 'command': lambda: copy_to_clipboard(page1_frame, tkvars['command_line'].get()),
'igridcol': 4
},
font_size = config.path_frame_font_size,
igridrow = 1)
tktkinter.make_path_frame(app, cparams, page1_frame, message_grid_frame,
tkvars, varname = 'message',
head_label_width = config.head_label_width,
entry_width = config.message_box_width,
button_width = 0,
edit_button_width = 0,
shell_button_width = 0,
optionmenu_width = 0,
font_size = config.path_frame_font_size,
igridrow = 2)
initialize_launcher_menus(app, cparams)
set_tbutton_captions(app, cparams)
set_rbutton_captions(app, cparams)
os_name = platform.system()
if app.use_tooltip:
for idx in range(len(tkvars["tbuttons"])):
button = tkvars["tbuttons"][idx]
tkTooltip(widget = button, text = f'tbutton {idx+1}', bg = '#FFFF00', fg = '#0000FF')
for idx in range(len(tkvars["rbuttons"])):
button = tkvars["rbuttons"][idx]
tkTooltip(widget = button, text = f'rbutton {idx+1}', bg = '#FFFF00', fg = '#0000FF')
app.s_engine.execute(app, cparams, '[TButton].onload', idx = None, action = 'onload', i_mouse_button = None)
app.s_engine.execute(app, cparams, '[RButton].onload', idx = None, action = 'onload', i_mouse_button = None)
return notebook_frame
[ドキュメント]
def create_window(app, config, cparams):
"""
概要: アプリケーションのメインウィンドウとGUIコンポーネントを作成します。
詳細説明: メインウィンドウを作成し、メニューバー、ツールバー、およびメインフレームを配置します。
メインフレームには、ユーザーが操作する主要なウィジェットが含まれます。
:param app: tkLauncherApp: アプリケーションのインスタンス。
:param config: tkParams: 設定パラメータを格納するオブジェクト。
:param cparams: tkParams: 計算エンジンのパラメータオブジェクト。
"""
ctkvars = app.tkvars
tkvars = ctkvars.get_param_dict()
# iconfile = 'tkProg.ico'
iconfile = os.path.join(os.environ.get('tkprog_X_path', '../'), 'tkProg.ico')
os_name = platform.system()
# print("os_name=", os_name)
# print(f"Use icon file [{iconfile}]")
if os_name == 'Linux' or not os.path.isfile(iconfile):
root_window = app.create_window(title = app.window_title,
geometry = config.geometry, minsize = config.minsize,
default_font = ('FixedSys', config.default_font_size)) #None) #('FixedSys', 14))
else:
root_window = app.create_window(title = app.window_title, iconfile = iconfile,
geometry = config.geometry, minsize = config.minsize,
default_font = ('FixedSys', config.default_font_size)) #None) #('FixedSys', 14))
app.main_window = root_window
# menu_bar = create_menu(app, config, cparams, root_window)
tool_bar = create_toolbar(app, config, cparams, root_window)
# 2分割ウインドウを使う場合。いづれのpaneもnotebook型ウインドウが作られる
# panes = app.add_root_panes(root = root_window, type = "pane2", sashwidth = 2, left_pane_width = 600)
# main_pane, left_notebook, right_notebook = panes
# 1p分割ウインドウを使う場合。paneはnotebook型ウインドウが作られる
panes = app.add_root_panes(type = "pane1", sashwidth = 2)
left_notebook = panes[0]
# 左ペインのnotebookにタブを追加。2ページ目は開発者用タブ
ltabframe1 = create_frame(app, config, cparams, left_notebook)
ltabframe2 = app.create_development_tab(config, cparams, left_notebook, copy_tkvars = copy_tkvars)
ltabframe3 = app.create_editor_tab(config, cparams, left_notebook, title = 'Editor test',
entry_width = 35,
eval_button_args = { 'text': 'eval', 'width': 4, "command": None },
)
[ドキュメント]
def main():
"""
概要: アプリケーションのエントリポイントです。
詳細説明: アプリケーションの初期化、設定ファイルの読み込み、コマンドライン引数の解析、
GUIウィンドウの作成と表示、ログファイルの設定、および終了時の設定保存処理を行います。
`tkLauncherApp` インスタンスを作成し、設定パラメータとGUIウィジェット変数を管理します。
"""
#==================================================================
# Initialize parameters
#==================================================================
if platform.system() == 'Windows':
user_ini_dir = get_user_inifile_dir(env_vars = [], dir_names = ['launcher'])
else:
user_ini_dir = get_user_inifile_dir(env_vars = ['HOME'], dir_names = ['.launcher', 'launcher'])
app = tkLauncherApp(usage_str = usage_str, globals = globals(), locals = locals(),
use_user_inifile = True,
inifile_dirs = [user_ini_dir, '../../user', 'user'], create_inidir = False)
print(f"Configuration file: {app.config.inifile}")
# 設定パラメータオブジェクト
config = app.config
cparams = app.get_params() # 計算エンジンのパラメータオブジェクト
# GUIウィジェット変数オブジェクトと辞書型
ctkvars = app.tkvars
tkvars = ctkvars.get_param_dict()
mtime = time.localtime(os.path.getctime(app.script_path))
print("")
print( "################################################")
print(f"# {app.script_path} ver {version[0]}.{version[1]}.{version[2]}")
print(f"# Modified time: {mtime.tm_year}/{mtime.tm_mon}/{mtime.tm_mday} {mtime.tm_hour}:{mtime.tm_min}:{mtime.tm_sec}")
print( "################################################")
print(f"Initialize parameters")
initialize(app, config)
print("")
print(f"Read configration from [{config.inifile}]")
config.read_parameters(path = config.inifile)
if config.get('show_log', None):
app.show_log = config.show_log
print("")
print(f"Add _path variables in {config.inifile} to environment vars")
for key in config.__dict__.keys():
if '_path' in key:
val = config.get(key, '')
if val is not None:
app.print(f" config[{key}]={val} is added")
app.env[key] = val
print("")
print(f"Update parameters by command-line arguments")
update_vars(app, cparams)
config.n_buttons = config.nx_button * config.ny_button
#===========================================
# logファイルへ保存
#===========================================
logfiles = []
if user_ini_dir:
logfiles.append(os.path.join(user_ini_dir, "Launcher-out.txt"))
logfiles.append(app.replace_path())
logfiles.append("Launcher-out.txt")
config.logfile = None
for path in logfiles:
#ファイルが無いときもFalseが返るので、tryに置き換え
# if os.access(path, os.W_OK):
try:
fout = open(path, "a")
fout.close()
config.logfile = path
break
except:
path = os.path.join(user_ini_dir, "Launcher-out.txt")
try:
fout = open(path, "a")
fout.close()
config.logfile = path
break
except:
print(f"\nWarning in main(): Can not write log to [{path}]")
if config.logfile is None:
print(f"\nWarning in main(): Can not create log file. Output log will not be save")
# app.show_log = True
print(f"Open logfile [{config.logfile}]")
if app.show_log:
app.redict(targets = ["stdout", config.logfile], mode = 'w')
else:
app.redict(targets = [config.logfile], mode = 'w')
# python_pathとpython3_pathを強制的に一致させる
config.python_path = config.python3_path
# VSCodeのパスにスペースが入っていて問題が起こるので、実行ファイル名だけにする
if re.search(r'code(\.exe)?$', config.editor_path):
config.editor_path = 'code'
#===========================================
# 内部パラメータ
#===========================================
os_name = platform.system()
print("")
print(f"OS: {os_name}")
print("")
print("Directories:")
print(" tkProg_Root :", app.tkProg_Root)
print(" tklib_Root :", app.tklib_Root)
print(" pythonlib_path :", app.pythonlib_path)
print(" perllib_path :", app.perllib_path)
print(" tkprog_X_path :", app.tkprog_X_path)
print(" internal_editor_path:", app.internal_editor_path)
print(" tkapp_path :", app.tkapp_path)
print(" tkapp_open_path :", app.tkapp_open_path)
print(" tkapp_etc_path :", app.tkapp_etc_path)
print(" tkprog_path :", app.tkprog_path)
print(" launcher_script_dir :", app.launcher_script_dir)
print("")
print("External applications:")
print(" editor_path :", config.editor_path)
print(" python_path :", config.python_path)
print(" python3_path:", config.python3_path)
print(" pythonw_path:", config.pythonw_path)
print(" perl_path :", config.perl_path)
print(" vesta_path :", config.vesta_path)
# Group名を取得
dirname, basename, filebody, ext = split_file_path(app.script_fullpath) # dirname = ".. /Launcher"
# dirname, basename, filebody, ext = split_file_path(dirname) # dirname = "tkprog_X_path"
last_dirname = get_last_directory(dirname)
m = re.search(r"_(.*?)$", last_dirname)
if m:
app.group = m.groups()[0]
else:
app.group = ""
# group listファイルの有無を確認
if cparams.script_list_name == '':
if app.group != '':
filename = f'{app.group}.list'
else:
filename = 'default.list'
path = os.path.join(app.launcher_script_dir, '../../config/Launcher', filename)
if os.path.exists(path):
cparams.script_list_name = os.path.join('../config/Launcher', filename)
else:
cparams.script_list_name = "default"
print("")
print(f"Group : {app.group}")
print(f"Script list: {cparams.script_list_name}")
editor = config.get("editor_path", None)
if editor is None or editor == '':
editors = app.get_editors()
if len(editors) > 0:
config.editor_path = editors[0]
if config.editor_path is not None:
app.env["tkEDITOR_PATH"] = config.editor_path
def read_script_list(app, filebody):
if '.list' in filebody:
path = os.path.join(app.launcher_script_dir, '../', filebody)
path = os.path.abspath(path)
else:
path = os.path.join(app.launcher_script_dir, f'{filebody}.list')
cparams.script_list_path = path
print("")
app.print_warning(f"Read script lists from [{path}]")
app.extra_scripts = []
app.remove_scripts = []
fp = open_chardet(path, 'r', def_encoding = 'shift_jis')
# print("line", fp.readline())
try:
fp = open_chardet(path, 'r', def_encoding = 'shift_jis')
# fp = open(path, 'r')
except:
app.print_warning(f" Warning: Can not read [{path}]")
return None, None
section = ''
while 1:
line = fp.readline()
if not line:
break
if '[extra_scripts]' in line:
section = 'add'
continue
elif '[remove_scripts]' in line:
section = 'remove'
continue
line = line.strip()
if line == '':
continue
if line[0] in "#:;":
continue
if section == 'add':
path = os.path.abspath(os.path.join(app.launcher_script_dir, line))
app.extra_scripts.append(path)
elif section == 'remove':
app.remove_scripts.append(line)
return app.extra_scripts, app.remove_scripts
if cparams.script_list_name:
read_script_list(app, cparams.script_list_name)
# print("add=", app.extra_scripts)
# print("remove=", app.remove_scripts)
# exit()
app.resource_path = os.path.join(app.resource_dir, f'resource_{app.config.lang}.resource')
app.phrases = app.read_resource(app.resource_path, app.config.lang) #, app.phrases)
# app.write_resource("resource.txt", 'w')
create_window(app, config, cparams)
app.mainloop()
# ウィジェットの設定tkvarsをcparams,confgにコピー
copy_tkvars(app, cparams, tkvars)
copy_tkvars(app, config, tkvars)
# メインウインドウサイズをconfigにコピー
config.geometry = app.root_window_geometry
print("")
print(f"Save configuration to [{config.inifile}]")
# app.print_warning(f"main(): config:")
# config.print_parameters(use_warning = True)
ret = config.save_parameters(path = config.inifile, section = 'Configure', sort_by_keys = True, IsPrint = False)
if not ret:
app.print_warning(f"\nError: Can not write configuration to [{config.inifile}]\n")
# 必要があれば、入力ファイルに対応するパラメータファイルを保存
if tkvars.get("prmpath", None) is not None:
cparams.prmpath = tkvars["prmpath"].get() #os.path.splitext(cparams.path)[0] + '.prm'
if cparams.prmpath != '':
print(f"Save parameters to [{cparams.prmpath}]")
ret = cparams.save_parameters(cparams.prmpath, section = 'Parameters',
sort_by_keys = True, IsPrint = False, update_commandline = False)
if not ret:
app.terminate(f"Error in main(): Cannot write parameters to [{cparams.prmpath}]", usage = usage)
app.terminate(usage = app.usage())
if __name__ == "__main__":
main()