|
|
|
|
| """
|
| 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()
|
|
|