Merge branch 'mesh_tab' into dev_dylan

new_design_pol
Dylan Jeannin 2026-08-31 17:07:16 +02:00
commit 53da35d959
16 changed files with 152 additions and 32 deletions

View File

@ -24,7 +24,7 @@ from datetime import date, time, datetime, timedelta
from tools import ( from tools import (
trace, timer, trace, timer,
timestamp_to_old_pamhyr_date, timestamp_to_old_pamhyr_date,
old_pamhyr_date_to_timestamp old_pamhyr_date_to_timestamp, format_datetime
) )
from View.Tools.PamhyrTable import PamhyrTableModel from View.Tools.PamhyrTable import PamhyrTableModel
@ -97,7 +97,9 @@ class TableModel(PamhyrTableModel):
if self._opt_data == "time": if self._opt_data == "time":
value = timestamp_to_old_pamhyr_date(int(v)) value = timestamp_to_old_pamhyr_date(int(v))
else: else:
value = str(datetime.fromtimestamp(v)) value = format_datetime(
self._start_date + timedelta(seconds=float(v))
)
else: else:
value = f"{v}" value = f"{v}"
@ -114,6 +116,10 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
try: try:
if (self._data.header[column] == "time"
and self._opt_data == "date"):
value = self.date_to_elapsed_seconds(value)
row = self.get_true_data_row(row) row = self.get_true_data_row(row)
self._undo.push( self._undo.push(

View File

@ -173,7 +173,8 @@ class EditBoundaryConditionWindow(PamhyrWindow):
}, },
data=self._data, data=self._data,
undo=self._undo_stack, undo=self._undo_stack,
opt_data=self._study.time_system opt_data=self._study.time_system,
start_date=self._study.date
) )
table.setModel(self._table) table.setModel(self._table)

View File

@ -24,7 +24,7 @@ from datetime import date, time, datetime, timedelta
from tools import ( from tools import (
trace, timer, trace, timer,
timestamp_to_old_pamhyr_date, timestamp_to_old_pamhyr_date,
old_pamhyr_date_to_timestamp old_pamhyr_date_to_timestamp, format_datetime
) )
from View.Tools.PamhyrTable import PamhyrTableModel from View.Tools.PamhyrTable import PamhyrTableModel
@ -87,7 +87,9 @@ class TableModel(PamhyrTableModel):
if self._opt_data == "time": if self._opt_data == "time":
value = timestamp_to_old_pamhyr_date(int(v)) value = timestamp_to_old_pamhyr_date(int(v))
else: else:
value = str(datetime.fromtimestamp(v)) value = format_datetime(
self._start_date + timedelta(seconds=float(v))
)
else: else:
value = f"{v}" value = f"{v}"
@ -104,6 +106,10 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
try: try:
if (self._data.header[column] == "time"
and self._opt_data == "date"):
value = self.date_to_elapsed_seconds(value)
row = self.get_true_data_row(row) row = self.get_true_data_row(row)
self._undo.push( self._undo.push(

View File

@ -120,7 +120,8 @@ class EditBoundaryConditionWindow(PamhyrWindow):
}, },
data=self._data, data=self._data,
undo=self._undo_stack, undo=self._undo_stack,
opt_data=self._study.time_system opt_data=self._study.time_system,
start_date=self._study.date
) )
table.setModel(self._table) table.setModel(self._table)

View File

@ -24,7 +24,7 @@ from datetime import date, time, datetime, timedelta
from tools import ( from tools import (
trace, timer, trace, timer,
timestamp_to_old_pamhyr_date, timestamp_to_old_pamhyr_date,
old_pamhyr_date_to_timestamp old_pamhyr_date_to_timestamp, format_datetime
) )
from View.Tools.PamhyrTable import PamhyrTableModel from View.Tools.PamhyrTable import PamhyrTableModel
@ -87,7 +87,9 @@ class TableModel(PamhyrTableModel):
if self._opt_data == "time": if self._opt_data == "time":
value = timestamp_to_old_pamhyr_date(int(v)) value = timestamp_to_old_pamhyr_date(int(v))
else: else:
value = str(datetime.fromtimestamp(v)) value = format_datetime(
self._start_date + timedelta(seconds=float(v))
)
else: else:
value = f"{v}" value = f"{v}"
@ -104,6 +106,10 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
try: try:
if (self._data.header[column] == "time"
and self._opt_data == "date"):
value = self.date_to_elapsed_seconds(value)
row = self.get_true_data_row(row) row = self.get_true_data_row(row)
self._undo.push( self._undo.push(

View File

@ -117,7 +117,8 @@ class EditBoundaryConditionWindow(PamhyrWindow):
}, },
data=self._data, data=self._data,
undo=self._undo_stack, undo=self._undo_stack,
opt_data=self._study.time_system opt_data=self._study.time_system,
start_date=self._study.date
) )
table.setModel(self._table) table.setModel(self._table)

