AdditionalFiles: Add model, view and solver export.

setup.py
Pierre-Antoine Rouby 2024-03-25 16:45:04 +01:00
parent db416d25de
commit 48e30125bb
16 changed files with 801 additions and 23 deletions

View File

@ -0,0 +1,148 @@
# AddFile.py -- Pamhyr
# Copyright (C) 2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from functools import reduce
from tools import trace, timer
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
class AddFile(SQLSubModel):
_sub_classes = []
_id_cnt = 0
def __init__(self, id: int = -1, enabled=True,
name="", path="", text="",
status=None):
super(AddFile, self).__init__()
if id == -1:
self.id = AddFile._id_cnt
else:
self.id = id
self._status = status
self._enabled = enabled
self._name = f"File {self.id}" if name == "" else name
self._path = path
self._text = text
AddFile._id_cnt = max(id, AddFile._id_cnt+1)
@property
def enabled(self):
return self._enabled
@enabled.setter
def enabled(self, enabled):
self._enabled = enabled
def is_enabled(self):
return self._enabled
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def path(self):
return self._path
@path.setter
def path(self, path):
self._path = path
@property
def text(self):
return self._text
@text.setter
def text(self, text):
self._text = text
@classmethod
def _db_create(cls, execute):
execute("""
CREATE TABLE additional_files(
id INTEGER NOT NULL PRIMARY KEY,
enabled BOOLEAN NOT NULL,
name TEXT NOT NULL,
path TEXT NOT NULL,
text TEXT NOT NULL
)
""")
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 8:
cls._db_create(execute)
return True
@classmethod
def _db_load(cls, execute, data=None):
new = []
table = execute(
"SELECT id, enabled, name, path, text " +
"FROM additional_files"
)
for row in table:
it = iter(row)
id = next(it)
enabled = (next(it) == 1)
name = next(it)
path = next(it)
text = next(it)
f = cls(
id=id, enabled=enabled, name=name, path=path, text=text,
status=data['status']
)
new.append(f)
return new
def _db_save(self, execute, data=None):
sql = (
"INSERT INTO " +
"additional_files(id, enabled, name, path, text) " +
"VALUES (" +
f"{self.id}, {self._enabled}, " +
f"'{self._db_format(self._name)}', " +
f"'{self._db_format(self._path)}', " +
f"'{self._db_format(self._text)}'" +
")"
)
execute(sql)
return True

View File

@ -0,0 +1,56 @@
# AddFileList.py -- Pamhyr
# Copyright (C) 2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from tools import trace, timer
from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrList import PamhyrModelList
from Model.AdditionalFile.AddFile import AddFile
class AddFileList(PamhyrModelList):
_sub_classes = [AddFile]
@classmethod
def _db_load(cls, execute, data=None):
new = cls(status=data["status"])
new._lst = AddFile._db_load(execute, data)
return new
def _db_save(self, execute, data=None):
ok = True
# Delete previous data
execute("DELETE FROM additional_files")
for af in self._lst:
ok &= af._db_save(execute, data)
return ok
@property
def files(self):
return self.lst
def new(self, index):
n = AddFile(status=self._status)
self.insert(index, n)
self._status.modified()
return n

View File

