Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pli checker #37

Merged
merged 2 commits into from
Nov 7, 2024
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
35 changes: 21 additions & 14 deletions tugui/gui_widgets.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
Expand All @@ -11,7 +12,8 @@ class EntryVariable:
Class defining a variable having a corresponding Entry object. Its value
is validated when set.
"""
def __init__(self, frame: tk.Frame, width: int, col: int, row: int, end: str) -> None:
def __init__(self, frame: tk.Frame, width: int, col: int, row: int,
end: str, validation: Callable) -> None:
"""
Constructor requiring the Frame object onto which putting the Entry.
The Entry width, as well as the column and row indices are passed to
Expand All @@ -26,35 +28,40 @@ def __init__(self, frame: tk.Frame, width: int, col: int, row: int, end: str) ->

# Register the validation funtion of the Entry widget
valid_entry = (self.entry.register(self.validate), '%P')
self.validation_func: Callable = validation
# Configure the entry for checking the validity of its content when the
# widget looses focus
self.entry.configure(validate='focusout', validatecommand=valid_entry)

# Entry file extension
self.entry_extension: str = end

def validate(self, event: Union[tk.Event, None] = None, newval: str = "") -> bool:
def validate(self, entry_txt: str = "") -> bool:
"""
Method that checks if the entry is valid. The "end" parameter indicates the
extension to check against.
Method that checks if the entry is valid.
"""
# Check the entry value against the allowed extension
if re.match(r"^.*\." + self.entry_extension + "$", newval) is not None:
# The entry is valid if a match is found
# Return if the entry is empty
if not entry_txt:
return True
# Check the entry value according to the validation function
try:
self.validation_func(entry_txt, self.entry_extension)
# FIXME: to add the following message into the log file
print("The entry is valid!")
self.entry.configure(foreground="#343638")
# Generate a virtual event stating that the entry is valid
self.entry.event_generate("<<Valid-Entry>>")
return True
else:
# If no match is found, handle the invalid case only if the entry value is not empty
if newval != "":
self.on_invalid()
return False
except Exception as e:
# Handle the invalid case
self.on_invalid(str(e))
return False

