497 lines
21 KiB
Python
497 lines
21 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
探测器设置选项卡
|
|
"""
|
|
|
|
import customtkinter as ctk
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
|
import numpy as np
|
|
|
|
class DetectorConfigTab(ctk.CTkFrame):
|
|
def __init__(self, parent):
|
|
super().__init__(parent)
|
|
|
|
# 配置网格布局
|
|
self.grid_columnconfigure(0, weight=1)
|
|
self.grid_rowconfigure(0, weight=0)
|
|
self.grid_rowconfigure(1, weight=1)
|
|
|
|
# 探测器选择器
|
|
detector_frame = ctk.CTkFrame(self)
|
|
detector_frame.grid(row=0, column=0, sticky="nsew", pady=(0, 10))
|
|
|
|
self.detector_var = ctk.StringVar(value="detector1")
|
|
|
|
ctk.CTkRadioButton(
|
|
detector_frame,
|
|
text="探测器1",
|
|
variable=self.detector_var,
|
|
value="detector1",
|
|
command=self.on_detector_change
|
|
).pack(side="left", padx=20, pady=10)
|
|
ctk.CTkRadioButton(
|
|
detector_frame,
|
|
text="探测器2",
|
|
variable=self.detector_var,
|
|
value="detector2",
|
|
command=self.on_detector_change
|
|
).pack(side="left", padx=20, pady=10)
|
|
ctk.CTkRadioButton(
|
|
detector_frame,
|
|
text="探测器3",
|
|
variable=self.detector_var,
|
|
value="detector3",
|
|
command=self.on_detector_change
|
|
).pack(side="left", padx=20, pady=10)
|
|
|
|
# 探测器配置框架
|
|
self.config_frame = ctk.CTkFrame(self)
|
|
self.config_frame.grid(row=1, column=0, sticky="nsew")
|
|
|
|
# 配置探测器1的默认值
|
|
self.detector_configs = {
|
|
"detector1": {
|
|
"sample_space_size": 5000,
|
|
"allow_replacement": True,
|
|
"energy_distribution": {
|
|
"type": "normal",
|
|
"mean": 1000.0,
|
|
"std": 200.0
|
|
},
|
|
"timestamp_distribution": {
|
|
"type": "exponential",
|
|
"scale": 50.0
|
|
}
|
|
},
|
|
"detector2": {
|
|
"sample_space_size": 4000,
|
|
"allow_replacement": True,
|
|
"energy_distribution": {
|
|
"type": "normal",
|
|
"mean": 800.0,
|
|
"std": 150.0
|
|
},
|
|
"timestamp_distribution": {
|
|
"type": "uniform",
|
|
"low": 0.0,
|
|
"high": 200.0
|
|
}
|
|
},
|
|
"detector3": {
|
|
"sample_space_size": 6000,
|
|
"allow_replacement": True,
|
|
"energy_distribution": {
|
|
"type": "gamma",
|
|
"shape": 2.0,
|
|
"scale": 500.0
|
|
},
|
|
"timestamp_distribution": {
|
|
"type": "normal",
|
|
"mean": 100.0,
|
|
"std": 30.0
|
|
}
|
|
}
|
|
}
|
|
|
|
# 显示当前探测器配置
|
|
self.update_config_display()
|
|
|
|
def on_detector_change(self):
|
|
"""探测器切换时的回调"""
|
|
self.update_config_display()
|
|
|
|
def update_config_display(self):
|
|
"""更新配置显示"""
|
|
# 清空现有内容
|
|
for widget in self.config_frame.winfo_children():
|
|
widget.destroy()
|
|
|
|
detector = self.detector_var.get()
|
|
config = self.detector_configs[detector]
|
|
|
|
# 样本空间设置(横向排列)
|
|
sample_frame = ctk.CTkFrame(self.config_frame)
|
|
sample_frame.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=10)
|
|
|
|
ctk.CTkLabel(sample_frame, text="样本空间大小:").pack(side="left", padx=10, pady=5)
|
|
self.sample_space_var = ctk.IntVar(value=config["sample_space_size"])
|
|
ctk.CTkEntry(
|
|
sample_frame,
|
|
textvariable=self.sample_space_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
self.allow_replacement_var = ctk.BooleanVar(value=config["allow_replacement"])
|
|
ctk.CTkCheckBox(
|
|
sample_frame,
|
|
text="允许重复抽样",
|
|
variable=self.allow_replacement_var
|
|
).pack(side="left", padx=20, pady=5)
|
|
|
|
# 能量分布
|
|
ctk.CTkLabel(
|
|
self.config_frame,
|
|
text="能量分布",
|
|
font=ctk.CTkFont(size=12, weight="bold")
|
|
).grid(row=2, column=0, sticky="w", padx=20, pady=10)
|
|
|
|
# 分布类型
|
|
ctk.CTkLabel(self.config_frame, text="分布类型:").grid(row=3, column=0, sticky="w", padx=40, pady=5)
|
|
self.energy_dist_type_var = ctk.StringVar(value=config["energy_distribution"]["type"])
|
|
dist_type_frame = ctk.CTkFrame(self.config_frame)
|
|
dist_type_frame.grid(row=3, column=1, sticky="w", padx=10, pady=5)
|
|
|
|
ctk.CTkOptionMenu(
|
|
dist_type_frame,
|
|
values=["normal", "uniform", "exponential", "gamma", "poisson", "lognormal"],
|
|
variable=self.energy_dist_type_var,
|
|
command=self.on_energy_dist_type_change
|
|
).pack(side="left")
|
|
|
|
# 分布参数(根据类型动态显示)
|
|
self.energy_params_frame = ctk.CTkFrame(self.config_frame)
|
|
self.energy_params_frame.grid(row=4, column=0, columnspan=2, sticky="w", padx=40, pady=5)
|
|
self.update_energy_params()
|
|
|
|
|
|
|
|
# 时间戳分布
|
|
ctk.CTkLabel(
|
|
self.config_frame,
|
|
text="时间戳分布",
|
|
font=ctk.CTkFont(size=12, weight="bold")
|
|
).grid(row=6, column=0, sticky="w", padx=20, pady=10)
|
|
|
|
# 分布类型
|
|
ctk.CTkLabel(self.config_frame, text="分布类型:").grid(row=7, column=0, sticky="w", padx=40, pady=5)
|
|
self.timestamp_dist_type_var = ctk.StringVar(value=config["timestamp_distribution"]["type"])
|
|
dist_type_frame2 = ctk.CTkFrame(self.config_frame)
|
|
dist_type_frame2.grid(row=7, column=1, sticky="w", padx=10, pady=5)
|
|
|
|
ctk.CTkOptionMenu(
|
|
dist_type_frame2,
|
|
values=["normal", "uniform", "exponential", "gamma", "poisson", "lognormal"],
|
|
variable=self.timestamp_dist_type_var,
|
|
command=self.on_timestamp_dist_type_change
|
|
).pack(side="left")
|
|
|
|
# 分布参数(根据类型动态显示)
|
|
self.timestamp_params_frame = ctk.CTkFrame(self.config_frame)
|
|
self.timestamp_params_frame.grid(row=8, column=0, columnspan=2, sticky="w", padx=40, pady=5)
|
|
self.update_timestamp_params()
|
|
|
|
|
|
|
|
def on_energy_dist_type_change(self, value):
|
|
"""能量分布类型变化时的回调"""
|
|
self.update_energy_params()
|
|
|
|
def on_timestamp_dist_type_change(self, value):
|
|
"""时间戳分布类型变化时的回调"""
|
|
self.update_timestamp_params()
|
|
|
|
def update_energy_params(self):
|
|
"""更新能量分布参数"""
|
|
# 清空现有内容
|
|
for widget in self.energy_params_frame.winfo_children():
|
|
widget.destroy()
|
|
|
|
dist_type = self.energy_dist_type_var.get()
|
|
config = self.detector_configs[self.detector_var.get()]["energy_distribution"]
|
|
|
|
if dist_type == "normal":
|
|
# 均值和标准差
|
|
ctk.CTkLabel(self.energy_params_frame, text="均值:").pack(side="left", padx=10, pady=5)
|
|
self.energy_mean_var = ctk.DoubleVar(value=config.get("mean", 1000.0))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_mean_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
ctk.CTkLabel(self.energy_params_frame, text="标准差:").pack(side="left", padx=10, pady=5)
|
|
self.energy_std_var = ctk.DoubleVar(value=config.get("std", 200.0))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_std_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
elif dist_type == "uniform":
|
|
# 最小值和最大值
|
|
ctk.CTkLabel(self.energy_params_frame, text="最小值:").pack(side="left", padx=10, pady=5)
|
|
self.energy_low_var = ctk.DoubleVar(value=config.get("low", 0.0))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_low_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
ctk.CTkLabel(self.energy_params_frame, text="最大值:").pack(side="left", padx=10, pady=5)
|
|
self.energy_high_var = ctk.DoubleVar(value=config.get("high", 2000.0))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_high_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
elif dist_type in ["exponential", "gamma"]:
|
|
# 尺度参数
|
|
ctk.CTkLabel(self.energy_params_frame, text="尺度参数:").pack(side="left", padx=10, pady=5)
|
|
self.energy_scale_var = ctk.DoubleVar(value=config.get("scale", 50.0))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_scale_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
if dist_type == "gamma":
|
|
# 形状参数
|
|
ctk.CTkLabel(self.energy_params_frame, text="形状参数:").pack(side="left", padx=10, pady=5)
|
|
self.energy_shape_var = ctk.DoubleVar(value=config.get("shape", 2.0))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_shape_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
elif dist_type == "poisson":
|
|
# lambda参数
|
|
ctk.CTkLabel(self.energy_params_frame, text="lambda:").pack(side="left", padx=10, pady=5)
|
|
self.energy_lam_var = ctk.DoubleVar(value=config.get("lam", 1000.0))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_lam_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
elif dist_type == "lognormal":
|
|
# 均值和sigma
|
|
ctk.CTkLabel(self.energy_params_frame, text="均值:").pack(side="left", padx=10, pady=5)
|
|
self.energy_mean_var = ctk.DoubleVar(value=config.get("mean", 6.0))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_mean_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
ctk.CTkLabel(self.energy_params_frame, text="sigma:").pack(side="left", padx=10, pady=5)
|
|
self.energy_sigma_var = ctk.DoubleVar(value=config.get("sigma", 0.5))
|
|
ctk.CTkEntry(
|
|
self.energy_params_frame,
|
|
textvariable=self.energy_sigma_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
def update_timestamp_params(self):
|
|
"""更新时间戳分布参数"""
|
|
# 清空现有内容
|
|
for widget in self.timestamp_params_frame.winfo_children():
|
|
widget.destroy()
|
|
|
|
dist_type = self.timestamp_dist_type_var.get()
|
|
config = self.detector_configs[self.detector_var.get()]["timestamp_distribution"]
|
|
|
|
if dist_type == "normal":
|
|
# 均值和标准差
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="均值:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_mean_var = ctk.DoubleVar(value=config.get("mean", 100.0))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_mean_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="标准差:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_std_var = ctk.DoubleVar(value=config.get("std", 30.0))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_std_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
elif dist_type == "uniform":
|
|
# 最小值和最大值
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="最小值:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_low_var = ctk.DoubleVar(value=config.get("low", 0.0))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_low_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="最大值:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_high_var = ctk.DoubleVar(value=config.get("high", 200.0))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_high_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
elif dist_type in ["exponential", "gamma"]:
|
|
# 尺度参数
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="尺度参数:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_scale_var = ctk.DoubleVar(value=config.get("scale", 50.0))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_scale_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
if dist_type == "gamma":
|
|
# 形状参数
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="形状参数:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_shape_var = ctk.DoubleVar(value=config.get("shape", 2.0))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_shape_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
elif dist_type == "poisson":
|
|
# lambda参数
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="lambda:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_lam_var = ctk.DoubleVar(value=config.get("lam", 100.0))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_lam_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
elif dist_type == "lognormal":
|
|
# 均值和sigma
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="均值:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_mean_var = ctk.DoubleVar(value=config.get("mean", 4.0))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_mean_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
ctk.CTkLabel(self.timestamp_params_frame, text="sigma:").pack(side="left", padx=10, pady=5)
|
|
self.timestamp_sigma_var = ctk.DoubleVar(value=config.get("sigma", 0.5))
|
|
ctk.CTkEntry(
|
|
self.timestamp_params_frame,
|
|
textvariable=self.timestamp_sigma_var,
|
|
width=100
|
|
).pack(side="left", padx=5, pady=5)
|
|
|
|
def preview_energy_dist(self):
|
|
"""预览能量分布曲线"""
|
|
dist_type = self.energy_dist_type_var.get()
|
|
|
|
# 生成数据
|
|
if dist_type == "normal":
|
|
mean = self.energy_mean_var.get()
|
|
std = self.energy_std_var.get()
|
|
data = np.random.normal(mean, std, 10000)
|
|
elif dist_type == "uniform":
|
|
low = self.energy_low_var.get()
|
|
high = self.energy_high_var.get()
|
|
data = np.random.uniform(low, high, 10000)
|
|
elif dist_type == "exponential":
|
|
scale = self.energy_scale_var.get()
|
|
data = np.random.exponential(scale, 10000)
|
|
elif dist_type == "gamma":
|
|
shape = self.energy_shape_var.get()
|
|
scale = self.energy_scale_var.get()
|
|
data = np.random.gamma(shape, scale, 10000)
|
|
elif dist_type == "poisson":
|
|
lam = self.energy_lam_var.get()
|
|
data = np.random.poisson(lam, 10000)
|
|
elif dist_type == "lognormal":
|
|
mean = self.energy_mean_var.get()
|
|
sigma = self.energy_sigma_var.get()
|
|
data = np.random.lognormal(mean, sigma, 10000)
|
|
elif dist_type == "constant":
|
|
value = self.energy_constant_var.get()
|
|
data = np.full(10000, value)
|
|
else:
|
|
data = np.random.normal(1000, 200, 10000)
|
|
|
|
# 创建图表
|
|
fig, ax = plt.subplots(figsize=(6, 4))
|
|
ax.hist(data, bins=50, alpha=0.7, color='blue')
|
|
ax.set_title(f"{dist_type} 分布预览")
|
|
ax.set_xlabel("能量 (keV)")
|
|
ax.set_ylabel("频率")
|
|
ax.grid(True, alpha=0.3)
|
|
|
|
# 创建临时窗口显示图表
|
|
preview_window = ctk.CTkToplevel(self)
|
|
preview_window.title("分布预览")
|
|
preview_window.geometry("600x400")
|
|
|
|
canvas = FigureCanvasTkAgg(fig, master=preview_window)
|
|
canvas.draw()
|
|
canvas.get_tk_widget().pack(fill="both", expand=True, padx=10, pady=10)
|
|
|
|
def save_config(self):
|
|
"""保存配置"""
|
|
detector = self.detector_var.get()
|
|
|
|
# 更新样本空间大小和替换设置
|
|
self.detector_configs[detector]["sample_space_size"] = self.sample_space_var.get()
|
|
self.detector_configs[detector]["allow_replacement"] = self.allow_replacement_var.get()
|
|
|
|
# 更新能量分布
|
|
dist_type = self.energy_dist_type_var.get()
|
|
self.detector_configs[detector]["energy_distribution"]["type"] = dist_type
|
|
|
|
if dist_type == "normal":
|
|
self.detector_configs[detector]["energy_distribution"]["mean"] = self.energy_mean_var.get()
|
|
self.detector_configs[detector]["energy_distribution"]["std"] = self.energy_std_var.get()
|
|
elif dist_type == "uniform":
|
|
self.detector_configs[detector]["energy_distribution"]["low"] = self.energy_low_var.get()
|
|
self.detector_configs[detector]["energy_distribution"]["high"] = self.energy_high_var.get()
|
|
elif dist_type == "exponential":
|
|
self.detector_configs[detector]["energy_distribution"]["scale"] = self.energy_scale_var.get()
|
|
elif dist_type == "gamma":
|
|
self.detector_configs[detector]["energy_distribution"]["shape"] = self.energy_shape_var.get()
|
|
self.detector_configs[detector]["energy_distribution"]["scale"] = self.energy_scale_var.get()
|
|
elif dist_type == "poisson":
|
|
self.detector_configs[detector]["energy_distribution"]["lam"] = self.energy_lam_var.get()
|
|
elif dist_type == "lognormal":
|
|
self.detector_configs[detector]["energy_distribution"]["mean"] = self.energy_mean_var.get()
|
|
self.detector_configs[detector]["energy_distribution"]["sigma"] = self.energy_sigma_var.get()
|
|
|
|
# 更新时间戳分布
|
|
dist_type = self.timestamp_dist_type_var.get()
|
|
self.detector_configs[detector]["timestamp_distribution"]["type"] = dist_type
|
|
|
|
if dist_type == "normal":
|
|
self.detector_configs[detector]["timestamp_distribution"]["mean"] = self.timestamp_mean_var.get()
|
|
self.detector_configs[detector]["timestamp_distribution"]["std"] = self.timestamp_std_var.get()
|
|
elif dist_type == "uniform":
|
|
self.detector_configs[detector]["timestamp_distribution"]["low"] = self.timestamp_low_var.get()
|
|
self.detector_configs[detector]["timestamp_distribution"]["high"] = self.timestamp_high_var.get()
|
|
elif dist_type == "exponential":
|
|
self.detector_configs[detector]["timestamp_distribution"]["scale"] = self.timestamp_scale_var.get()
|
|
elif dist_type == "gamma":
|
|
self.detector_configs[detector]["timestamp_distribution"]["shape"] = self.timestamp_shape_var.get()
|
|
self.detector_configs[detector]["timestamp_distribution"]["scale"] = self.timestamp_scale_var.get()
|
|
elif dist_type == "poisson":
|
|
self.detector_configs[detector]["timestamp_distribution"]["lam"] = self.timestamp_lam_var.get()
|
|
elif dist_type == "lognormal":
|
|
self.detector_configs[detector]["timestamp_distribution"]["mean"] = self.timestamp_mean_var.get()
|
|
self.detector_configs[detector]["timestamp_distribution"]["sigma"] = self.timestamp_sigma_var.get()
|
|
|
|
def get_config(self):
|
|
"""获取配置"""
|
|
# 保存当前配置
|
|
self.save_config()
|
|
# 确保每个探测器配置都包含name键
|
|
for detector_name, config in self.detector_configs.items():
|
|
config['name'] = detector_name
|
|
return self.detector_configs
|
|
|
|
def set_config(self, config):
|
|
"""设置配置"""
|
|
if isinstance(config, dict):
|
|
for detector, det_config in config.items():
|
|
if detector in self.detector_configs:
|
|
self.detector_configs[detector] = det_config
|
|
# 更新显示
|
|
self.update_config_display()
|