@ -40,6 +40,7 @@ from Model.Reservoir.ReservoirList import ReservoirList
from Model.HydraulicStructures.HydraulicStructuresList import (
HydraulicStructureList,
)
from Model.AdditionalFile.AddFileList import AddFileList
from Solver.Solvers import solver_type_list
@ -224,6 +225,7 @@ class River(Graph, SQLSubModel):
SedimentLayerList,
ReservoirList,
HydraulicStructureList,
AddFileList,
]
def __init__(self, status=None):
@ -245,6 +247,7 @@ class River(Graph, SQLSubModel):
self._hydraulic_structures = HydraulicStructureList(
status=self._status
)
self._additional_files = AddFileList(status=self._status)
@classmethod
def _db_create(cls, execute):
@ -263,64 +266,59 @@ class River(Graph, SQLSubModel):
# Stricklers (Stricklers is load in first because it's needed
# for reachs)
new._stricklers = StricklersList._db_load(
execute,
data
execute, data
)
data["stricklers"] = new._stricklers
# Initial conditions
new._sediment_layers = SedimentLayerList._db_load(
execute,
data
execute, data
)
data["sediment_layers_list"] = new._sediment_layers
# Network
new._nodes = RiverNode._db_load(
execute,
data
execute, data
)
data["nodes"] = new.nodes()
new._edges = RiverReach._db_load(
execute,
data
execute, data
)
data["edges"] = new.edges()
# Boundary Condition
new._boundary_condition = BoundaryConditionList._db_load(
execute,
data
execute, data
)
# Lateral Contribution
new._lateral_contribution = LateralContributionList._db_load(
execute,
data
execute, data
)
# Initial conditions
new._initial_conditions = InitialConditionsDict._db_load(
execute,
data
execute, data
)
# Reservoir
new._reservoir = ReservoirList._db_load(
execute,
data
execute, data
)
# Hydraulic Structures
new._hydraulic_structures = HydraulicStructureList._db_load(
execute,
data
execute, data
)
# Parameters
new._parameters = SolverParametersList._db_load(
execute,
data
execute, data
)
# Additional Files
new._additional_files = AddFileList._db_load(
execute, data
)
return new
@ -336,6 +334,7 @@ class River(Graph, SQLSubModel):
objs.append(self._stricklers)
objs.append(self._reservoir)
objs.append(self._hydraulic_structures)
objs.append(self._additional_files)
for solver in self._parameters:
objs.append(self._parameters[solver])
@ -373,11 +372,27 @@ class River(Graph, SQLSubModel):
logger_exception(e)
def init_default(self):
self.init_default_network()
self.init_default_additional_files()
def init_default_network(self):
n1 = self.add_node(880.0, 950.0)
n2 = self.add_node(1120.0, 1020.0)
e = self.add_edge(n1, n2)
def init_default_additional_files(self):
add_file = self._additional_files.new(0)
add_file.name = "Pamhyr2 stamp file"
add_file.path = "Pamhyr2.txt"
add_file.text = """This repository has been generated by Pamhyr2 \
version "@version" !
All hand made file modification could be erased by the next solver
execution...
Last export at: @date."""
@property
def boundary_condition(self):
return self._boundary_condition
@ -419,6 +434,10 @@ class River(Graph, SQLSubModel):
def hydraulic_structures(self):
return self._hydraulic_structures
@property
def additional_files(self):
return self._additional_files
@property
def parameters(self):
return self._parameters

View File

@ -41,7 +41,7 @@ class Study(SQLModel):
def __init__(self, filename=None, init_new=True):
# Metadata
self._version = "0.0.7"
self._version = "0.0.8"
self.creation_date = datetime.now()
self.last_modification_date = datetime.now()
self.last_save_date = datetime.now()

View File

@ -40,6 +40,7 @@ class Modules(Flag):
HYDRAULIC_STRUCTURES = auto()
RESERVOIR = auto()
SEDIMENT_LAYER = auto()
ADDITIONAL_FILES = auto()
# Results
RESULTS = auto()

View File