View File

@ -171,7 +171,7 @@ class ProfileWindow(PamhyrWindow):
def profiles(self): def profiles(self):
"""Return profiles from the geometry model owning this profile.""" """Return profiles from the geometry model owning this profile."""
return self._profile.reach.reach.profiles return self._profile.reach.profiles
def navigate_profile(self, offset): def navigate_profile(self, offset):
profiles = self.profiles() profiles = self.profiles()

View File

@ -818,6 +818,10 @@ class GeometryWindow(PamhyrWindow):
# self.update_plot_rkc() # self.update_plot_rkc()
def export_to_file(self): def export_to_file(self):
export_interpolated = self._ask_export_options()
if export_interpolated is None:
return
settings = QSettings( settings = QSettings(
QSettings.IniFormat, QSettings.IniFormat,
QSettings.UserScope, 'MyOrg' QSettings.UserScope, 'MyOrg'
@ -847,11 +851,9 @@ class GeometryWindow(PamhyrWindow):
suffix = Path(filename).suffix suffix = Path(filename).suffix
if suffix != "": if suffix != "":
if suffix == ".st" or suffix == ".ST": if suffix == ".st" or suffix == ".ST":
export_interpolated = self._ask_export_options() self._export_to_file_st(
if export_interpolated is not None: filename[:-3], export_interpolated
self._export_to_file_st( )
filename[:-3], export_interpolated
)
else: else:
# Warning popup when the format is not exportable # Warning popup when the format is not exportable
win = QtWidgets.QMessageBox() win = QtWidgets.QMessageBox()
@ -862,9 +864,7 @@ class GeometryWindow(PamhyrWindow):
pass pass
else: else:
export_interpolated = self._ask_export_options() self._export_to_file_st(filename, export_interpolated)
if export_interpolated is not None:
self._export_to_file_st(filename, export_interpolated)
def _ask_export_options(self): def _ask_export_options(self):
win = QtWidgets.QMessageBox(self) win = QtWidgets.QMessageBox(self)
@ -901,8 +901,9 @@ class GeometryWindow(PamhyrWindow):
if is_interpolated and not export_interpolated: if is_interpolated and not export_interpolated:
continue continue
self._export_to_file_st_profile(wfile, profile, pid) if profile.enabled:
pid += 1 self._export_to_file_st_profile(wfile, profile, pid)
pid += 1
def _export_to_file_st_profile(self, wfile, profile, pid): def _export_to_file_st_profile(self, wfile, profile, pid):
num = f"{pid:>6}" num = f"{pid:>6}"

View File

@ -23,7 +23,7 @@ from datetime import date, time, datetime, timedelta
from tools import ( from tools import (
trace, timer, trace, timer,
timestamp_to_old_pamhyr_date, timestamp_to_old_pamhyr_date,
old_pamhyr_date_to_timestamp old_pamhyr_date_to_timestamp, format_datetime
) )
from View.Tools.PamhyrTable import PamhyrTableModel from View.Tools.PamhyrTable import PamhyrTableModel
@ -76,7 +76,9 @@ class TableModel(PamhyrTableModel):
if self._opt_data == "time": if self._opt_data == "time":
value = timestamp_to_old_pamhyr_date(int(v)) value = timestamp_to_old_pamhyr_date(int(v))
else: else:
value = str(datetime.fromtimestamp(v)) value = format_datetime(
self._start_date + timedelta(seconds=float(v))
)
else: else:
value = f"{v}" value = f"{v}"
@ -93,6 +95,10 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
try: try:
if (self._data.header[column] == "time"
and self._opt_data == "date"):
value = self.date_to_elapsed_seconds(value)
self._undo.push( self._undo.push(
SetDataCommand( SetDataCommand(
self._data, row, column, value self._data, row, column, value

View File

@ -109,7 +109,8 @@ class EditLateralContributionWindow(PamhyrWindow):
data=self._data, data=self._data,
undo=self._undo_stack, undo=self._undo_stack,
trad=self._trad, trad=self._trad,
opt_data=self._study.time_system opt_data=self._study.time_system,
start_date=self._study.date
) )
table.setModel(self._table) table.setModel(self._table)

View File

@ -24,7 +24,7 @@ from tools import (
trace, timer, trace, timer,
timestamp_to_old_pamhyr_date, timestamp_to_old_pamhyr_date,
old_pamhyr_date_to_timestamp, old_pamhyr_date_to_timestamp,
timestamp_to_old_pamhyr_date_adists timestamp_to_old_pamhyr_date_adists, format_datetime
) )
from View.Tools.PamhyrTable import PamhyrTableModel from View.Tools.PamhyrTable import PamhyrTableModel
@ -90,7 +90,9 @@ class TableModel(PamhyrTableModel):
if self._opt_data == "time": if self._opt_data == "time":
value = timestamp_to_old_pamhyr_date_adists(int(v)) value = timestamp_to_old_pamhyr_date_adists(int(v))
else: else:
value = str(datetime.fromtimestamp(v)) value = format_datetime(
self._start_date + timedelta(seconds=float(v))
)
else: else:
value = f"{v}" value = f"{v}"
@ -107,6 +109,10 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
try: try:
if (self._data.header[column] == "time"
and self._opt_data == "date"):
value = self.date_to_elapsed_seconds(value)
row = self.get_true_data_row(row) row = self.get_true_data_row(row)
self._undo.push( self._undo.push(

View File

@ -124,7 +124,8 @@ class EditLateralContributionAdisTSWindow(PamhyrWindow):
data=self._data, data=self._data,
undo=self._undo_stack, undo=self._undo_stack,
trad=self._trad, trad=self._trad,
opt_data=self._study.time_system opt_data=self._study.time_system,
start_date=self._study.date
) )
table.setModel(self._table) table.setModel(self._table)

View File

@ -19,7 +19,7 @@
import logging import logging
import traceback import traceback
from tools import trace, timer from tools import trace, timer, parse_datetime
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
@ -34,8 +34,11 @@ from PyQt5.QtWidgets import (
QFileDialog, QTableView, QAbstractItemView, QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate, QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QStyledItemDelegate, QHeaderView, QComboBox, QStyledItemDelegate, QHeaderView,
QMessageBox,
) )
_translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
@ -59,6 +62,27 @@ class PamhyrTextDelegate(QStyledItemDelegate):
class PamhyrTableModel(QAbstractTableModel): class PamhyrTableModel(QAbstractTableModel):
def date_to_elapsed_seconds(self, value):
"""Convert an entered date to seconds since the study start date."""
try:
entered_date = parse_datetime(value)
except ValueError as error:
QMessageBox.warning(
self._table_view,
_translate("PamhyrTableModel", "Invalid date/time"),
_translate(
"PamhyrTableModel",
"The entered date/time is invalid.\n"
"Expected formats:\n"
"YYYY-MM-DD [HH:MM[:SS]],\n"
"DD-MM-YYYY [HH:MM[:SS]] or\n"
"DD/MM/YYYY [HH:MM[:SS]]."
)
)
raise error
return (entered_date - self._start_date).total_seconds()
def _setup_delegates(self): def _setup_delegates(self):
if self._table_view is None: if self._table_view is None:
return return
@ -85,7 +109,8 @@ class PamhyrTableModel(QAbstractTableModel):
undo=None, undo=None,
opt_data=None, opt_data=None,
options=["rows_selection"], options=["rows_selection"],
parent=None): parent=None,
start_date=None):
super(PamhyrTableModel, self).__init__() super(PamhyrTableModel, self).__init__()
self._table_view = table_view self._table_view = table_view
@ -102,6 +127,7 @@ class PamhyrTableModel(QAbstractTableModel):
self._options = options self._options = options
self._undo = undo self._undo = undo
self._lst = [] self._lst = []
self._start_date = start_date
self._setup_delegates() self._setup_delegates()
self._setup_lst() self._setup_lst()

