XRF.XRF_fit のソースコード

"""
XRFデータフィッティングおよび解析アプリケーション。

このモジュールは、X線蛍光分析(XRF)データを読み込み、プロットし、
フィッティングやピーク探索などの解析機能を提供するGUIアプリケーションです。
データ入力にはプラグイン機構を採用しており、様々な形式のファイルに対応可能です。

:doc:`XRF_fit_usage`
"""
import os
import sys
import re
import numpy as np
from numpy import exp, log, log10, sqrt, sin, cos, tan
from importlib import import_module
from matplotlib import pyplot as plt
import tkinter as tk


from tklib.tkapplication import tkApplication
from tklib.tkutils import getarg, getintarg, getfloatarg, pint, pfloat, split_file_path, replace_path, print_line
from tklib.tkfile import tkFile
from tklib.tkparams import tkParams
from tklib.tkvariousdata import tkVariousData
from tklib.tkfilter import tkFilter
from tklib.tksci.tksci import Gaussian, Lorentzian, GaussLorentz
from tklib.tksci.tkmatrix import make_matrix1, make_matrix2, make_matrix3
from tklib.tkgraphic.tkplotevent import tkPlotEvent, RangeSelector
from tklib.tkgraphic.tkplot_pyplot import select_plt, tkPlot_pyplot
from tklib.tkgraphic.tkplot_tkinter import tkPlot_tkinter
from tklib.tkgui.tksimple_gui import tkWidgets, CustomDialog_with_config
#from tklib.tkgui.tksimple_gui import get_window_from_plt, add_widgets, update_variables, show_varibles, CustomDialog_with_config


ProgramName = "XRF fit"


#===================================
# Global variables
#===================================
markers = ['o', 's', '+', 'x', 'D', 'v', '^', '<', '>', '8', 'h', 'H']
colors  = ['black', 'red', 'blue', 'darkgreen', 'darkorange', 'hotpink', 'lightgreen', 'cyan', 'yellow', 'magenta', 'chocolate', 
           'navy', 'slategray', 'olive' ]

figsize         = (8, 6)
fontsize        = 12
legend_fontsize = 8


