import ttkbootstrap as ttk
from ttkbootstrap.constants import *
import tkinter as tk
from tkinter import filedialog
import logging
from NekUpload.frontend.components.help import HelpNotification
from NekUpload.frontend.components.scrollbox import ScrolledListbox,UploadingScrolledListBox
from NekUpload.frontend import style_guide
from NekUpload.NekData.data_type import SolverType
from NekUpload.manager import NekManager
[docs]
class FileUploadFrame(ttk.LabelFrame):
def __init__(self,parent):
super().__init__(
master=parent,
text="Simulation Dataset (Excluding Geometry)",
bootstyle=DANGER,
padding=10
)
self.columnconfigure(0,weight=1)
self.columnconfigure(1,weight=1)
self.rowconfigure(0,weight=1)
self.rowconfigure(1,weight=1)
self.rowconfigure(2,weight=1)
self.rowconfigure(3,weight=1)
self.rowconfigure(4,weight=1)
label = ttk.Label(
master=self,
text="Upload all Nektar session files and output files related files here.",
font=("TKDefaultFont", 12),
anchor="w",
justify="left",
)
label.grid(row=0,column=0,columnspan=2,pady=2,sticky=(NSEW))
basic_info_frame = self.basic_info_block()
basic_info_frame.grid(row=1,column=0,sticky=NSEW)
n = ttk.Notebook(self,bootstyle=SECONDARY)
n.grid(row=2,column=0,columnspan=4,sticky=NSEW)
f1: ttk.Frame = self._upload_input_frame(n)
f2: ttk.Frame = self._upload_output_frame(n)
n.add(f1,text="Inputs")
n.add(f2,text="Outputs")
"""f2: ttk.Frame = self._link_geometry_to_existing_record(n)
n.add(f2,text="Link to Existing Record")
"""
[docs]
def basic_info_block(self) -> ttk.Frame:
frame = ttk.Frame(master=self)
frame.columnconfigure(0,weight=1)
frame.columnconfigure(1,weight=1)
frame.rowconfigure(0,weight=1)
frame.rowconfigure(1,weight=1)
#title of dataset
title_label = ttk.Label(master=frame,text="Dataset Title: ",bootstyle=PRIMARY,padding=10,anchor="w")
title_label.grid(row=0,column=0,sticky=W)
self._title = tk.StringVar()
self.title_entry = ttk.Entry(master=frame,textvariable=self._title,bootstyle=PRIMARY)
self.title_entry.grid(row=0,column=1,sticky=EW)
#combobox for solver type
solver_label = ttk.Label(master=frame,text="Solver Type: ",bootstyle=PRIMARY,padding=10,anchor="w")
solver_label.grid(row=1,column=0,sticky=W)
solver_type_list: list[str] = [solver.value for solver in SolverType]
#provide option to auto-detect from file
self.AUTODETECT_SOLVER = "Auto-Detect Solver From File"
solver_type_list.insert(0, self.AUTODETECT_SOLVER)
self.solver_type_combobox = ttk.Combobox(master=frame,
values=solver_type_list,
state="readonly")
self.solver_type_combobox.set(solver_type_list[0])#set default as autodetect
self.solver_type_combobox.grid(row=1,column=1,sticky=EW)
# Event bindings
self.title_entry.bind(
"<FocusOut>",
lambda event: style_guide.highlight_mandatory_entry_on_focus_out(self.title_entry)
)
self.title_entry.bind(
"<FocusIn>",
lambda event: style_guide.highlight_mandatory_entry_on_focus_in(self.title_entry)
)
return frame
def _upload_input_frame(self, parent) -> ttk.Frame:
frame = ttk.Frame(master=parent)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=3)
frame.columnconfigure(2, weight=1)
frame.columnconfigure(3, weight=1)
descriptor = ttk.Label(
frame,
text=(
"Files such as session file, boundary condition files and "
"function forcing files should be uploaded here. Note that "
"although geometry file is part of input, it is uploaded separately."
)
)
descriptor.grid(row=0, column=0, columnspan=4, sticky=W) # Span all columns
# Ask for session file
session_file_label = ttk.Label(
master=frame,
text="Select Session File: ",
justify="left"
)
session_file_label.grid(row=1, column=0, sticky=W)
self._session_file = tk.StringVar()
self.session_file_entry = ttk.Entry(
master=frame,
textvariable=self._session_file,
)
self.session_file_entry.grid(row=1, column=1, sticky=EW)
# Event bindings
self.session_file_entry.bind(
"<FocusOut>",
lambda event: style_guide.highlight_mandatory_entry_on_focus_out(self.session_file_entry)
)
self.session_file_entry.bind(
"<FocusIn>",
lambda event: style_guide.highlight_mandatory_entry_on_focus_in(self.session_file_entry)
)
def browse_file():
file_path = filedialog.askopenfilename(
title="Select Session File",
filetypes=(("Session File",(".xml",)),)
)
self._session_file.set(file_path)
browse = ttk.Button(
master=frame,
text="Browse Files",
command=browse_file
)
browse.grid(row=1,column=2,sticky=NSEW)
help_logo:ttk.Label = HelpNotification(frame)
help_logo.add_help_message("XML session file expected here. Other files should be placed under supporting")
help_logo.grid(row=1,column=4,sticky=W)
######
extra_files_frame = ttk.Frame(master=frame)
extra_files_frame.grid(row=2,column=0,columnspan=4,sticky=NSEW)
extra_files_frame.columnconfigure(0,weight=1)
extra_files_frame.columnconfigure(1,weight=1)
extra_files_frame.columnconfigure(2,weight=1)
extra_files_frame.rowconfigure(0,weight=1)
self.boundary_conditions_frame = UploadingScrolledListBox(extra_files_frame,"Boundary Conditions Files",(
("Boundary Conditions Files",("*.bc","*.bce")),("Other Files",("*"))))
self.boundary_conditions_frame.grid(row=0,column=0,sticky=NSEW)
self.function_frame = UploadingScrolledListBox(extra_files_frame,"Function Files",(("Function File","*"),))
self.function_frame.grid(row=0,column=1,sticky=NSEW)
self.input_supporting_files_frame = UploadingScrolledListBox(extra_files_frame,"Supporting Files")
self.input_supporting_files_frame.grid(row=0,column=2,sticky=NSEW)
return frame
def _upload_output_frame(self, parent) -> ttk.Frame:
frame = ttk.Frame(master=parent)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=3)
frame.columnconfigure(2, weight=1)
frame.columnconfigure(3, weight=1)
descriptor = ttk.Label(
frame,
text=(
"Files such as output field file, output checkpoint files,"
"history files and other outputted files should be uploaded here."
"If you have other images and pdfs that will support the record," \
"please also upload under the supporting section."
)
)
descriptor.grid(row=0, column=0, columnspan=4, sticky=W) # Span all columns
# Ask for session file
output_file_label = ttk.Label(
master=frame,
text="Select Output Field File: ",
justify="left"
)
output_file_label.grid(row=1, column=0, sticky=W)
self._output_file = tk.StringVar()
self.output_file_entry = ttk.Entry(
master=frame,
textvariable=self._output_file,
)
self.output_file_entry.grid(row=1, column=1, sticky=EW)
# Event bindings
self.output_file_entry.bind(
"<FocusOut>",
lambda event: style_guide.highlight_mandatory_entry_on_focus_out(self.output_file_entry)
)
self.output_file_entry.bind(
"<FocusIn>",
lambda event: style_guide.highlight_mandatory_entry_on_focus_in(self.output_file_entry)
)
def browse_file():
file_path = filedialog.askopenfilename(
title="Select Output File",
filetypes=(("Output File",(".fld",)),)
)
self._output_file.set(file_path)
browse = ttk.Button(
master=frame,
text="Browse Files",
command=browse_file
)
browse.grid(row=1,column=2,sticky=NSEW)
help_logo:ttk.Label = HelpNotification(frame)
help_logo.add_help_message("HDF5 output file expected here. Other files should be placed under supporting")
help_logo.grid(row=1,column=4,sticky=W)
###########
extra_files_frame = ttk.Frame(master=frame)
extra_files_frame.grid(row=2,column=0,columnspan=4,sticky=NSEW)
extra_files_frame.columnconfigure(0,weight=1)
extra_files_frame.columnconfigure(1,weight=1)
extra_files_frame.columnconfigure(2,weight=1)
extra_files_frame.rowconfigure(0,weight=1)
self.checkpoint_files_frame = UploadingScrolledListBox(extra_files_frame,"Checkpoint Files",(("Checkpoint Files","*.chk"),))
self.checkpoint_files_frame.grid(row=0,column=0,sticky=NSEW)
self.filter_files_frame = UploadingScrolledListBox(extra_files_frame,"Filter Files",(
("AeroForces","*.fce"),("History","*.his"),
("Other Filter Files","*")))
self.filter_files_frame.grid(row=0,column=1,sticky=NSEW)
self.output_supporting_files_frame = UploadingScrolledListBox(extra_files_frame,"Supporting Files")
self.output_supporting_files_frame.grid(row=0,column=2,sticky=NSEW)
return frame
[docs]
def add_error_style_to_mandatory_entries(self):
if not self.title_entry.get():
style_guide.show_error_in_entry(self.title_entry)
if not self.session_file_entry.get():
style_guide.show_error_in_entry(self.session_file_entry)
if not self.output_file_entry.get():
style_guide.show_error_in_entry(self.output_file_entry)
@property
def session_file_name(self) -> str:
return self._session_file.get()
@property
def output_file_name(self) -> str:
return self._output_file.get()
@property
def dataset_title(self) -> str:
return self._title.get()
@property
def checkpoint_filename_list(self) -> list[str]:
return self.checkpoint_files_frame.filename_list
@property
def filter_filename_list(self) -> list[str]:
return self.filter_files_frame.filename_list
@property
def output_supporting_filename_list(self) -> list[str]:
return self.output_supporting_files_frame.filename_list
@property
def boundary_condition_file_name_list(self) -> list[str]:
return self.boundary_conditions_frame.filename_list
@property
def function_file_name_list(self) -> list[str]:
return self.function_frame.filename_list
@property
def input_supporting_filename_list(self) -> list[str]:
return self.input_supporting_files_frame.filename_list
@property
def solver_type(self) -> SolverType:
if self.solver_type_combobox.get() == self.AUTODETECT_SOLVER:
try:
solver = NekManager.detect_solver_type(self.session_file_name)
logging.info(f"Auto-detecting solver from {self.session_file_name}: {solver}")
return solver
except Exception as e:
logging.error(f"Auto-detecting solver from {self.session_file_name} failed: {solver} \n Please manually specify")
else:
return SolverType(self.solver_type_combobox.get())