def on_invalid(self) -> None:
def on_invalid(self, error_message: str) -> None:
"""
Show the error message if the data is not valid.
"""
error_message = "The entry is not valid: please provide a path to a file with the valid \"" + self.entry_extension + "\" extension!"
# FIXME: to add the following message to the log file
print(error_message)
# Highlight the entry color in red
self.entry.configure(foreground="red")
Expand Down
60 changes: 39 additions & 21 deletions tugui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from tu_interface import DatGenerator, InpHandler, MicReader, PliReader, StaReader, TuInp, MacReader
from gui_configuration import GuiPlotFieldsConfigurator
from gui_widgets import CustomNotebook, EntryVariable, StatusBar, provide_label_image
from support import IANT, ERROR_LEVEL, OS_PLATFORM, SUPPORTED_OS_PLATFORMS, ICON_PATH
from support import IANT, ERROR_LEVEL, OS_PLATFORM, SUPPORTED_OS_PLATFORMS, ICON_PATH, check_file_extension_and_existence
from shutil import copyfile
from typing import Union
from sv_ttk import set_theme
Expand Down Expand Up @@ -107,7 +107,9 @@ def __init__(self, window_title: str, width: int, height: int) -> None:
# Instantiate a label for the .pli file entry
ttk.Label(mainframe, text="Path to the .pli input file").grid(column=0, row=0, sticky='ew')
# Instantiate the field holding the path to the .pli input file
self.pli_entry: EntryVariable = EntryVariable(mainframe, 50, col=1, row=0, end="pli")
self.pli_entry: EntryVariable = EntryVariable(
mainframe, 50, col=1, row=0, end="pli",
validation=check_file_extension_and_existence)
# Put a button next the the entry for allowing the selection of the .pli file to open
ttk.Button(mainframe, # width = 80,
text="Choose file",
Expand Down Expand Up @@ -146,7 +148,7 @@ def __init__(self, window_title: str, width: int, height: int) -> None:
# Bind Ctrl+Q to the request of quitting the application
self.bind("<Control-q>", func=self.quit_app)
# Bind the entry "FocusOut" event to the method that retrieves the information from the input .pli file
self.pli_entry.entry.bind("<FocusOut>", func=lambda event: self.retrieve_simulation_info())
self.pli_entry.entry.bind("<<Valid-Entry>>", func=lambda event: self.retrieve_simulation_info())
# Bind the activation of the plot tabs to a valid set of the .pli entry field. If this file shows a value of 1
# for the ISTATI field, both TuPlot and TuStat areas are enabled. If not, only the TuPlot one is available.
# In the first case, the "ActivateAllTabs" event activates both tabs; in the second case, the "ActivateTuPlotTab"
Expand All @@ -167,7 +169,7 @@ def __check_OS_platform(self) -> None:
"""
Method that checks whether the OS platform is supported:
for the time being, only Windows and Linux OSs are supported.
"""
"""
if OS_PLATFORM not in SUPPORTED_OS_PLATFORMS:
error_message = f"The {OS_PLATFORM} OS is not yet supported!"
messagebox.showerror("Error", error_message)
Expand Down Expand Up @@ -399,6 +401,14 @@ def retrieve_simulation_info(self) -> None:
self.plireader = PliReader.init_PliReader(self.pli_entry.var.get())
print("Path to the .pli file: " + self.plireader.pli_path)

# Provide a message to the status bar and to the log file
output_message = "Selected .pli file: " + self.plireader.pli_path
self.status_bar.set_text(output_message)
# FIXME: to print into log file
print(output_message)
# Set the initial and output directories
self.__set_dirs(self.plireader.pli_path)

# Instantiate the MacReader class
self.macreader = MacReader(
os.path.dirname(self.plireader.pli_path) + os.sep + self.plireader.mac_path,
Expand Down Expand Up @@ -778,23 +788,7 @@ def __select_file_and_fill_entry(self, entry: ttk.Entry, fileToSearch: str, form
# and retrieve the path of the selected file.
filename = self.__select_file(fileToSearch, format)

# Do nothing if no file has been selected
if not filename: return

# Update the default directory of the file selection window to the one of the currently opened file
self.__initial_dir = os.path.dirname(filename)

# If not already done, set the output directory to the one of the currently opened file
if not self.__output_dir:
self.__output_dir = self.__initial_dir

# Provide a message to the status bar and to the log file
output_message = "Selected .pli file: " + filename
self.status_bar.set_text(output_message)
# FIXME: to print into log file
print(output_message)

# Delete any already present path in the given entry
# Delete the content of the entry, if any
entry.delete(0, tk.END)
# Insert the selected file path in the given entry
entry.insert(0, filename)
Expand All @@ -805,6 +799,30 @@ def __select_file_and_fill_entry(self, entry: ttk.Entry, fileToSearch: str, form
# content validation
self.focus()

def __set_dirs(self, filename: str) -> None:
"""
Method for setting:
- the directory to show the next time the file selection window is opened;
- the directory where output files will be produced, if it has not been
set yet.
Both attributes are set to the path of the parent folder of the file
provided as input.

Parameters
----------
filename : str
Path of the file whose parent folder has to be used for setting the
initial and output folders.
"""
# Update the default directory of the file selection window to the one of
# the currently opened file
self.__initial_dir = os.path.dirname(filename)

# If not already done, set the output directory to the one of the
# currently opened file
if not self.__output_dir:
self.__output_dir = self.__initial_dir

def select_output_folder(self, event: Union[tk.Event, None] = None) -> None:
"""
Method for asking the user to select the output folder by opening a selection window.
Expand Down
41 changes: 40 additions & 1 deletion tugui/support.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import platform
from enum import Enum
from typing import List, Tuple, Union
Expand Down Expand Up @@ -73,4 +74,42 @@ def description(self) -> str:
"""
Descriptive string of the element of the enumeration
"""
return self.value[1]
return self.value[1]


def check_file_existence(file_path: str) -> None:
"""
Function for checking if the given file path exists and is a file.
If not, the function raises an exception.
"""
if not os.path.isfile(file_path):
# If the file does not exists, throw an exception
raise Exception(f"Error: file {file_path} does not exist!")

def check_file_extension_and_existence(file_path: str,
file_extension: str) -> None:
"""
Function for checking if the given file has the correct extension and
exists. If so, True is returned; False otherwise.

Parameters
----------
file_path : str
The path to the file to check.
file_extension : str
The extension the file must have and is checked for.

Raises
------
An Exception if the file extension does not match with the required
one or the file does not exist.
"""
# Check the extension
extension = os.path.splitext(file_path)
if not (extension[1] == '.' + file_extension):
# The file specified by the given path has not the correct extension
raise Exception(f"The indicated file with extension '{extension[1]}' is "
"not valid. Please provide one with the "
f"'{file_extension}' extension.")
# Check the existence
check_file_existence(file_path)
21 changes: 5 additions & 16 deletions tugui/tu_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from gui_configuration import DiagramCharacteristics
from support import check_file_existence
from io import TextIOWrapper


Expand Down Expand Up @@ -198,11 +199,11 @@ def save_loaded_inp(self) -> str:
else:
plireader = PliReader.init_PliReader(os.path.join(self.inp_dir, diagr.pli_name))
# Check if any of the DAT files is missing
check_file_existence(os.path.join(self.inp_dir, plireader.mac_path), 'mac')
check_file_existence(os.path.join(self.inp_dir, plireader.mic_path), 'mic')
check_file_existence(os.path.join(self.inp_dir, plireader.mac_path))
check_file_existence(os.path.join(self.inp_dir, plireader.mic_path))
# Check the .sta file existence only if required, i.e if the 'ISTATI' field is '1'
if plireader.opt_dict['ISTATI'] == 1:
check_file_existence(os.path.join(self.inp_dir, plireader.sta_path), 'sta')
check_file_existence(os.path.join(self.inp_dir, plireader.sta_path))

# Declare a string holding the .inp filename (with default to 'TuPlot')
filename = "TuPlot.inp"
Expand Down Expand Up @@ -279,17 +280,6 @@ def _extract_diagram_info(self, plot_index: int,
inp_config.diagram_config += line


def check_file_existence(file_path: str, file_extension: str) -> None:
"""
Function that can be accessed globally for checking if the given
file path exists and is a file. If not, the function raises an
exception.
"""
if not os.path.isfile(file_path):
# If the file does not exists, throw an exception
raise Exception(f"Error: the .{file_extension} file does not exist at the specified path.")


@dataclass
class DatGenerator():
"""
Expand Down Expand Up @@ -461,7 +451,6 @@ def init_PliReader(pli_path: str) -> Self:
This method returns the built instance of the 'PliReader' class.
"""
# Check the .pli file existence
check_file_existence(pli_path, 'pli')
# Get the path to the .pli file directory
pli_dir = os.path.dirname(pli_path)
# Instantiate the 'PliReader' class
Expand Down Expand Up @@ -530,7 +519,7 @@ def __init__(self, da_path: str, extension: str) -> None:
direct-access file to read and checks its actual existence.
"""
# Check the direct-access file existence
check_file_existence(da_path, extension)
check_file_existence(da_path)
# Store the direct-access file path
self.da_path: str = da_path
# Initialize the time values read from the direct-access file
Expand Down
Loading