#=============================
# Treat argments
#=============================
[ドキュメント] def usage(app = None, cparams = None): """ アプリケーションの利用方法を表示します。 `app` オブジェクトに設定されている利用方法文字列をコンソールに出力します。 :param app: tkApplicationのインスタンス。 :type app: tkApplication :param cparams: 設定パラメータを含むtkParamsのインスタンス。 :type cparams: tkParams :returns: なし """ cparams = app.get_params() for s in app.usage_str.split('\n'): cmd = 'print({})'.format(s.rstrip()) eval(cmd)
[ドキュメント] def initialize(app = None): """ アプリケーションの初期設定と引数定義を行います。 `tkParams` オブジェクトを生成し、デバッグレベル、プリントレベル、 デフォルトの入力ファイル名、およびコマンドライン引数を定義します。 引数には、モード、入力プラグインディレクトリ、入力プラグイン名、入力ファイルパスが含まれます。 :param app: tkApplicationのインスタンス。 :type app: tkApplication :returns: 初期化された設定オブジェクト。 :rtype: tkParams """ argv, narg = app.get_argv() app.cfg = tkParams() cfg = app.cfg cfg.debug = 0 cfg.print_level = 0 infile = "KI240409-1-IGZO.txt" app.add_argument(opt = "--mode", type = "str", desc = 'mode=[plot_all|peak_search]', defval = 'plot_all', optional = True) def_plugin_dir = os.path.join(os.environ["tkprog_X_path"], 'plugin', "filter") app.add_argument(opt = "--input_plugin_dir", type = "str", var_name = 'input_plugin_dir', opt_str = "--input_plugin_dir=dir", desc = 'input_plugin_dir', defval = def_plugin_dir, optional = False) app.add_argument(opt = "--input_plugin_name", type = "str", var_name = 'input_plugin_name', opt_str = "--input_plugin_name=file_body", desc = 'input_plugin_name', defval = 'xlsx2xlsx', optional = False) app.add_argument(opt = "--infile", type = "str", var_name = 'infile', opt_str = "--infile=path", desc = 'input file path', defval = infile, optional = True) return cfg
[ドキュメント] def update_vars(app = None, cparams = None): """ コマンドライン引数をパースし、設定変数を更新します。 `app.read_args` を呼び出し、引数のチェックとデフォルト値の適用を行います。 引数解析中にエラーが発生した場合は、エラーメッセージを表示してアプリケーションを終了します。 :param app: tkApplicationのインスタンス。 :type app: tkApplication :param cparams: 更新対象の設定パラメータを含むtkParamsのインスタンス。 :type cparams: tkParams :returns: なし """ args_opt, args_idx, args_vars = app.read_args(vars = cparams, check_allowed_args = True, apply_default = 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)
[ドキュメント] def read_file(path, app, cparams, modules, module_names): """ 指定されたパスのファイルを読み込むための適切なモジュールを特定し、データを読み込みます。 提供されたモジュールリストを巡回し、各モジュールがファイルのタイプを チェックして、適合するモジュールを見つけます。 その後、そのモジュールを使用してデータを読み込みます。 :param path: 読み込むファイルのパス。 :type path: str :param app: tkApplicationのインスタンス。 :type app: tkApplication :param cparams: 設定パラメータを含むtkParamsのインスタンス。 :type cparams: tkParams :param modules: 読み込みモジュールのリスト。 :type modules: list :param module_names: 読み込みモジュール名のリスト。 :type module_names: list :returns: 読み込みに使用されたモジュール名、モジュールオブジェクト、および読み込まれたデータ。 適切なモジュールが見つからない場合、(None, None, None) を返します。 :rtype: tuple[str | None, object | None, dict | None] """ module = None module_name = "" for i in range(len(modules)): name = module_names[i] m = modules[i] file_type = m.check_file_type(path, app = app, cparams = cparams) print(f"try [{name}] for [{path}]: file_type={file_type}") if file_type is not None and 'Error' not in file_type: module = m module_name = name break if module is None: return None, None, None inf = module.read_data(path, app = app, cparams = cparams) return module_name, module, inf
[ドキュメント] def load_input_modules(app, cparams): """ 入力データの読み込みモジュールをロードし、入力ファイルを読み込みます。 `cparams.input_plugin_name` の設定に応じて、自動判別または指定された プラグインディレクトリからモジュールをロードします。 ロード後、`cparams.infile` で指定された入力ファイルを読み込み、データを取得します。 :param app: tkApplicationのインスタンス。 :type app: tkApplication :param cparams: 設定パラメータを含むtkParamsのインスタンス。 :type cparams: tkParams :returns: 読み込まれた入力ファイルの情報。辞書形式で、"meta", "labels", "data_list" などを含む。 :rtype: dict """ if cparams.input_plugin_name == '': cparams.plugin_dir = os.path.join(os.environ['tkprog_X_path'], 'XRF', 'filter') print() print(f"Load modules from [{cparams.plugin_dir}] (auto judge)") module_names, modules = app.load_modules(cparams.plugin_dir, "*.py", target = "read_data", is_print = True) for m in modules: input_type = m.get_input_type(app = app, cparams = cparams) print(f" {m.name}: input_type={input_type}") module_name, module_input, inf_in = read_file(cparams.infile, app, cparams, modules, module_names) # print("module_name=", module_name) if module_name is None or len(module_name) == 0: app.terminate(f"Error in extract_data.extract(): Can not find a module for [{cparams.infile}]", pause = True) cparams.in_module_names = [module_name] cparams.in_modules = [module_input] else: print("") print(f"Load input module from {cparams.input_plugin_dir}/{cparams.input_plugin_name}.py") filter_in = tkFilter(app = app, cparams = cparams, plugin_dir = cparams.input_plugin_dir, module_file = f"{cparams.input_plugin_name}.py") cparams.in_module_names, cparams.in_modules = filter_in.load(target = "read_data", is_print = True) print("cparams.in_module_names=", cparams.in_module_names) if len(cparams.in_module_names) == 0: app.terminate(f"\nError in extract(): Cannot load [{cparams.input_plugin_dir}/{cparams.input_plugin_name}.py]", pause = True) print("Read input files:", cparams.infile) inf_in = filter_in.read_data(0, cparams.infile, app = app, cparams = cparams, is_print = True) return inf_in
[ドキュメント] def plot(app, cparams, tkplt, fig, axes, inf_in): """ 入力データ (`inf_in`) をMatplotlibとTkinterの組み合わせでプロットします。 `inf_in` からメタデータ、ラベル、データリストを取り出し、 各スペクトルをサブプロットに描画します。 軸ラベル、凡例なども設定し、グラフを視覚化します。 :param app: tkApplicationのインスタンス。 :type app: tkApplication :param cparams: 設定パラメータを含むtkParamsのインスタンス。 :type cparams: tkParams :param tkplt: tkPlot_pyplotまたはtkPlot_tkinterのインスタンス。プロット操作を提供します。 :type tkplt: tkPlot_pyplot | tkPlot_tkinter :param fig: MatplotlibのFigureオブジェクト。 :type fig: matplotlib.figure.Figure :param axes: MatplotlibのAxesオブジェクト、またはAxesオブジェクトの配列。 :type axes: matplotlib.axes.Axes | np.ndarray :param inf_in: 読み込まれた入力ファイルの情報。辞書形式で、"meta", "labels", "data_list" などを含む。 :type inf_in: dict :returns: なし """ inf_list = inf_in["meta"] # sample = inf_in.get("sample_name", "") labels = inf_in["labels"] data_list = inf_in["data_list"] nspectrum = len(data_list) ndata = len(data_list[0]) # axesが単一のAxesオブジェクトの場合、リストに変換してループ処理に対応させる if nspectrum == 1 and not isinstance(axes, (list, np.ndarray)): axes = [axes] for isp in range(nspectrum): ax = axes[isp] subinf = inf_list[isp] x_list, y_list = data_list[isp] xmin = x_list[0] xmax = x_list[-1] print(f" {subinf['range']}: {subinf['name']} 2Q=[{xmin}, {xmax}]: wl={subinf['_WL']} A Xtal={subinf['_Xtal']}") # ax.set_title(f"{cparams.infile}") ax.tick_params(labelsize = fontsize) ax.plot(x_list, y_list, label = subinf['name']) ax.set_xlabel(r'2$\theta$', fontsize = fontsize) if isp == 0: ax.set_ylabel('Intensity', fontsize = fontsize) legend = ax.legend(fontsize = legend_fontsize) legend.set_draggable(True) tkplt.tight_layout()
# tkinter.mainloop()
[ドキュメント] def plot_all(app, cparams): """ 全てのスペクトルデータをロードし、Tkinter GUIと連携してプロットを表示します。 入力モジュールをロードし、`matplotlib` と `tkinter` を使用してグラフウィンドウを作成します。 ツールバーボタン(図の保存、再描画)、GUIウィジェット(エントリー、ボタン、リストボックスなど) を追加し、データをプロットします。ユーザー操作に応じたコールバック関数も定義します。 :param app: tkApplicationのインスタンス。 :type app: tkApplication :param cparams: 設定パラメータを含むtkParamsのインスタンス。 :type cparams: tkParams :returns: なし """ print() print(f"input_plugin: {cparams.input_plugin_dir}/{cparams.input_plugin_name}") print(f"input file : {cparams.infile}") inf_in = load_input_modules(app, cparams) data_list = inf_in["data_list"] nspectrum = len(data_list) print() print("plot") tkplt, root, tkpyplot = select_plt(use_tkinter = 1, plt = plt, parent = None, title = "XRF fit") fig, axes = tkplt.subplots(1, nspectrum, figsize = figsize, dpi = 100, tight_layout = False) plot_event = tkPlotEvent(plt) # plot_event.add_stop_button() # plot_event.prepare_annotation() # plot_event.prepare_popup_menu(fig, parent = root) tkplt.create_window(fig) tkplt.add_toolbar() wg = tkWidgets(parent = tkplt.parent, plt = plt) notebook = wg.add_tab() page1_frame = wg.add_page(notebook, title = "main") page2_frame = wg.add_page(notebook, title = "configure") top_frame = tk.Frame(page1_frame) #tkplt.parent) top_frame.pack(side = tk.TOP, expand = True, fill = "both") left_frame = tk.Frame(top_frame) left_frame.pack(side = tk.LEFT, anchor = "n", expand = True, fill = "x") canvas_frame = tk.Frame(top_frame) canvas = tkplt.add_canvas(fig, parent = canvas_frame) canvas_frame.pack(side = tk.LEFT, expand = True, fill = "both") def save_figure(fig, outfile = "figure.png"): """ 現在の図をファイルに保存します。 :param fig: 保存するMatplotlibのFigureオブジェクト。 :type fig: matplotlib.figure.Figure :param outfile: 保存するファイル名。デフォルトは"figure.png"。 :type outfile: str :returns: なし """ print() print(f"Save figure to [{outfile}]") fig.savefig(outfile) def redraw(): """ グラフを再描画します。 Matplotlibの図とTkinterキャンバスを更新して、グラフを再描画します。 :returns: なし """ # self.ax.clear() # 既存のグラフをクリア tkplt.draw() canvas.draw() tkplt.add_toolbar_button(text = "Save figure", command = lambda: save_figure(fig)) tkplt.add_toolbar_button(text = "Redraw", command = redraw) wg.vars.update(Vg_min = 0, Vg_max = 10, Vd_min = 0, Vd_max = 20) def on_click(): """ GUIウィジェットのクリックイベントハンドラです。 ウィジェットの値を更新し、デバッグ目的でコンソールに出力します。 :returns: なし """ # show_varibles(config) wg.update_variables() print("clicked", wg.vars.Vg_min, wg.vars.Vg_max, wg.vars.Vd_min, wg.vars.Vd_max) # button1 = tk.Button(left_frame, text = "test")#, command = command) widgets = [ # [{"type": "widget", "widget": button1}], [{"type": "label", "label": "Vg:", "width": None}, {"type": "entry","varname": "Vg_min", "vartype": "float", "width": 10, "expand": True, "fill": "x"}, {"type": "label", "label": "-", "width": None}, {"type": "entry","varname": "Vg_max", "vartype": "float", "width": 10, "expand": True, "fill": "x"}, {"type": "label", "label": " ", "width": None}, ], [{"type": "label", "label": "Vd:", "width": None}, {"type": "entry","varname": "Vd_min", "vartype": "float", "width": 10, "expand": True, "fill": "x"}, {"type": "label", "label": "-", "width": None}, {"type": "entry","varname": "Vd_max", "vartype": "float", "width": 10, "expand": True, "fill": "x"}, {"type": "label", "label": " ", "width": None}, ], [{"type": "button", "name": "smoothing", "text": "smoothing", "anchor": "w", "command": on_click}], [{"type": "button", "name": "peak_search", "text": "peak search", "anchor": "w", "command": on_click}], [{"type": "listbox", "name": "test", "text": "data", "anchor": "w", "options": ["all", "b"], "sel": 0, "width": 20, "height": 5, "command": on_click}], [{"type": "combobox", "name": "test", "text": "data", "anchor": "w", "options": ["all", "b"], "sel": 0, "width": 20, "height": 5, "command": on_click}], [{"type": "checkbox", "varname": "cb", "text": "data", "anchor": "w", "value": True}], [{"type": "radiobutton", "varname": "rb", "anchor": "w", "options": ["all", "b"], "sel": 0}], [{"type": "label", "varname": "input_path", "label": "input"}], [{"type": "path", "varname": "input_path", "label": "input", "value": "***", "width": 30, "title": "Choose input file", "command": None}], ] widgets_frame, config_left_pain = wg.add_widgets(parent = left_frame, widgets = widgets, side = "top", expand = True, fill = "x") plot(app, cparams, tkplt, fig, axes, inf_in) tkplt.pause(0.1) input("Press ENTER to terminate>>")
[ドキュメント] def main(): """ アプリケーションのエントリポイントです。 `tkApplication` を初期化し、設定をロードします。 コマンドライン引数から入力ファイルパスを特定し、ログファイルを設定します。 設定されたモード(例: `plot_all`)に基づいて適切な関数を呼び出します。 無効なモードが指定された場合、エラーメッセージを表示して終了します。 :returns: なし """ app = tkApplication() cfg = initialize(app) #--infile引数をチェックし、読み込みファイル名を最初に確定する cfg.infile = app.check_arg('--infile', defval = None, vartype = 'str') #cfg.infileからログファイル名を作り、console出力をredirectする logfile = app.replace_path(None, template = ["{dirname}", "{filebody}-out.txt"]) logfile = app.replace_path(cfg.infile) # logfile = app.replace_path(cparams.infile, template = ["{dirname}", "{filebody}-out.txt"]) app.redirect(heading = f"Open logfile [{logfile}]", targets = ["stdout", logfile], mode = 'w') app.print_title(f"# {ProgramName}") #起動時引数で与えられたパラメータをcfgに設定 app.update_vars(cfg,apply_default = True) # cfg.modeで与えられた関数が定義されていれば、呼び出す if cfg.mode in globals(): globals()[cfg.mode](app, cfg) else: app.terminate("Error in main: Invalide mode [{}]".format(cfg.mode), usage = app.usage, pause = True)
# app.terminate(pause = True)#usage = usage) if __name__ == "__main__": main()