Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .codex/environments/environment.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
version = 1
name = "astra_gui"

[setup]
script = ""

[[actions]]
name = "Run"
icon = "run"
command = "hatch run all"
4 changes: 2 additions & 2 deletions src/astra_gui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ def menu(self) -> None:

def get_process_from_notebooks(self, action: str, *args, **kwargs) -> None:
"""Get action from all the notebooks."""
for _notebook in self.notebooks:
getattr(_notebook, action)(*args, **kwargs)
for notebook in self.notebooks:
getattr(notebook, action)(*args, **kwargs)

@log_operation('erasing in all notebooks')
def erase(self) -> None:
Expand Down
5 changes: 4 additions & 1 deletion src/astra_gui/close_coupling/bsplines.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
missing_required_calculation_popup,
missing_required_file_popup,
required_field_popup,
save_success_popup,
warning_popup,
)
from astra_gui.utils.required_fields_module import RequiredFields
Expand Down Expand Up @@ -170,7 +171,7 @@ def erase(self) -> None:
self.num_bspline_entry.delete(0, tk.END)
self.erase_plot_parameters()

def save(self) -> None:
def save(self, show_popup: bool = True) -> None:
"""Validate the current configuration and write all required input files."""
# Saving EXTERNAL_BASIS_BSPLINES.INP

Expand Down Expand Up @@ -344,6 +345,8 @@ class PlotRequiredFields(RequiredFields):

if self.path_exists(self.PRISM_FOLDER):
self.remove_path(self.PRISM_FOLDER)
if show_popup:
save_success_popup(f'{self.label} inputs saved successfully.')

def load(self) -> None:
"""Populate the form from existing Astra or PRISM files."""
Expand Down
10 changes: 8 additions & 2 deletions src/astra_gui/close_coupling/clscplng.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
import numpy as np

from astra_gui.utils.font_module import title_font
from astra_gui.utils.popup_module import invalid_input_popup, missing_required_calculation_popup, required_field_popup
from astra_gui.utils.popup_module import (
invalid_input_popup,
missing_required_calculation_popup,
required_field_popup,
save_success_popup,
)
from astra_gui.utils.required_fields_module import RequiredFields
from astra_gui.utils.scrollable_module import ScrollableFrame

Expand Down Expand Up @@ -224,9 +229,10 @@ class ClscplngRequiredFields(RequiredFields):

bsplines_page = cast('Bsplines', self.notebook.pages[4])
if self.path_exists(bsplines_page.BSPLINES_INPUT_FILE):
bsplines_page.save()
bsplines_page.save(show_popup=False)

self.get_cc_data()
save_success_popup(f'{self.label} inputs saved successfully.')

def load(self) -> None:
"""Populate the form based on an existing CLSCPLNG.INP file."""
Expand Down
2 changes: 2 additions & 0 deletions src/astra_gui/close_coupling/dalton.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from tkinter import ttk
from typing import TYPE_CHECKING, cast

from astra_gui.utils.popup_module import save_success_popup
from astra_gui.utils.required_fields_module import RequiredFields

from .cc_notebook_page_module import CcNotebookPage
Expand Down Expand Up @@ -356,6 +357,7 @@ class DaltonRequiredFields(RequiredFields):
self.notebook.dalton_data[occ_option] = ''

self.save_file(self.DALTON_FILE, self.notebook.dalton_data, '!', blank_lines=False)
save_success_popup(f'{self.label} inputs saved successfully.')

def erase(self) -> None:
"""Reset Dalton form fields to their defaults."""
Expand Down
7 changes: 5 additions & 2 deletions src/astra_gui/close_coupling/lucia.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
missing_output_popup,
missing_required_calculation_popup,
required_field_popup,
save_success_popup,
warning_popup,
)
from astra_gui.utils.required_fields_module import RequiredFields
Expand Down Expand Up @@ -292,7 +293,7 @@ def get_new_dimension_requirement() -> int:

error_string += '\nUpdating LUCIA.INP and running Hamiltonian calculation again.'

self.save()
self.save(show_popup=False)

if self.sa_var.get():
self.run_astra_setup('-run eht --sa', 'State average')
Expand Down Expand Up @@ -758,7 +759,7 @@ def get_title(self) -> str:

return '\n'.join(title_lines)

def save(self) -> None:
def save(self, show_popup: bool = True) -> None:
"""Validate the Lucia form and update the associated input files."""