View File

@ -23,7 +23,7 @@ from datetime import date, time, datetime, timedelta
from tools import ( from tools import (
trace, timer, trace, timer,
timestamp_to_old_pamhyr_date, timestamp_to_old_pamhyr_date,
old_pamhyr_date_to_timestamp old_pamhyr_date_to_timestamp, format_datetime
) )
from View.Tools.PamhyrTable import PamhyrTableModel from View.Tools.PamhyrTable import PamhyrTableModel
@ -77,7 +77,9 @@ class TableModel(PamhyrTableModel):
if self._opt_data == "time": if self._opt_data == "time":
value = timestamp_to_old_pamhyr_date(int(v)) value = timestamp_to_old_pamhyr_date(int(v))
else: else:
value = str(datetime.fromtimestamp(v)) value = format_datetime(
self._start_date + timedelta(seconds=float(v))
)
else: else:
value = f"{v}" value = f"{v}"
@ -94,6 +96,10 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
try: try:
if (self._data.header[column] == "time"
and self._opt_data == "date"):
value = self.date_to_elapsed_seconds(value)
self._undo.push( self._undo.push(
SetDataCommand( SetDataCommand(
self._data, row, column, value self._data, row, column, value

View File

@ -121,7 +121,8 @@ class EditWeatherParametersWindow(PamhyrWindow):
data=self._data, data=self._data,
undo=self._undo_stack, undo=self._undo_stack,
trad=self._trad, trad=self._trad,
opt_data=self._study.time_system opt_data=self._study.time_system,
start_date=self._study.date
) )
table.setModel(self._table) table.setModel(self._table)

View File

@ -22,7 +22,7 @@ import logging
import sqlite3 import sqlite3
import traceback import traceback
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
from colorama import Fore from colorama import Fore
@ -239,6 +239,57 @@ def timestamp(dt: datetime):
return dt.timestamp() return dt.timestamp()
SUPPORTED_DATETIME_FORMATS = (
"%d-%m-%Y %H:%M:%S.%f",
"%d-%m-%Y %H:%M:%S",
"%d-%m-%Y %H:%M",
"%d-%m-%Y",
"%d/%m/%Y %H:%M:%S.%f",
"%d/%m/%Y %H:%M:%S",
"%d/%m/%Y %H:%M",
"%d/%m/%Y",
"%d-%m-%y %H:%M:%S",
"%d/%m/%y %H:%M:%S",
)
def parse_datetime(value):
"""Parse a supported date representation into a naive UTC datetime."""
if isinstance(value, datetime):
parsed = value
else:
text = str(value).strip()
if text.endswith(("Z", "z")):
text = text[:-1] + "+00:00"
try:
parsed = datetime.fromisoformat(text)
except ValueError as iso_error:
for date_format in SUPPORTED_DATETIME_FORMATS:
try:
parsed = datetime.strptime(text, date_format)
break
except ValueError:
continue
else:
raise ValueError(
f"Unsupported date format: {value!r}. Expected an ISO "
"date/time or one of: "
+ ", ".join(SUPPORTED_DATETIME_FORMATS)
) from iso_error
# If the parsed datetime has timezone information, convert it to UTC and remove the timezone info
if parsed.tzinfo is not None:
parsed = parsed.astimezone(timezone.utc).replace(tzinfo=None)
return parsed
def format_datetime(value):
"""Return a datetime using the canonical application format."""
return parse_datetime(value).strftime("%Y-%m-%d %H:%M:%S")
def date_iso_to_timestamp(date: str): def date_iso_to_timestamp(date: str):
if type(date) is str: if type(date) is str:
return timestamp(datetime.fromisoformat(date)) return timestamp(datetime.fromisoformat(date))