@ -19,7 +19,8 @@
import os
import logging
from tools import timer, parse_command_line
from datetime import datetime
from tools import timer, parse_command_line, get_version
try:
# Installation allow Unix-like signal
@ -124,6 +125,39 @@ class CommandLineSolver(AbstractSolver):
"""
raise NotImplementedMethodeError(self, self.log_file)
def export_additional_files(self, study, repertory, qlog, name="0"):
files = []
if qlog is not None:
qlog.put("Export additional files")
files = study.river.additional_files.files
for add_file in files:
self.export_additional_file(
add_file, repertory, files
)
def export_additional_file(self, add_file, repertory, files):
if add_file.path == "" or not add_file.is_enabled():
return files
path = os.path.join(repertory, add_file.path)
os.makedirs(
os.path.dirname(path),
exist_ok=True
)
with open(path, "w+") as f:
files.append(add_file.path)
txt = add_file.text
txt = txt.replace("@version", get_version())
txt = txt.replace("@date", datetime.now().isoformat(sep=" "))
f.write(txt)
return files
#######
# Run #
#######

View File

@ -671,6 +671,7 @@ class Mage(CommandLineSolver):
self._bin_file = f"{name}.BIN"
self._export_ST(study, repertory, qlog, name=name)
self.export_additional_files(study, repertory, qlog, name=name)
return True
@ -886,6 +887,10 @@ class Mage8(Mage):
files = files + self._export_VAR(study, repertory, qlog, name=name)
files = files + self._export_CAS(study, repertory, qlog, name=name)
files = files + self._export_DEV(study, repertory, qlog, name=name)
files = files + self.export_additional_files(
study, repertory, qlog, name=name
)
self._export_REP(study, repertory, files, qlog, name=name)
return True

View File

@ -0,0 +1,80 @@
# Window.py -- Pamhyr
# Copyright (C) 2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtWidgets import (
QLabel, QPlainTextEdit, QPushButton,
QCheckBox,
)
from View.AdditionalFiles.Translate import AddFileTranslate
logger = logging.getLogger()
class EditAddFileWindow(PamhyrWindow):
_pamhyr_ui = "AdditionalFile"
_pamhyr_name = "Edit additional file"
def __init__(self, study=None, config=None, add_file=None,
trad=None, parent=None):
name = trad[self._pamhyr_name] + " - " + study.name
super(EditAddFileWindow, self).__init__(
title=name,
study=study,
config=config,
options=[],
parent=parent
)
self._add_file = add_file
self._hash_data.append(self._add_file)
self.setup_values()
self.setup_connection()
def setup_values(self):
self.set_check_box("checkBox", self._add_file.enabled)
self.set_line_edit_text("lineEdit_name", self._add_file.name)
self.set_line_edit_text("lineEdit_path", self._add_file.path)
self.set_plaintext_edit_text("plainTextEdit", self._add_file.text)
def setup_connection(self):
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.close)
self.find(QPushButton, "pushButton_ok")\
.clicked.connect(self.accept)
def accept(self):
is_enabled = self.get_check_box("checkBox")
name = self.get_line_edit_text("lineEdit_name")
path = self.get_line_edit_text("lineEdit_path")
text = self.get_plaintext_edit_text("plainTextEdit")
self._add_file.enabled = is_enabled
self._add_file.name = name
self._add_file.path = path
self._add_file.text = text
self._propagate_update(key=Modules.ADDITIONAL_FILES)
self.close()

View File

@ -0,0 +1,71 @@
# List.py -- Pamhyr
# Copyright (C) 2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from functools import reduce
from tools import trace, timer
from PyQt5.QtCore import (
Qt, QVariant,
)
from PyQt5.QtGui import (
QColor, QBrush,
)
from View.Tools.PamhyrList import PamhyrListModel
logger = logging.getLogger()
class ListModel(PamhyrListModel):
def data(self, index, role):
row = index.row()
column = index.column()
file = self._data.files[row]
if role == Qt.ForegroundRole:
color = Qt.gray
if file.is_enabled():
color = QColor("black")
else:
color = QColor("grey")
return QBrush(color)
if role == Qt.ItemDataRole.DisplayRole:
text = f"{file.name}: '{file.path}'"
if not file.is_enabled():
text += " (disabled)"
return text
return QVariant()
def add(self, row):
self._data.new(row)
self.update()
def delete(self, rows):
logger.info(f"add_files: delete {rows}")
self._data.delete_i(rows)
self.update()

View File

@ -0,0 +1,35 @@
# Translate.py -- Pamhyr
# Copyright (C) 2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
from View.Translate import MainTranslate
_translate = QCoreApplication.translate
class AddFileTranslate(MainTranslate):
def __init__(self):
super(AddFileTranslate, self).__init__()
self._dict["Additional files"] = _translate(
"AdditionalFiles", "Additional files"
)
self._dict["Edit additional file"] = _translate(
"AdditionalFiles", "Edit additional file"
)

View File

@ -0,0 +1,104 @@
# Window.py -- Pamhyr
# Copyright (C) 2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from tools import trace, timer
from PyQt5.QtWidgets import (
QAction, QListView,
)
from View.Tools.PamhyrWindow import PamhyrWindow
from View.AdditionalFiles.List import ListModel
from View.AdditionalFiles.Translate import AddFileTranslate
from View.AdditionalFiles.Edit.Window import EditAddFileWindow
class AddFileListWindow(PamhyrWindow):
_pamhyr_ui = "AdditionalFileList"
_pamhyr_name = "Additional files"
def __init__(self, study=None, config=None,
parent=None):
trad = AddFileTranslate()
name = trad[self._pamhyr_name] + " - " + study.name
super(AddFileListWindow, self).__init__(
title=name,
study=study,
config=config,
trad=trad,
options=[],
parent=parent
)
self.setup_list()
self.setup_connections()
def setup_list(self):
lst = self.find(QListView, f"listView")
self._list = ListModel(
list_view=lst,
data=self._study.river.additional_files,
)
def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_delete").triggered.connect(self.delete)
self.find(QAction, "action_edit").triggered.connect(self.edit)
def update(self):
self._list.update()
def selected_rows(self):
lst = self.find(QListView, f"listView")
return list(map(lambda i: i.row(), lst.selectedIndexes()))
def add(self):
rows = self.selected_rows()
if len(rows) > 0:
row = rows[0]
else:
row = 0
self._list.add(row)
def delete(self):
rows = self.selected_rows()
self._list.delete(rows)
def edit(self):
rows = self.selected_rows()
for row in rows:
add_file = self._study.river.additional_files.files[row]
if self.sub_window_exists(
EditAddFileWindow,
data=[self._study, self._config, add_file]
):
continue
win = EditAddFileWindow(
study=self._study,
config=self._config,
add_file=add_file,
trad=self._trad,
parent=self,
)
win.show()

View File

@ -67,6 +67,7 @@ from View.Stricklers.Window import StricklersWindow
from View.Frictions.Window import FrictionsWindow
from View.SedimentLayers.Window import SedimentLayersWindow
from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
from View.AdditionalFiles.Window import AddFileListWindow
from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow
from View.CheckList.Window import CheckListWindow
@ -117,7 +118,7 @@ define_model_action = [
"action_menu_edit_friction", "action_menu_edit_lateral_contribution",
"action_menu_run_solver", "action_menu_sediment_layers",
"action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs",
"action_menu_edit_hydraulic_structures",
"action_menu_edit_hydraulic_structures", "action_menu_additional_file",
"action_menu_results_last", "action_open_results_from_file",
"action_menu_boundary_conditions_sediment",
]
@ -252,6 +253,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_sediment_layers": self.open_sediment_layers,
"action_menu_edit_reach_sediment_layers":
self.open_reach_sediment_layers,
"action_menu_additional_file": self.open_additional_files,
"action_menu_close": self.close_model,
"action_menu_results_last": self.open_last_results,
"action_open_results_from_file": self.open_results_from_file,
@ -1063,6 +1065,19 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
else:
self.msg_select_reach()
def open_additional_files(self):
if self._study is not None:
if self.sub_window_exists(
AddFileListWindow,
data=[self._study, None]
):
return
self.additonal_files = AddFileListWindow(
study=self._study, parent=self
)
self.additonal_files.show()
def open_solver_parameters(self):
if self.sub_window_exists(
SolverParametersWindow,

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>896</width>
<height>504</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="locale">
<locale language="English" country="Europe"/>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_ok">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>File text</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="plainTextEdit"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Information</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Enabled</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_path">
<property name="toolTip">
<string>The relative file path on executable directory</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_name"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Path</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>896</width>
<height>504</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QListView" name="listView"/>
</item>
</layout>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="action_add"/>
<addaction name="action_delete"/>
<addaction name="action_edit"/>
</widget>
<action name="action_add">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-add.png</normaloff>ressources/gtk-add.png</iconset>
</property>
<property name="text">
<string>Add</string>
</property>
<property name="toolTip">
<string>Add a new file</string>
</property>
</action>
<action name="action_delete">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-remove.png</normaloff>ressources/gtk-remove.png</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete selected file(s)</string>
</property>
</action>
<action name="action_edit">
<property name="icon">
<iconset>
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
</property>
<property name="text">
<string>Edit</string>
</property>
<property name="toolTip">
<string>Edit file</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -204,11 +204,18 @@
<string>&amp;Windows</string>
</property>
</widget>
<widget class="QMenu" name="menu_Avensed">
<property name="title">
<string>&amp;Advansed</string>
</property>
<addaction name="action_menu_additional_file"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_network"/>
<addaction name="menu_geometry"/>
<addaction name="menu_Hydraulics"/>
<addaction name="menuSediment"/>
<addaction name="menu_Avensed"/>
<addaction name="menu_run"/>
<addaction name="menu_results"/>
<addaction name="menu_cartography"/>
@ -988,6 +995,11 @@
<string>Boundary conditions and punctual contributions</string>
</property>
</action>
<action name="action_menu_additional_file">
<property name="text">
<string>&amp;Additional file</string>
</property>
</action>
</widget>
<resources/>
<connections>

View File

@ -279,10 +279,20 @@ def get_user_name():
return "Me"
def get_version():
with open(os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"VERSION"
)
), "r") as f:
return f.readline().strip()
#######################
# COMMAND LINE PARSER #
#######################
parser_special_char = ["\"", "\'"]