@dataclass
Expand Down Expand Up @@ -812,6 +813,8 @@ class LuciaRequiredFields(RequiredFields):
commands['lz2'] = 'LZ2'

self.save_file(self.LUCIA_FILE, commands, '*', blank_lines=False)
if show_popup:
save_success_popup(f'{self.label} inputs saved successfully.')

def save_sa(self) -> None:
"""Persist the state-averaged Lucia configuration to disk."""
Expand Down
3 changes: 2 additions & 1 deletion src/astra_gui/close_coupling/molecule.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from astra_gui.utils.font_module import title_font
from astra_gui.utils.notebook_module import NotebookPage
from astra_gui.utils.popup_module import required_field_popup
from astra_gui.utils.popup_module import required_field_popup, save_success_popup
from astra_gui.utils.symmetry_module import Symmetry
from astra_gui.utils.table_module import Table

Expand Down Expand Up @@ -334,6 +334,7 @@ def save(self) -> None:
)
self.notebook.molecule_data['number_atoms'] = np.shape(self.get_all_atoms()[1])[0]
self.notebook.molecule_data['linear_molecule'] = self.is_molecule_linear()
save_success_popup(f'{self.label} inputs saved successfully.')

def plot_molecule(self) -> None:
"""Render the molecule via Molden if the environment supports it."""
Expand Down
3 changes: 2 additions & 1 deletion src/astra_gui/time_dependent/pulse.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from astra_gui.utils.font_module import bold_font
from astra_gui.utils.notebook_module import NotebookPage
from astra_gui.utils.popup_module import invalid_input_popup, warning_popup
from astra_gui.utils.popup_module import invalid_input_popup, save_success_popup, warning_popup
from astra_gui.utils.required_fields_module import RequiredFields
from astra_gui.utils.table_module import Table

Expand Down Expand Up @@ -1153,6 +1153,7 @@ def save(self) -> None:

for pulse_name, pulse_tabulation_data in pulse_tabulation.items():
self.save_file_from_blank(Path(f'{pulse_name}.DAT'), pulse_tabulation_data)
save_success_popup(f'{self.label} inputs saved successfully.')

def print_irrep(self, _new_sym: bool = False) -> None:
"""Relay symmetry changes to both pulse configuration frames."""
Expand Down
28 changes: 18 additions & 10 deletions src/astra_gui/time_independent/structural.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,15 @@ def get_commands(self) -> str:

susc_dp_length_vals = ','.join([self.dp_labels[ind] for ind in range(3) if susc_dp_vals[ind]])
lines.extend([
f'astraDipoleTransition -gif ASTRA.INP -op {susc_dp_length_vals} '
f'-ketsym {susc_entries[0]} -trans bb {cap_text}',
f'astraSusceptibility -gif ASTRA.INP -op {susc_dp_length_vals} '
f'-ketsym {susc_entries[0]} -trans bb -emin {susc_entries[1]} '
f'-emax {susc_entries[2]} -ne {susc_entries[3]} {cap_text}',
(
f'astraDipoleTransition -gif ASTRA.INP -op {susc_dp_length_vals} '
f'-ketsym {susc_entries[0]} -trans bb {cap_text}'
),
(
f'astraSusceptibility -gif ASTRA.INP -op {susc_dp_length_vals} '
f'-ketsym {susc_entries[0]} -trans bb -emin {susc_entries[1]} '
f'-emax {susc_entries[2]} -ne {susc_entries[3]} {cap_text}'
),
])

if any(susc_dp_vals[3:]):
Expand All @@ -579,11 +583,15 @@ def get_commands(self) -> str:

susc_dp_velocity_vals = ','.join([self.dp_labels[ind] for ind in range(3, 6) if susc_dp_vals[ind]])
lines.extend([
f'astraDipoleTransition -gif ASTRA.INP -op {susc_dp_velocity_vals} '
f'-ketsym {susc_entries[0]} -trans bb {cap_text}',
f'astraSusceptibility -gif ASTRA.INP -op {susc_dp_velocity_vals} '
f'-ketsym {susc_entries[0]} -trans bb -emin {susc_entries[1]} '
f'-emax {susc_entries[2]} -ne {susc_entries[3]} {cap_text}',
(
f'astraDipoleTransition -gif ASTRA.INP -op {susc_dp_velocity_vals} '
f'-ketsym {susc_entries[0]} -trans bb {cap_text}'
),
(
f'astraSusceptibility -gif ASTRA.INP -op {susc_dp_velocity_vals} '
f'-ketsym {susc_entries[0]} -trans bb -emin {susc_entries[1]} '
f'-emax {susc_entries[2]} -ne {susc_entries[3]} {cap_text}'
),
])

