"""
Frame for managing molecule parameters in the molecules.yaml file.
This module handles the editing and addition of molecules in the
molecules.yaml configuration file.
"""
from pathlib import Path
import yaml
from tkinter import messagebox
from GUIBRUSHR.GUI.LAYOUT.MyPanel import MyPanel
from GUIBRUSHR.GUI.WIDGET.MyButton import MyButton
from GUIBRUSHR.GUI.WIDGET.MyDropDown import MyDropdown
from GUIBRUSHR.GUI.WIDGET.MyTextField import MyTextField
from GUIBRUSHR.General_Constants.Classes.HelpButton import HelpButton
[docs]
class FrameParams(MyPanel):
"""
A panel for managing molecule parameters in the molecules.yaml file.
This class provides functionality to:
- View existing molecule parameters (name, label)
- Modify molecule parameters
- Add new molecules to the configuration
Note: multi is always set to 0 automatically
"""
[docs]
def __init__(self, parent, color, row, column, path_default, **kwargs):
"""Initialize the FrameParams panel with all necessary components."""
super().__init__(parent, color, row, column, **kwargs)
# Store instance variables
self.dropdown_molecule = None
self.path_default = path_default
self.molecs_file = str(Path(path_default, "GUIBRUSHR", "Files", "Configuration_Yaml", "molecules.yaml"))
self.color = color
self.molecules_dict = {}
self.molecule_list = []
self.current_molecule = None
# Load molecules from molecules.yaml
self._load_molecules()
# Initialize panels
self._init_panels()
# Initialize controls for editing molecules
self._init_edit_controls()
# Initialize controls for adding new molecules
self._init_add_controls()
def _init_panels(self):
"""Initialize the main panel columns."""
self.column1 = MyPanel(self, self.color, 0, 1)
self.column2 = MyPanel(self, self.color, 0, 2)
def _init_edit_controls(self):
"""Initialize controls for editing existing molecules."""
# Help text dictionary
help_text = {
"Molecule (dropdown)": "Select an existing molecule from the molecules.yaml file to view and edit its parameters. This dropdown shows all molecules currently defined in the molecules.yaml.",
"Name": "The internal name identifier for the molecule. This should match the key in the molecules.yaml file and correspond to the molecule name used in petitRADTRANS opacity files.",
"Label": "LaTeX-formatted label string for display in plots and GUI. Use raw string notation with LaTeX math mode, e.g., r'$log_{10} (vmr\\,H_2O)$' for H₂O volume mixing ratio. Note: multi is always set to 0 automatically."
}
# Create help button
self.help_button_edit = HelpButton(
self.column1, 0, 0,
"Molecule Parameters Help",
help_text,
columnspan=1
)
self.help_button_edit.button.grid(rowspan=3)
# Molecule dropdown
molecule_list = list(self.molecules_dict.keys())
self.dropdown_molecule = MyDropdown(
self.column1, 0, 1, molecule_list, "Molecule:",
color=self.color, columnspan=2
)
self.dropdown_molecule.set_callback(self._on_molecule_selected)
# Text fields for molecule parameters
self.textfield_name = MyTextField(
self.column1, 1, 1, "", "Name:", width=30, columnspan=2
)
self.textfield_label = MyTextField(
self.column1, 2, 1, "", "Label:", width=30, columnspan=2
)
self._on_molecule_selected()
# Modify button
self.button_modify = MyButton(
self.column1, 3, 1, r'Modify Molecule',
bg="#1e4fda", color_panel=self.color, command=self.modify_molecule
)
def _init_add_controls(self):
"""Initialize controls for adding new molecules."""
# Help text dictionary for adding molecules
help_text_add = {
"Molecule Key": "The unique key identifier for the new molecule. This will be used as the dictionary key in molecules.yaml file. Use standard chemical notation (e.g., 'H2O', 'CO2', 'CH4').",
"Name": "The internal name identifier for the new molecule. Typically matches the key and corresponds to the molecule name in petitRADTRANS opacity files.",
"Label": "LaTeX-formatted label string for display. Use raw string notation with LaTeX math mode, e.g., r'$log_{10} (vmr\\,H_2O)$'. Escape special characters properly. Note: multi is always set to 0 automatically."
}
# Create help button for add panel
self.help_button_add = HelpButton(
self.column2, 0, 0,
"Add New Molecule Help",
help_text_add,
columnspan=1
)
self.help_button_add.button.grid(rowspan=3)
# Text fields for new molecule
self.textfield_new_key = MyTextField(
self.column2, 1, 1, "", "Molecule Key:", width=30, columnspan=2
)
self.textfield_new_name = MyTextField(
self.column2, 2, 1, "", "Name:", width=30, columnspan=2
)
self.textfield_new_label = MyTextField(
self.column2, 3, 1, "", "Label:", width=30, columnspan=2
)
# Add button
self.button_add = MyButton(
self.column2, 4, 1, r'Add New Molecule',
bg="#1e4fda", color_panel=self.color, command=self.add_molecule
)
def _load_molecules(self):
"""Load molecules from the molecules.yaml file."""
try:
with open(self.molecs_file, 'r') as f:
# Use safe_load to load the YAML content
self.molecules_dict = yaml.safe_load(f)
except Exception as e:
messagebox.showerror(
"Error Loading File",
f"Failed to load molecules.yaml: {str(e)}"
)
self.molecules_dict = {}
def _on_molecule_selected(self):
"""Handle molecule selection from dropdown."""
selected = self.dropdown_molecule.get_value()
if selected and selected in self.molecules_dict:
self.current_molecule = selected
mol_data = self.molecules_dict[selected]
# Update text fields with molecule data
self.textfield_name.insert_text(str(mol_data.get('name', '')))
self.textfield_label.insert_text(str(mol_data.get('label', '')))
[docs]
def modify_molecule(self):
"""Modify the selected molecule's parameters in molecules.yaml."""
try:
# Get current selection
selected = self.dropdown_molecule.get_value()
if not selected:
messagebox.showwarning(
"No Selection",
"Please select a molecule to modify"
)
return
# Get new values from text fields
new_name = self.textfield_name.get_text()
new_label = self.textfield_label.get_text()
# Validate inputs
if not new_name or not new_label:
messagebox.showwarning(
"Missing Values",
"Please fill in all fields"
)
return
# Multi is always set to 0
new_multi = 0
# Read the entire YAML file
with open(self.molecs_file, 'r') as f:
content = yaml.safe_load(f)
content[selected] = {
'name': new_name,
'multi': new_multi,
'label': new_label
}
# Write back to file, preserving structure
with open(self.molecs_file, 'w') as f:
yaml.dump(content, f, default_flow_style=False, allow_unicode=True)
# Update internal dictionary
self.molecules_dict[selected] = {
'name': new_name,
'multi': new_multi,
'label': new_label
}
messagebox.showinfo(
"Success",
f"Molecule '{selected}' updated successfully"
)
except Exception as e:
messagebox.showerror(
"Error",
f"Failed to modify molecule: {str(e)}"
)
[docs]
def add_molecule(self):
"""Add a new molecule to the molecules.yaml file."""
try:
# Get values from text fields
new_key = self.textfield_new_key.get_text()
new_name = self.textfield_new_name.get_text()
new_label = self.textfield_new_label.get_text()
# Validate inputs
if not new_key or not new_name or not new_label:
messagebox.showwarning(
"Missing Values",
"Please fill in all fields"
)
return
# Multi is always set to 0
new_multi = 0
# Check if molecule already exists
if new_key in self.molecules_dict:
messagebox.showwarning(
"Duplicate Key",
f"Molecule '{new_key}' already exists. Use modify instead."
)
return
# Read the entire YAML file
with open(self.molecs_file, 'r') as f:
content = yaml.safe_load(f)
content[new_key] = {
'name': new_name,
'multi': new_multi,
'label': new_label
}
# Write back to file
with open(self.molecs_file, 'w') as f:
yaml.dump(content, f, default_flow_style=False, allow_unicode=True)
# Update internal dictionary
self.molecules_dict[new_key] = {
'name': new_name,
'multi': new_multi,
'label': new_label
}
# Update dropdown with new molecule
self.molecule_list = list(self.molecules_dict.keys())
self.modify_list_molecule(self.molecule_list)
# Clear the add form
self.textfield_new_key.insert_text("")
self.textfield_new_name.insert_text("")
self.textfield_new_label.insert_text("")
messagebox.showinfo(
"Success",
f"Molecule '{new_key}' added successfully"
)
except Exception as e:
messagebox.showerror(
"Error",
f"Failed to add molecule: {str(e)}"
)
[docs]
def modify_list_molecule(self, molecule_list):
"""Update the molecule list dropdown with new molecules."""
self.dropdown_molecule.clean_widget()
self.dropdown_molecule = MyDropdown(
self.column1, 0, 1, molecule_list,
"Molecule:", color=self.color, columnspan=2
)
# Re-bind the selection event after recreating dropdown
self.dropdown_molecule.set_callback(self._on_molecule_selected)
self.molecule_list = molecule_list