|
|
|
|
|
"""
|
|
Created on Tue Feb 28 13:21:41 2023
|
|
|
|
@author: DIDSR
|
|
"""
|
|
|
|
|
|
import argparse
|
|
from tkinter import filedialog, Menu, IntVar, W
|
|
import customtkinter
|
|
import numpy as np
|
|
from matplotlib.pyplot import (
|
|
style as plt_style,
|
|
ioff as plt_ioff,
|
|
figure as plt_figure,
|
|
rcParams as params,
|
|
)
|
|
|
|
plt_ioff()
|
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
|
import tomli
|
|
from dose_equations import (
|
|
Sarno_mono_dgn,
|
|
Sarno_poly_dgn,
|
|
sarno_dgnct,
|
|
Hernandez_hetero_mono_dgn,
|
|
exposure_per_fluence,
|
|
Sechopoulos_poly_dgn,
|
|
)
|
|
|
|
|
|
dict_font_size_set = {1: 14, 2: 24}
|
|
|
|
font_size = None
|
|
|
|
|
|
|
|
|
|
def quit_me():
|
|
root.quit()
|
|
root.destroy()
|
|
|
|
|
|
|
|
def calculate_pDgNct(*values):
|
|
keV = values[2]
|
|
I = values[3]
|
|
psiE = np.array(list(map(exposure_per_fluence, keV)))
|
|
|
|
if values[0] == "Sarno Koning":
|
|
variables = values[1]
|
|
DgNctE = np.array(
|
|
list(
|
|
map(
|
|
sarno_dgnct,
|
|
variables[:, 0],
|
|
variables[:, 1],
|
|
variables[:, 2],
|
|
variables[:, 3],
|
|
variables[:, 4],
|
|
variables[:, 5],
|
|
variables[:, 6],
|
|
variables[:, 7],
|
|
keV,
|
|
)
|
|
)
|
|
)
|
|
pDgN = np.sum(I * psiE * DgNctE) / np.sum(I * psiE)
|
|
elif values[0] == "Hernandez":
|
|
DgN_list = np.array(values[1])
|
|
pDgN = np.sum(I * psiE * DgN_list) / (np.sum(I * psiE))
|
|
|
|
return pDgN
|
|
|
|
|
|
|
|
with open("method_specific_inputs.toml", "rb") as file_method_specific_inputs:
|
|
config_method_specific_inputs = tomli.load(file_method_specific_inputs)
|
|
|
|
|
|
|
|
|
|
|
|
class Main_Window:
|
|
|
|
def __init__(self, master):
|
|
self.master = master
|
|
|
|
menubar = Menu(master)
|
|
root.config(menu=menubar)
|
|
helpmenu = Menu(menubar, tearoff=0)
|
|
helpmenu.add_command(label="Quick Start Guide", command=self.help_command)
|
|
menubar.add_cascade(label="Help", menu=helpmenu)
|
|
menubar.add_command(label="Exit", command=lambda: quit_me())
|
|
|
|
|
|
self.methods_frame = customtkinter.CTkFrame(master=master)
|
|
self.methods_frame.grid(row=0, column=0, ipady=36)
|
|
self.inputs_frame = customtkinter.CTkFrame(master=master)
|
|
self.inputs_frame.grid(row=0, column=1, padx=10, pady=5, ipadx=6)
|
|
self.kerma_spec_frame = customtkinter.CTkFrame(master=master)
|
|
self.kerma_spec_frame.grid(row=0, column=2, pady=5, ipady=24, ipadx=4)
|
|
self.output_frame = customtkinter.CTkFrame(master=master)
|
|
self.output_frame.grid(row=1, column=0, ipady=12)
|
|
self.graph_frame = customtkinter.CTkFrame(master=master)
|
|
self.graph_frame.grid(row=1, column=1, columnspan=2, padx=4, ipady=14)
|
|
|
|
self.keV = []
|
|
self.I = []
|
|
self.plot_spectra()
|
|
|
|
|
|
self.method_label = customtkinter.CTkLabel(
|
|
master=self.methods_frame,
|
|
text="Choose any of the following BCT methods:",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.method_chosen = IntVar(root)
|
|
self.method_chosen.set(2)
|
|
self.current_method = 1
|
|
self.Sarno_radiobutton = customtkinter.CTkRadioButton(
|
|
master=self.methods_frame,
|
|
text="Sarno 49 kVp W Spectra",
|
|
command=lambda: self.change_inputs(),
|
|
variable=self.method_chosen,
|
|
value=1,
|
|
radiobutton_width=14,
|
|
radiobutton_height=14,
|
|
font=("Roman", font_size),
|
|
)
|
|
|
|
self.Sarno_incident_radiobutton = customtkinter.CTkRadioButton(
|
|
master=self.methods_frame,
|
|
text="Sarno Any Spectrum",
|
|
command=lambda: self.change_inputs(),
|
|
variable=self.method_chosen,
|
|
value=2,
|
|
radiobutton_width=14,
|
|
radiobutton_height=14,
|
|
font=("Roman", font_size),
|
|
)
|
|
|
|
self.Hernandez_radiobutton = customtkinter.CTkRadioButton(
|
|
master=self.methods_frame,
|
|
text="Hernandez Any Spectrum",
|
|
command=lambda: self.change_inputs(),
|
|
variable=self.method_chosen,
|
|
value=3,
|
|
radiobutton_width=14,
|
|
radiobutton_height=14,
|
|
font=("Roman", font_size),
|
|
)
|
|
|
|
self.Sechopoulos_radiobutton = customtkinter.CTkRadioButton(
|
|
master=self.methods_frame,
|
|
text="Sechopoulos 49 kVp W Spectra",
|
|
command=lambda: self.change_inputs(),
|
|
variable=self.method_chosen,
|
|
value=4,
|
|
radiobutton_width=14,
|
|
radiobutton_height=14,
|
|
font=("Roman", font_size),
|
|
)
|
|
|
|
self.method_label.pack(padx=20, pady=5, anchor=W)
|
|
self.Sarno_incident_radiobutton.pack(padx=20, pady=5, anchor=W)
|
|
self.Hernandez_radiobutton.pack(padx=20, pady=5, anchor=W)
|
|
self.Sarno_radiobutton.pack(padx=20, pady=5, anchor=W)
|
|
self.Sechopoulos_radiobutton.pack(padx=20, pady=5, anchor=W)
|
|
|
|
|
|
self.Breast_diameter_label = customtkinter.CTkLabel(
|
|
master=self.inputs_frame,
|
|
text="Breast Diameter (cm):",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.Breast_diameter_combo = customtkinter.CTkComboBox(
|
|
master=self.inputs_frame,
|
|
values=["8", "10", "12", "14", "19", "18"],
|
|
width=120,
|
|
state="readonly",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.Breast_diameter_combo.set("8")
|
|
|
|
self.Breast_height_label = customtkinter.CTkLabel(
|
|
master=self.inputs_frame, text="Breast Height:", font=("Roman", font_size)
|
|
)
|
|
self.Breast_height_combo = customtkinter.CTkComboBox(
|
|
master=self.inputs_frame,
|
|
values=["1 x radius", "1.5 x radius", "2 x radius"],
|
|
width=120,
|
|
state="readonly",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.Breast_height_combo.set("1 x radius")
|
|
self.Breast_glandularity_label = customtkinter.CTkLabel(
|
|
master=self.inputs_frame,
|
|
text="Breast Glandularity:",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.Breast_glandularity_combo = customtkinter.CTkComboBox(
|
|
master=self.inputs_frame,
|
|
values=["0.1%", "14.3%", "25%", "50%", "100%"],
|
|
width=120,
|
|
state="readonly",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.Breast_glandularity_combo.set("0.1%")
|
|
|
|
self.HVL_label = customtkinter.CTkLabel(
|
|
master=self.inputs_frame, text="HVL (mm Al):", font=("Roman", font_size)
|
|
)
|
|
self.HVL_combo = customtkinter.CTkComboBox(
|
|
master=self.inputs_frame,
|
|
values=["1.25", "1.30", "1.35", "1.40", "1.45", "1.50"],
|
|
width=120,
|
|
state="readonly",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.HVL_combo.set("1.25")
|
|
|
|
self.VGF_label = customtkinter.CTkLabel(
|
|
master=self.inputs_frame,
|
|
text="Heterogeneous Categories:",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.VGF_combo = customtkinter.CTkComboBox(
|
|
master=self.inputs_frame,
|
|
values=["V1 = 19.9%", "V3 = 9.5%", "V5 = 3.8%"],
|
|
width=120,
|
|
state="readonly",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.VGF_combo.set("V1 = 19.9%")
|
|
self.input_spectra_button = customtkinter.CTkButton(
|
|
master=self.inputs_frame,
|
|
fg_color=("black", "lightgray"),
|
|
width=100,
|
|
border_width=0,
|
|
corner_radius=2,
|
|
text="Upload Incident Spectrum File",
|
|
font=("Roman", font_size),
|
|
command=lambda: self.browse_files(),
|
|
)
|
|
self.Breast_diameter_label.grid(row=0, column=0, pady=5, padx=4, sticky=W)
|
|
self.Breast_diameter_combo.grid(row=0, column=1, pady=5, padx=10, sticky=W)
|
|
self.Breast_height_label.grid(row=1, column=0, pady=5, padx=4, sticky=W)
|
|
self.Breast_height_combo.grid(row=1, column=1, pady=5)
|
|
self.Breast_glandularity_label.grid(row=2, column=0, pady=5, padx=4, sticky=W)
|
|
self.Breast_glandularity_combo.grid(row=2, column=1, pady=5)
|
|
self.HVL_label.grid(row=3, column=0, pady=5, padx=4, sticky=W)
|
|
self.HVL_combo.grid(row=3, column=1, pady=5)
|
|
self.HVL_combo.configure(state="disabled")
|
|
self.VGF_label.grid(row=4, column=0, pady=5, padx=4, sticky=W)
|
|
self.VGF_combo.grid(row=4, column=1, pady=5, padx=12, sticky=W)
|
|
self.VGF_combo.configure(state="disabled")
|
|
self.input_spectra_button.grid(row=5, column=0, pady=5, columnspan=2)
|
|
|
|
|
|
self.output_textbox = customtkinter.CTkTextbox(
|
|
self.output_frame, width=285, height=305, font=("Roman", font_size)
|
|
)
|
|
self.output_textbox.tag_config("green", foreground="green")
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.insert(
|
|
"end",
|
|
f'{"".join(config_method_specific_inputs["method_specific_inputs"]["Sarno_specific_outputs"])}',
|
|
"green",
|
|
)
|
|
self.output_textbox.configure(state="disabled")
|
|
self.clear_button = customtkinter.CTkButton(
|
|
master=self.output_frame,
|
|
width=120,
|
|
border_width=0,
|
|
corner_radius=8,
|
|
text="Clear Text",
|
|
font=("Roman", font_size),
|
|
command=lambda: self.clear_text(),
|
|
)
|
|
self.calculate_button = customtkinter.CTkButton(
|
|
master=self.output_frame,
|
|
width=120,
|
|
border_width=0,
|
|
corner_radius=8,
|
|
text="Calculate Dose",
|
|
font=("Roman", font_size),
|
|
command=lambda: self.calculate_dose(),
|
|
)
|
|
|
|
self.clear_button.grid(row=1, column=0, pady=5, padx=4)
|
|
self.output_textbox.grid(row=0, column=0, columnspan=3)
|
|
self.output_textbox.configure(state="disabled")
|
|
self.calculate_button.grid(row=1, column=1, pady=5, padx=4)
|
|
|
|
|
|
|
|
self.mAs_label = customtkinter.CTkLabel(
|
|
master=self.kerma_spec_frame,
|
|
text="mAs per Projection:",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.mAs_entry = customtkinter.CTkEntry(master=self.kerma_spec_frame, width=80)
|
|
self.mAs_units_combo = customtkinter.CTkComboBox(
|
|
master=self.kerma_spec_frame,
|
|
values=["mAs"],
|
|
width=80,
|
|
state="readonly",
|
|
font=("Roman", font_size),
|
|
)
|
|
|
|
self.air_kerma_label = customtkinter.CTkLabel(
|
|
master=self.kerma_spec_frame,
|
|
text="Air kerma per Projection:",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.air_kerma_entry = customtkinter.CTkEntry(
|
|
master=self.kerma_spec_frame, width=80
|
|
)
|
|
self.input_label = customtkinter.CTkLabel(
|
|
master=self.kerma_spec_frame,
|
|
text="Air kerma Units:",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.air_kerma_units_combo = customtkinter.CTkComboBox(
|
|
master=self.kerma_spec_frame,
|
|
values=["mrad", "mGy", "R", "mR"],
|
|
width=80,
|
|
state="readonly",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.air_kerma_units_combo.set("R")
|
|
self.air_kerma_output_label = customtkinter.CTkLabel(
|
|
master=self.kerma_spec_frame,
|
|
text="MGD Units:",
|
|
anchor=W,
|
|
font=("Roman", font_size),
|
|
)
|
|
self.output_units = customtkinter.CTkComboBox(
|
|
master=self.kerma_spec_frame,
|
|
values=["mrad", "mGy"],
|
|
width=80,
|
|
state="readonly",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.output_units.set("mrad")
|
|
|
|
self.number_projections_label = customtkinter.CTkLabel(
|
|
master=self.kerma_spec_frame,
|
|
text="Number of Projections: ",
|
|
font=("Roman", font_size),
|
|
)
|
|
self.number_projections_entry = customtkinter.CTkEntry(
|
|
master=self.kerma_spec_frame, width=80
|
|
)
|
|
|
|
self.graph_spectra = customtkinter.CTkButton(
|
|
master=self.kerma_spec_frame,
|
|
width=220,
|
|
border_width=1,
|
|
corner_radius=10,
|
|
text="Graph Spectrum",
|
|
font=("Roman", font_size),
|
|
command=self.plot_spectra,
|
|
)
|
|
|
|
self.air_kerma_label.grid(row=1, column=0, pady=5, padx=4, sticky=W)
|
|
self.air_kerma_entry.grid(row=1, column=1, pady=5, padx=12, sticky=W)
|
|
self.number_projections_label.grid(row=2, column=0, pady=5, padx=4, sticky=W)
|
|
self.number_projections_entry.grid(row=2, column=1, pady=5, padx=12, sticky=W)
|
|
self.mAs_label.grid(row=5, column=0, pady=5, padx=4, sticky=W)
|
|
self.mAs_entry.grid(row=5, column=1, pady=5, padx=12, sticky=W)
|
|
self.input_label.grid(row=3, column=0, pady=5, padx=4, sticky=W)
|
|
self.air_kerma_units_combo.grid(row=3, column=1, pady=5, padx=12, sticky=W)
|
|
self.air_kerma_output_label.grid(row=4, column=0, pady=5, padx=4, sticky=W)
|
|
self.output_units.grid(row=4, column=1, pady=5, padx=12, sticky=W)
|
|
self.graph_spectra.grid(row=6, column=0, pady=5, columnspan=2)
|
|
self.graph_spectra.configure(state="disabled")
|
|
|
|
|
|
|
|
def help_command(self):
|
|
pop_up = customtkinter.CTkToplevel()
|
|
textbox = customtkinter.CTkTextbox(master=pop_up, width=800, height=500)
|
|
textbox.pack(fill="both")
|
|
|
|
with open("CT_Dose_Calculate_Quick_Guide.txt", "r") as file:
|
|
data = file.read()
|
|
|
|
textbox.insert("end", f"{data}")
|
|
textbox.configure(state="disabled")
|
|
|
|
|
|
def change_inputs(self):
|
|
current_method = self.method_chosen.get()
|
|
|
|
if current_method == 1:
|
|
|
|
self.VGF_combo.configure(state="disabled")
|
|
self.input_spectra_button.configure(state="disabled")
|
|
self.Breast_diameter_combo.configure(state="normal")
|
|
self.Breast_height_combo.configure(state="normal")
|
|
self.Breast_glandularity_combo.configure(state="normal")
|
|
self.HVL_combo.configure(state="normal")
|
|
self.graph_spectra.configure(state="disabled")
|
|
self.Breast_glandularity_combo.configure(
|
|
values=["0.1%", "14.3%", "25%", "50%", "75%", "100%"]
|
|
)
|
|
self.Breast_glandularity_combo.set("0.1%")
|
|
self.Breast_diameter_combo.configure(
|
|
values=["8", "10", "12", "14", "19", "18"]
|
|
)
|
|
self.Breast_diameter_combo.set("8")
|
|
self.Breast_height_combo.configure(
|
|
values=["1 x radius", "1.5 x radius", "2 x radius"]
|
|
)
|
|
self.Breast_height_combo.set("1 x radius")
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.delete("0.0", "end")
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.insert(
|
|
"end",
|
|
f'{"".join(config_method_specific_inputs["method_specific_inputs"]["Sarno_49_specific_output"])}',
|
|
"green",
|
|
)
|
|
self.output_textbox.configure(state="disabled")
|
|
self.keV = []
|
|
self.I = []
|
|
self.plot_spectra()
|
|
|
|
elif current_method == 2:
|
|
self.VGF_combo.configure(state="disabled")
|
|
self.input_spectra_button.configure(state="normal")
|
|
self.Breast_diameter_combo.configure(state="normal")
|
|
self.Breast_height_combo.configure(state="normal")
|
|
self.Breast_glandularity_combo.configure(state="normal")
|
|
self.HVL_combo.configure(state="disabled")
|
|
self.graph_spectra.configure(state="disabled")
|
|
self.Breast_glandularity_combo.configure(
|
|
values=["0.1%", "14.3%", "25%", "50%", "75%", "100%"]
|
|
)
|
|
self.Breast_glandularity_combo.set("0.1%")
|
|
self.Breast_diameter_combo.configure(
|
|
values=["8", "10", "12", "14", "19", "18"]
|
|
)
|
|
self.Breast_diameter_combo.set("8")
|
|
self.Breast_height_combo.configure(
|
|
values=["1 x radius", "1.5 x radius", "2 x radius"]
|
|
)
|
|
self.Breast_height_combo.set("1 x radius")
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.delete("0.0", "end")
|
|
self.output_textbox.insert(
|
|
"end",
|
|
f'{"".join(config_method_specific_inputs["method_specific_inputs"]["Sarno_specific_outputs"])}',
|
|
"green",
|
|
)
|
|
self.output_textbox.configure(state="disabled")
|
|
self.keV = []
|
|
self.I = []
|
|
self.plot_spectra()
|
|
|
|
elif current_method == 3:
|
|
self.VGF_combo.configure(state="normal")
|
|
self.input_spectra_button.configure(state="normal")
|
|
self.Breast_diameter_combo.configure(state="disabled")
|
|
self.Breast_height_combo.configure(state="disabled")
|
|
self.Breast_glandularity_combo.configure(state="disabled")
|
|
self.HVL_combo.configure(state="disabled")
|
|
self.graph_spectra.configure(state="disabled")
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.delete("0.0", "end")
|
|
self.output_textbox.insert(
|
|
"end",
|
|
f'{"".join(config_method_specific_inputs["method_specific_inputs"]["Hernandez_specific_output"])}',
|
|
"green",
|
|
)
|
|
self.output_textbox.configure(state="disabled")
|
|
self.keV = []
|
|
self.I = []
|
|
self.plot_spectra()
|
|
|
|
else:
|
|
self.VGF_combo.configure(state="disabled")
|
|
self.input_spectra_button.configure(state="disabled")
|
|
self.Breast_diameter_combo.configure(state="normal")
|
|
self.Breast_height_combo.configure(state="normal")
|
|
self.Breast_glandularity_combo.configure(state="normal")
|
|
self.HVL_combo.configure(state="disabled")
|
|
self.graph_spectra.configure(state="disabled")
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.delete("0.0", "end")
|
|
self.output_textbox.insert(
|
|
"end",
|
|
f'{"".join(config_method_specific_inputs["method_specific_inputs"]["Sechopoulos_specific_output"])}',
|
|
"green",
|
|
)
|
|
self.output_textbox.configure(state="disabled")
|
|
self.Breast_glandularity_combo.configure(
|
|
values=["1%", "14.3%", "25%", "50%", "75%", "100%"]
|
|
)
|
|
self.Breast_glandularity_combo.set("1%")
|
|
self.Breast_diameter_combo.configure(values=["10", "12", "14", "19", "18"])
|
|
self.Breast_diameter_combo.set("10")
|
|
self.Breast_height_combo.configure(
|
|
values=["0.5 x diameter", "0.75 x diameter", "1 x diameter"]
|
|
)
|
|
self.Breast_height_combo.set("0.5 x diameter")
|
|
self.keV = []
|
|
self.I = []
|
|
self.plot_spectra()
|
|
|
|
|
|
def browse_files(self):
|
|
|
|
self.input_txt_file = filedialog.askopenfilename(
|
|
initialdir="/", title="Select a File", filetypes=[("all files", "*.*")]
|
|
)
|
|
self.display_txt_file = self.input_txt_file.split("/")[-1]
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.insert(
|
|
"end", f"\nInputted incident spectrum: \n{self.display_txt_file}", "\n"
|
|
)
|
|
self.output_textbox.configure(state="disabled")
|
|
|
|
try:
|
|
|
|
self.keV, self.I = self.read_input_spectra()
|
|
self.minimum = min(self.keV)
|
|
self.maximum = max(self.keV)
|
|
method = self.method_chosen.get()
|
|
|
|
if method == 3:
|
|
if self.minimum < 9 or self.maximum > 70:
|
|
raise (ValueError)
|
|
elif method == 2:
|
|
if self.minimum < 8 or self.maximum > 80:
|
|
raise (ValueError)
|
|
|
|
self.graph_spectra.configure(state="normal")
|
|
|
|
|
|
except UnicodeDecodeError:
|
|
pop_up = customtkinter.CTkToplevel()
|
|
customtkinter.CTkLabel(
|
|
pop_up, text="Please enter a valid text file", font=("Roman", font_size)
|
|
).pack()
|
|
|
|
except ValueError:
|
|
pop_up = customtkinter.CTkToplevel()
|
|
customtkinter.CTkLabel(
|
|
pop_up,
|
|
text="For Hernadez Any spectrum, please enter spectrum ranging from 9 to 70 keV",
|
|
font=("Roman", font_size),
|
|
).pack()
|
|
customtkinter.CTkLabel(
|
|
pop_up,
|
|
text="For Sarno Any Spectrum, please enter spectrum ranging from 8 to 80 keV",
|
|
font=("Roman", font_size),
|
|
).pack()
|
|
|
|
|
|
def read_input_spectra(self):
|
|
with open(self.input_txt_file, "r") as file:
|
|
keV = []
|
|
I = []
|
|
data = file.readlines()
|
|
for line in data:
|
|
line = line.split()
|
|
keV.append(float(line[0]))
|
|
I.append(float(line[1]))
|
|
|
|
return keV, I
|
|
|
|
|
|
def clear_text(self):
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.delete("0.0", "end")
|
|
self.output_textbox.configure(state="disabled")
|
|
|
|
|
|
def plot_spectra(self):
|
|
try:
|
|
y_end = max(self.I) * 1.1
|
|
x_end = max(self.keV) * 1.1
|
|
file_name = self.display_txt_file
|
|
except:
|
|
x_end = 5
|
|
y_end = 5
|
|
file_name = "Input Incident Spectrum"
|
|
|
|
plt_style.use(["dark_background"])
|
|
params["figure.figsize"] = [7.50, 3.50]
|
|
params["figure.autolayout"] = True
|
|
self.figure = plt_figure(figsize=(7.6, 4.2), dpi=100)
|
|
axes = self.figure.add_subplot(111)
|
|
axes.plot(self.keV, self.I, "b-")
|
|
axes.set_title(f"{file_name}")
|
|
axes.set_xlabel("Energy (keV)", fontsize=11)
|
|
axes.set_ylabel("Intensity (counts)", fontsize=11)
|
|
axes.set_xlim(0, x_end)
|
|
axes.set_ylim([0, y_end])
|
|
chart = FigureCanvasTkAgg(self.figure, master=self.graph_frame)
|
|
chart.get_tk_widget().grid(row=0, column=0, columnspan=2)
|
|
|
|
|
|
def calculate_mgd(
|
|
self, air_kerma_input, air_kerma, dgn, output_units, number_of_projections
|
|
):
|
|
|
|
if air_kerma_input != "mGy":
|
|
if air_kerma_input == "mrad":
|
|
air_kerma = air_kerma * 0.01
|
|
elif air_kerma_input == "R":
|
|
air_kerma = air_kerma * 8.77
|
|
elif air_kerma_input == "mR":
|
|
air_kerma = air_kerma * 0.00877
|
|
|
|
mgd = air_kerma * dgn * float(number_of_projections)
|
|
|
|
if output_units == "mrad":
|
|
mgd = mgd * 100
|
|
|
|
return mgd
|
|
|
|
|
|
def output_dose(self, *values):
|
|
self.output_textbox.configure(state="normal")
|
|
self.output_textbox.insert(
|
|
"end", f"\n\nMGD = {self.mgd:.4f} {self.output_units.get()}"
|
|
)
|
|
self.output_textbox.insert(
|
|
"end", f" with {self.number_projections_entry.get()} projections"
|
|
)
|
|
self.output_textbox.insert("end", f" with {self.mAs_entry.get()} mAs")
|
|
|
|
for index in range(len(values)):
|
|
if index == 0 or index % 2 == 0:
|
|
self.output_textbox.insert("end", f"\n{values[index]}")
|
|
else:
|
|
self.output_textbox.insert("end", f"{values[index]}")
|
|
|
|
self.output_textbox.configure(state="disabled")
|
|
|
|
|
|
def calculate_dose(self):
|
|
|
|
current_method = self.method_chosen.get()
|
|
air_kerma_input_units = self.air_kerma_units_combo.get()
|
|
output_units = self.output_units.get()
|
|
air_kerma = self.air_kerma_entry.get()
|
|
number_of_projections = self.number_projections_entry.get()
|
|
|
|
try:
|
|
|
|
air_kerma_check = len(air_kerma)
|
|
number_of_projections_check = len(number_of_projections)
|
|
air_kerma = float(air_kerma)
|
|
mAs_per_projection = float(self.mAs_entry.get())
|
|
air_kerma = mAs_per_projection * air_kerma
|
|
|
|
if air_kerma_check == 0 or number_of_projections_check == 0:
|
|
raise (ValueError)
|
|
|
|
elif current_method == 2 or current_method == 3:
|
|
kev_check = len(self.keV)
|
|
if kev_check == 0:
|
|
raise (TypeError)
|
|
|
|
|
|
if current_method == 1:
|
|
|
|
HVL = self.HVL_combo.get()
|
|
breast_diameter = self.Breast_diameter_combo.get()
|
|
breast_glandularity = self.Breast_glandularity_combo.get()
|
|
breast_height = "".join(self.Breast_height_combo.get().split(" ")[0:2])
|
|
|
|
|
|
dgn_subtable_breast_height = Sarno_poly_dgn.groupby(
|
|
["breast height"]
|
|
).get_group(breast_height)
|
|
dgn_subtable_glandularity = dgn_subtable_breast_height.groupby(
|
|
["Glandularity"]
|
|
).get_group(breast_glandularity)
|
|
dgn = dgn_subtable_glandularity.loc[float(HVL), str(breast_diameter)]
|
|
|
|
|
|
self.mgd = self.calculate_mgd(
|
|
air_kerma_input_units,
|
|
air_kerma,
|
|
dgn,
|
|
output_units,
|
|
number_of_projections,
|
|
)
|
|
|
|
self.output_dose(
|
|
"Breast diameter: ",
|
|
breast_diameter,
|
|
"HVL: ",
|
|
HVL,
|
|
"Breast_glandularity: ",
|
|
breast_glandularity,
|
|
"Breast Height: ",
|
|
self.Breast_height_combo.get(),
|
|
)
|
|
|
|
elif current_method == 2:
|
|
breast_diameter = self.Breast_diameter_combo.get()
|
|
breast_glandularity = self.Breast_glandularity_combo.get()
|
|
breast_height = "".join(self.Breast_height_combo.get().split(" ")[0:2])
|
|
|
|
|
|
dgn_subtable_breast_height = Sarno_mono_dgn.groupby(
|
|
["breast height"]
|
|
).get_group(breast_height)
|
|
dgn_subtable_glandularity = dgn_subtable_breast_height.groupby(
|
|
["Glandularity"]
|
|
).get_group(breast_glandularity)
|
|
values = dgn_subtable_glandularity[str(breast_diameter)].tolist()
|
|
variables = np.zeros((len(self.keV), len(values)))
|
|
|
|
for index in range(0, len(values)):
|
|
variables[:, index] = [float(values[index])]
|
|
|
|
|
|
dgn = calculate_pDgNct(
|
|
"Sarno Koning", variables, self.keV, self.I
|
|
)
|
|
|
|
self.mgd = self.calculate_mgd(
|
|
air_kerma_input_units,
|
|
air_kerma,
|
|
dgn,
|
|
output_units,
|
|
number_of_projections,
|
|
)
|
|
|
|
self.output_dose(
|
|
"Breast diameter: ",
|
|
breast_diameter,
|
|
"Breast Glandularity: ",
|
|
breast_glandularity,
|
|
"Breast Height: ",
|
|
self.Breast_height_combo.get(),
|
|
)
|
|
|
|
elif current_method == 3:
|
|
|
|
VGF = self.VGF_combo.get().split("=")[0].strip()
|
|
DgN_list = Hernandez_hetero_mono_dgn.loc[:, VGF].tolist()
|
|
|
|
|
|
start_index = int(abs(self.minimum - 9))
|
|
if self.maximum != 70:
|
|
end_index = int(abs(self.maximum - 9)) + 1
|
|
else:
|
|
end_index = -1
|
|
|
|
|
|
Hernandez_keV = list(np.arange(self.minimum, self.maximum + 1))
|
|
DgN_list = DgN_list[start_index:end_index]
|
|
interp_DgN_list = np.interp(self.keV, Hernandez_keV, DgN_list)
|
|
|
|
|
|
dgn = calculate_pDgNct("Hernandez", interp_DgN_list, self.keV, self.I)
|
|
|
|
print(dgn)
|
|
|
|
|
|
self.mgd = self.calculate_mgd(
|
|
air_kerma_input_units,
|
|
air_kerma,
|
|
dgn,
|
|
output_units,
|
|
number_of_projections,
|
|
)
|
|
|
|
self.output_dose("VGF: ", self.VGF_combo.get())
|
|
|
|
else:
|
|
|
|
breast_diameter = self.Breast_diameter_combo.get()
|
|
breast_glandularity = self.Breast_glandularity_combo.get()
|
|
breast_height = float(
|
|
self.Breast_height_combo.get().split("x")[0]
|
|
) * float(breast_diameter)
|
|
|
|
|
|
dgn_subtable_breast_height = Sechopoulos_poly_dgn.groupby(
|
|
["Chest wall-to-nipple distance"]
|
|
).get_group(breast_height)
|
|
dgn = dgn_subtable_breast_height[breast_glandularity].tolist()[0]
|
|
|
|
|
|
self.mgd = self.calculate_mgd(
|
|
air_kerma_input_units,
|
|
air_kerma,
|
|
dgn,
|
|
output_units,
|
|
number_of_projections,
|
|
)
|
|
|
|
self.output_dose(
|
|
"Breast diameter: ",
|
|
breast_diameter,
|
|
"Breast_glandularity: ",
|
|
breast_glandularity,
|
|
"Breast Height: ",
|
|
self.Breast_height_combo.get(),
|
|
)
|
|
|
|
except ValueError:
|
|
pop_up = customtkinter.CTkToplevel()
|
|
|
|
if air_kerma_check == 0:
|
|
customtkinter.CTkLabel(
|
|
pop_up,
|
|
text="Please enter a numeric value into the air kerma entry box or into \nthe number of projections box",
|
|
font=("Roman", font_size),
|
|
).pack()
|
|
|
|
else:
|
|
customtkinter.CTkLabel(
|
|
pop_up,
|
|
text="Please enter only numbers into the air kerma and ensure \nthat a numeric value is placed into the number of projections",
|
|
font=("Roman", font_size),
|
|
).pack()
|
|
|
|
except TypeError:
|
|
pop_up = customtkinter.CTkToplevel()
|
|
|
|
customtkinter.CTkLabel(
|
|
pop_up,
|
|
text="Please enter an incident spectrum",
|
|
font=("Roman", font_size),
|
|
).pack()
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"--font_size_set",
|
|
nargs="?",
|
|
default=1,
|
|
type=int,
|
|
help="select the font size set (default=1 is the first set)",
|
|
)
|
|
args = parser.parse_args()
|
|
font_size = dict_font_size_set[args.font_size_set]
|
|
font_size = dict_font_size_set[args.font_size_set]
|
|
|
|
customtkinter.set_appearance_mode("light")
|
|
root = customtkinter.CTk()
|
|
root.title("BCT Dose Calculator")
|
|
CT_dose = Main_Window(root)
|
|
root.protocol("WM_DELETE_WINDOW", quit_me)
|
|
root.mainloop()
|
|
|