Fletを使った簡易な計測GUIのひな型(シミュレーション)を作ったので、覚えとして記載します。
仕様
- ある値を徐々に変えて、その応答を測定する
(今回の例では、分光器を制御して試料に当てる光のエネルギーを変えていき、その時に試料から放出される電子を測定する。光電子収量(Photonelectron Yield Spectroscopy: PYS)と呼ばれる測定) - リアルタイム(100ms程度)でグラフを更新する
- 途中で測定をキャンセルできるようにする
開発環境
- windows 11 pro
- miniconda
(minicondaで最低限のファイルと仮想環境を作り、それ以外はすべてpipでインストール) - Python=3.10
- Flet (GUIとして)
- 測定によって、pyserial, pyvisa, nidaqmxなどをインストール
コード
import time
import datetime
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
matplotlib.use("svg")
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import flet as ft
from flet.matplotlib_chart import MatplotlibChart
# -----
def ramp_func(start,end,step):
# 測定点を作る配列
s_d = len(str(step).split('.')[1]) # 小数点以下の桁数を調べる
ramp_ = np.round(np.arange(start,end+step,step),s_d) # 数の丸めを行う。
ramp_= ramp_[np.where(ramp_ <= end)]
# print(ramp_)
return ramp_
# -----
# global変数(途中で測定を止めるために利用する)
flag= False # False:続行、True:停止とする
def main(page: ft.Page):
out_file_name = ft.Ref[ft.Text]()
fig, ax = plt.subplots()
ui_rows = []
# callback
def goto_clicked(e):
ct_ene.value = float(move_ene.value)
time.sleep(0.5)
page.update()
def current_clicked(e):
ct_ene.value = float(move_ene.value)
page.update()
def start_clicked(e):
measure_plot(fig, ax )
def abort_clicked(e):
# For loopを途中で止めるため
global flag
flag = True
# GUI 設定
title_txt = ft.Text("PYS Measurement System",size=20)
start_ene = ft.TextField(label="Start[eV]", value="4",width=100)
end_ene = ft.TextField(label="End[eV]", value="6.2",width=100)
step = ft.TextField(label="Step[eV]", value="0.05",width=100)
move_ene = ft.TextField(label="Move[eV]", value="4",width=100)
ct_ene = ft.TextField(label="Current[eV]", value="0",width=100)
out_file = ft.Text(ref=out_file_name)
goto_btn = ft.ElevatedButton("Goto", on_click=goto_clicked)
current_btn = ft.ElevatedButton("Current", on_click=current_clicked)
start_btn = ft.ElevatedButton("Start", on_click=start_clicked)
abort_btn = ft.ElevatedButton("Abort", on_click=abort_clicked)
ui_rows.append(ft.Row(controls=[title_txt]))
ui_rows.append(ft.Row(controls=[current_btn,ct_ene, goto_btn, move_ene]))
ui_rows.append(ft.Row(controls=[start_btn, abort_btn]))
ui_rows.append(ft.Row(controls=[start_ene, end_ene, step]))
ui_rows.append(ft.Row(controls=[out_file]))
fig_draw = MatplotlibChart(fig,expand=True)
ui_controls = ft.Column(controls=ui_rows)
page.add(ui_controls, fig_draw)
# 測定 Realtimeでグラフを更新する
def measure_plot(fig, ax):
global flag
flag = False
# スキャンするXの範囲
start_f = float(start_ene.value)
end_f = float(end_ene.value)
step_f = float(step.value)
xs = []
ys = []
for x in ramp_func(start_f,end_f,step_f):
ax.cla()
# ---測定する命令を書く
# 例えばエネルギーを設定する。
# 測定器から帰ってくる値をとる
x1 = x
y1 = x1**4 # デモとして4乗の値を返す
# ---
xs.append(x1)
ys.append(y1)
ax.plot(xs, ys,'ro-')
ax.set_xlabel('Energy[eV]')
ax.set_ylabel('Yield')
ax.grid()
fig_draw.update()
if flag:
break
time.sleep(0.5)
# data Save 測定が終わると自動でデータをSaveする
save_path_name = 'results'
Path(f'./{save_path_name}').mkdir(exist_ok=True)
now = datetime.datetime.now()
ext_file_name = f'./{save_path_name}/{out_file.value}_{now.strftime("%Y%m%d_%H%M%S")}'
ext_csv = ext_file_name + '.csv'
ext_png = ext_file_name + '.png'
df_ = pd.DataFrame({"x":xs,"y":ys})
df_.to_csv(ext_csv,index=False)
fig.savefig(ext_png)
# plt.close(fig)
if __name__ == '__main__':
ft.app(target=main)
# ft.app(target=main, view=ft.WEB_BROWSER)
測定画面と測定結果(シミュレーション)
測定をスタートする