return self.add_idle_thread_and_join_lines(lines)
Expand Down
3 changes: 2 additions & 1 deletion src/astra_gui/time_independent/ti_notebook_page_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from astra_gui.utils.font_module import bold_font
from astra_gui.utils.notebook_module import NotebookPage
from astra_gui.utils.popup_module import missing_symmetry_popup, required_field_popup
from astra_gui.utils.popup_module import missing_symmetry_popup, required_field_popup, save_success_popup
from astra_gui.utils.scrollable_module import ScrollableFrame, ScrollableTreeview

if TYPE_CHECKING:
Expand Down Expand Up @@ -242,6 +242,7 @@ def save(self) -> None:
return

self.save_script(self.SCRIPT_FILE, commands, f'{self.label} calculation', convert_cs_irreps=True)
save_success_popup(f'{self.label} inputs saved successfully.')
Comment on lines 244 to +245
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

save_success_popup is shown unconditionally after save_script(...), but save_script can return early without writing anything (e.g., when idle_processor_popup(...) is declined). This can lead to a false “inputs saved successfully” confirmation. Consider having save_script return a boolean indicating whether it actually saved, or restructuring so the success popup is only shown after the save definitely occurred.

Suggested change
self.save_script(self.SCRIPT_FILE, commands, f'{self.label} calculation', convert_cs_irreps=True)
save_success_popup(f'{self.label} inputs saved successfully.')
saved = self.save_script(
self.SCRIPT_FILE,
commands,
f'{self.label} calculation',
convert_cs_irreps=True,
)
if saved:
save_success_popup(f'{self.label} inputs saved successfully.')

Copilot uses AI. Check for mistakes.

def run(self) -> None:
"""Execute the notebook script using the shared runner."""
Expand Down
8 changes: 6 additions & 2 deletions src/astra_gui/utils/notebook_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
invalid_input_popup,
missing_script_file_popup,
required_field_popup,
started_calculation_popup,
)
from .symmetry_module import Symmetry

Expand Down Expand Up @@ -316,8 +317,7 @@ def get_cpu_stats_data() -> list[str]:
lines = stdout.split('\n')
else:
try:
with Path(proc_stat_file).open('r') as f:
lines = f.read().split('\n')
lines = Path(proc_stat_file).read_text().split('\n')
except FileNotFoundError:
if system() == 'Darwin':
logger.warning(
Expand Down Expand Up @@ -577,6 +577,10 @@ def check_programs_helper(script_name: str) -> None:
return
if not run[0]:
return
self.controller.after(
0,
lambda: started_calculation_popup(f'{script_name} started running.'),
)
Comment on lines +580 to +583
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

started_calculation_popup is scheduled before verifying that the background launch command actually succeeded. If the local nohup subprocess returns a non-zero code or the remote run_remote_command returns exit_code != 0, the user will still see “started running”, which is inaccurate. Consider moving this popup to after the launch succeeds (after checking result.returncode == 0 locally / exit_code == 0 remotely), or changing the message to something like “launch requested” and only showing “started” on success.

Copilot uses AI. Check for mistakes.

if self.ssh_client:
remote_command = (
Expand Down
10 changes: 10 additions & 0 deletions src/astra_gui/utils/popup_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ def completed_calculation_popup(message: str) -> None:
messagebox.showinfo('Completed Calculation!', message)


def started_calculation_popup(message: str) -> None:
"""Display a start message for long-running calculations."""
messagebox.showinfo('Calculation Started', message)


def save_success_popup(message: str) -> None:
"""Display a confirmation message after saving files."""
messagebox.showinfo('Files Saved', message)

Comment on lines +91 to +99
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New popup helpers started_calculation_popup and save_success_popup were added here, but the existing popup wrapper test suite (see tests/test_popup_module.py) enumerates the popup functions it expects to forward to the messagebox spy. Please extend/update that test coverage to include these new wrappers so regressions (wrong messagebox method, title, etc.) are caught.

Copilot uses AI. Check for mistakes.

def missing_script_file_popup(name: str) -> None:
"""Inform that a required script file has not been saved yet."""
messagebox.showerror('Missing script file!', f'Please save the script for {name} before running!')
Expand Down