diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py
index 14ac5db4..86f2cb6e 100644
--- a/src/Model/BoundaryCondition/BoundaryCondition.py
+++ b/src/Model/BoundaryCondition/BoundaryCondition.py
@@ -582,10 +582,10 @@ class BoundaryCondition(SQLSubModel):
self._data.index(bc)
def get_i(self, index):
- if len(self.data) == 0:
+ if len(self._data) == 0:
return None
- return self.data[index]
+ return self._data[index]
def get_range(self, _range):
lst = []
diff --git a/src/Model/Results/Results.py b/src/Model/Results/Results.py
index 3ea6e45a..3b352bec 100644
--- a/src/Model/Results/Results.py
+++ b/src/Model/Results/Results.py
@@ -52,6 +52,9 @@ class Results(SQLSubModel):
"study_revision": study.status.version,
}
+ if solver is not None:
+ self.set("solver_type", solver._type)
+
@property
def date(self):
date = self._meta_data["creation_date"]
@@ -112,7 +115,10 @@ class Results(SQLSubModel):
)
""")
- return True
+ if ext != "":
+ return True
+
+ return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
@@ -147,7 +153,7 @@ class Results(SQLSubModel):
)
if table is None:
- yield new
+ yield
if len(table) > 1:
logger.warning("Multiple results for this scenario")
@@ -178,23 +184,33 @@ class Results(SQLSubModel):
data["timestamps"] = sorted(ts)
new_results._river = River._db_load(execute, data)
- new = new_results
+ yield (solver_type, new_results)
- yield new
+ def _db_save_clear(self, execute, solver_type, data=None):
+ old_pid = execute(
+ "SELECT pamhyr_id FROM results " +
+ f"WHERE scenario = {self._owner_scenario} " +
+ f"AND solver_type = '{solver_type}'"
+ )
+ if len(old_pid) != 0:
+ for pid in old_pid:
+ pid = pid[0]
+
+ execute(
+ "DELETE FROM results " +
+ f"WHERE scenario = {self._owner_scenario} " +
+ f"AND solver_type = '{solver_type}'"
+ )
+ execute(
+ "DELETE FROM results_data " +
+ f"WHERE scenario = {self._owner_scenario} " +
+ f"AND result = {pid}"
+ )
def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario:
return
- execute(
- "DELETE FROM results " +
- f"WHERE scenario = {self._owner_scenario}"
- )
- execute(
- "DELETE FROM results_data " +
- f"WHERE scenario = {self._owner_scenario}"
- )
-
pid = self._pamhyr_id
if self._solver is None:
solver_name = self.get("solver_name")
@@ -203,6 +219,8 @@ class Results(SQLSubModel):
solver_name = self._solver._name
solver_type = self._solver._type
+ self._db_save_clear(execute, solver_type, data=data)
+
ts = sorted(self.get("timestamps"))
sf = ">" + ''.join(itertools.repeat("d", len(ts)))
diff --git a/src/Model/Results/River/River.py b/src/Model/Results/River/River.py
index b94b06d6..d111bf9f 100644
--- a/src/Model/Results/River/River.py
+++ b/src/Model/Results/River/River.py
@@ -227,7 +227,7 @@ class Profile(SQLSubModel):
values = list(map(float, values))
sf = ">" + ''.join(itertools.repeat("f", len(values)))
len_values = len(values)
- elif key is "sl":
+ elif key == "sl":
# HACK: Some dirty code to transforme list of list of
# tuple to list of values and ensure the values is
# float type...
diff --git a/src/Model/River.py b/src/Model/River.py
index 2671b12b..e0c4602b 100644
--- a/src/Model/River.py
+++ b/src/Model/River.py
@@ -505,7 +505,7 @@ class River(Graph):
self._D90AdisTS = D90AdisTSList(status=self._status)
self._DIFAdisTS = DIFAdisTSList(status=self._status)
- self._results = None
+ self._results = {}
@classmethod
def _db_create(cls, execute):
@@ -620,7 +620,10 @@ class River(Graph):
return new
def _db_load_results(self, execute, data=None):
- self._results = Results._db_load(execute, data)
+ results_lst = Results._db_load(execute, data)
+
+ for solv_type, results in results_lst:
+ self._results[solv_type] = results
def _db_save(self, execute, data=None):
self._db_save_delete_artefact(execute, data)
@@ -647,8 +650,8 @@ class River(Graph):
objs.append(self._D90AdisTS)
objs.append(self._DIFAdisTS)
- if self.results is not None:
- objs.append(self.results)
+ for solv_type in self.results:
+ objs.append(self.results[solv_type])
self._save_submodel(execute, objs, data)
return True
@@ -902,7 +905,9 @@ Last export at: @date."""
@results.setter
def results(self, results):
- self._results = results
+ solv_type = results.get("solver_type")
+
+ self._results[solv_type] = results
def _split_reach(self, reach, profile):
node1 = reach.node1
diff --git a/src/Model/Scenario.py b/src/Model/Scenario.py
index 8ceb8161..b66faa64 100644
--- a/src/Model/Scenario.py
+++ b/src/Model/Scenario.py
@@ -276,7 +276,7 @@ class Scenario(SQLSubModel):
if self.id != 0:
srep = os.path.join(
self.parent.workdir(),
- "senario_" + str(self.id)
+ "scenario_" + str(self.id)
)
return srep
diff --git a/src/Model/Study.py b/src/Model/Study.py
index a89416f8..4e054c24 100644
--- a/src/Model/Study.py
+++ b/src/Model/Study.py
@@ -44,7 +44,10 @@ class Study(SQLModel):
River,
]
- def __init__(self, filename=None, init_new=True):
+ def __init__(self, filename=None, init_new=True, copy=False):
+ if copy:
+ return
+
# Metadata
self.creation_date = datetime.now()
self.last_modification_date = datetime.now()
@@ -476,31 +479,8 @@ class Study(SQLModel):
return new
def reload_from_scenario(self, scenario):
- if scenario in self._river_scenario_cache:
- self._river = self._river_scenario_cache[scenario]
- self.status.scenario = scenario
- else:
- def sql_exec(sql):
- return self.execute(
- sql, fetch_one=False, commit=True
- )
-
- self.status.scenario = scenario
- data = {
- "status": self.status,
- "loaded_pid": set(),
- "scenario": scenario
- }
-
- # Reload river data
- river = River._db_load(
- sql_exec, data=data
- )
- data["study"] = self
- river._db_load_results(sql_exec, data=data)
-
- self._river_scenario_cache[scenario] = river
- self._river = river
+ river = self.load_scenario(scenario)
+ self._river = river
if reduce(
lambda a, s: a or (s.parent is scenario),
@@ -511,6 +491,80 @@ class Study(SQLModel):
else:
self.status.set_as_editable()
+ def load_scenario(self, scenario):
+ if scenario in self._river_scenario_cache:
+ return self._river_scenario_cache[scenario]
+
+ def sql_exec(sql):
+ return self.execute(
+ sql, fetch_one=False, commit=True
+ )
+
+ old_scenario = self.status.scenario
+ self.status.scenario = scenario
+
+ data = {
+ "status": self.status,
+ "loaded_pid": set(),
+ "scenario": scenario
+ }
+
+ # Load river data
+ river = River._db_load(
+ sql_exec, data=data
+ )
+ data["study"] = self
+ river._db_load_results(sql_exec, data=data)
+
+ self._river_scenario_cache[scenario] = river
+ self.status.scenario = old_scenario
+
+ return river
+
+ def copy_from_scenario(self, scenario):
+ new = self._copy()
+
+ new._river = self.load_scenario(scenario)
+
+ def set_status(obj):
+ obj._status = new.status
+
+ new.river._data_traversal(
+ modifier=lambda obj, data: set_status(obj),
+ )
+
+ return new
+
+ def _copy(self):
+ """
+ This method make a copy of current study. This copy is like an
+ empty shell, it's not fully functional. Study object use
+ SQLite connection to file, this copy as no valid connection.
+
+ (!) Please use this copy as read only object!
+ """
+ new = Study(copy=True)
+
+ new._filename = ""
+ new.creation_date = self.creation_date
+ new.last_modification_date = self.last_modification_date
+ new.last_save_date = self.last_save_date
+
+ new._name = self._name
+ new.description = self.description
+
+ new._time_system = self._time_system
+ new._date = self._date
+
+ new.status = StudyStatus()
+ new.scenarios = self.scenarios
+ new.status.scenario = self.status.scenario
+
+ new._river = self._river
+ new._river_scenario_cache = self._river_scenario_cache
+
+ return new
+
def duplicate_current_scenario(self):
source = self.status.scenario_id
new = self.scenarios.new(
diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py
index d3e1068f..95f642bf 100644
--- a/src/Solver/Mage.py
+++ b/src/Solver/Mage.py
@@ -177,15 +177,15 @@ class Mage(CommandLineSolver):
return lst
def input_param(self):
- name = self._study.name
+ name = self._study.name.replace(" ", "_")
return f"{name}.REP"
def output_param(self):
- name = self._study.name
+ name = self._study.name.replace(" ", "_")
return f"{name}.BIN"
def log_file(self):
- name = self._study.name
+ name = self._study.name.replace(" ", "_")
return f"{name}.TRA"
def _export_REP_additional_lines(self, study, rep_file):
@@ -819,7 +819,8 @@ class Mage(CommandLineSolver):
repertory=repertory,
name=name,
)
- fname = os.path.join(repertory, f"{name}.BIN")
+
+ fname = os.path.join(repertory, f"{name}.BIN".replace(" ", "_"))
if not os.path.isfile(fname):
logger.info(f"Result file {name}.BIN does not exist")
return None
@@ -1374,6 +1375,7 @@ class Mage8(Mage):
results = super(Mage8, self).results(study, repertory, qlog, name=name)
if results is None:
return None
+
if with_gra:
self.read_gra(study, repertory, results, qlog, name=name)
diff --git a/src/View/BoundaryCondition/Edit/Table.py b/src/View/BoundaryCondition/Edit/Table.py
index 601a09ac..c0cfa1ed 100644
--- a/src/View/BoundaryCondition/Edit/Table.py
+++ b/src/View/BoundaryCondition/Edit/Table.py
@@ -58,7 +58,7 @@ logger = logging.getLogger()
class TableModel(PamhyrTableModel):
def get_true_data_row(self, row):
- bc = self._data.get_i(row)
+ bc = self._data.data[row]
return next(
map(
@@ -83,6 +83,7 @@ class TableModel(PamhyrTableModel):
value = QVariant()
if 0 <= column < 2:
+ row = self.get_true_data_row(row)
v = self._data.get_i(row)[column]
if self._data.get_type_column(column) == float:
if type(v) is str:
@@ -106,13 +107,16 @@ class TableModel(PamhyrTableModel):
column = index.column()
try:
+ row = self.get_true_data_row(row)
+
self._undo.push(
SetDataCommand(
- self._data, row, column, value
+ self._data, row,
+ column, value
)
)
except Exception as e:
- logger.info(e)
+ logger.warning(e)
logger.debug(traceback.format_exc())
self.update()
diff --git a/src/View/BoundaryCondition/Edit/UndoCommand.py b/src/View/BoundaryCondition/Edit/UndoCommand.py
index 5289ef22..986b4b73 100644
--- a/src/View/BoundaryCondition/Edit/UndoCommand.py
+++ b/src/View/BoundaryCondition/Edit/UndoCommand.py
@@ -83,13 +83,13 @@ class AddCommand(QUndoCommand):
self._new = None
def undo(self):
- self._data.delete_i([self._index])
+ self._new.set_as_deleted()
def redo(self):
if self._new is None:
self._new = self._data.add(self._index)
else:
- self._data.insert(self._index, self._new)
+ self._new.set_as_not_deleted()
class DelCommand(QUndoCommand):
@@ -101,15 +101,16 @@ class DelCommand(QUndoCommand):
self._bc = []
for row in rows:
- self._bc.append((row, self._data.get_i(row)))
+ self._bc.append(self._data.get_i(row))
self._bc.sort()
def undo(self):
- for row, el in self._bc:
- self._data.insert(row, el)
+ for el in self._bc:
+ el.set_as_not_deleted()
def redo(self):
- self._data.delete_i(self._rows)
+ for el in self._bc:
+ el.set_as_deleted()
class SortCommand(QUndoCommand):
diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py
index 0821cd62..db4ec59f 100644
--- a/src/View/MainWindow.py
+++ b/src/View/MainWindow.py
@@ -32,7 +32,7 @@ from platformdirs import user_cache_dir
from Solver.AdisTS import AdisTS
from Solver.Mage import Mage8
from Solver.RubarBE import Rubar3
-from tools import logger_exception, pamhyr_db_need_update
+from tools import logger_exception, pamhyr_db_need_update, timer
from PyQt5 import QtGui
from PyQt5.QtGui import (
@@ -84,10 +84,12 @@ from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import (
SelectSolverWindow,
SolverLogWindow,
- CompareSolversWindow
)
from View.Results.Window import ResultsWindow
+from View.Results.CompareDialog import (
+ CompareSolversWindow, CompareScenariosWindow
+)
from View.RunSolver.WindowAdisTS import (
SelectSolverWindowAdisTS,
@@ -128,8 +130,8 @@ no_model_action = [
model_action = [
"action_menu_close", "action_menu_edit", "action_menu_save",
"action_menu_save_as", "action_toolBar_close", "action_toolBar_save",
- "action_menu_numerical_parameter", "action_open_results_from_file",
- "action_open_results_adists",
+ "action_menu_numerical_parameter", "action_menu_open_results_from_file",
+ "action_menu_open_results_adists",
]
other_model_action = [
@@ -150,8 +152,10 @@ define_model_action = [
"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_additional_file",
- "action_menu_results_last", "action_open_results_from_file",
- "action_compare_results", "action_menu_boundary_conditions_sediment",
+ "action_menu_results_last", "action_menu_open_results_from_file",
+ # "action_menu_compare_results",
+ "action_menu_compare_scenarios_results",
+ "action_menu_boundary_conditions_sediment",
"action_menu_rep_additional_lines", "action_menu_output_rk",
"action_menu_run_adists", "action_menu_pollutants",
"action_menu_d90", "action_menu_dif",
@@ -296,9 +300,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_rep_additional_lines": self.open_rep_lines,
"action_menu_close": self.close_model,
"action_menu_results_last": self.open_last_results,
- "action_open_results_from_file": self.open_results_from_file,
- "action_compare_results": self.compare_results,
- "action_open_results_adists": self.open_results_adists,
+ "action_menu_open_results_from_file": self.open_results_from_file,
+ # "action_menu_compare_results": self.compare_results,
+ "action_menu_compare_scenarios_results":
+ self.compare_results_scenarios,
+ "action_menu_open_results_adists": self.open_results_adists,
# Help
"action_menu_pamhyr_users_wiki": self.open_doc_user,
"action_menu_pamhyr_developers_pdf":
@@ -596,7 +602,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if self._study is None:
return None
- return self._study.results
+ results = self._study.results
+ if self._last_solver._type in results:
+ return self._study.results[self._last_solver._type]
+
+ return None
@last_results.setter
def last_results(self, results):
@@ -1507,7 +1517,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return False
def open_solver_results(self, solver, results=None):
- logger.info(f"{solver} {results}")
+ logger.info(f"Open results for '{solver}' ({results})")
def reading_fn():
self._tmp_results = results
@@ -1637,12 +1647,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
res.show()
- def _solver_workdir(self, solver):
+ def _solver_workdir(self, solver, scenario=None):
+ if scenario is None:
+ scenario = self._study.status.scenario
+
workdir = os.path.join(
os.path.dirname(self._study.filename),
"_PAMHYR_",
self._study.name.replace(" ", "_"),
solver.name.replace(" ", "_"),
+ scenario.workdir(),
)
return workdir
@@ -1697,32 +1711,73 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
results=file_name[0]
)
- def compare_results(self):
+ # def compare_results(self):
+ # if self._study is None:
+ # return
+
+ # run = CompareSolversWindow(
+ # study=self._study,
+ # config=self.conf,
+ # parent=self
+ # )
+ # if not run.exec():
+ # return
+
+ # results = self.diff_results(
+ # run.solver1, run.solver2
+ # )
+
+ # # At least one result not available
+ # if results is None:
+ # return
+
+ # # Windows already opened
+ # if self.sub_window_exists(
+ # CompareSolversWindow,
+ # data=[self._study, None] +
+ # [r._solver for r in results] +
+ # [r._repertory for r in results] +
+ # [r._name for r in results]
+ # ):
+ # return
+
+ # res = ResultsWindow(
+ # study=self._study,
+ # results=results,
+ # parent=self
+ # )
+ # res.show()
+
+ def compare_results_scenarios(self):
if self._study is None:
return
- run = CompareSolversWindow(
+ dlg = CompareScenariosWindow(
study=self._study,
config=self.conf,
parent=self
)
- if run.exec():
- results = self.diff_results(run.solver1,
- run.solver2)
- else:
+ if not dlg.exec():
return
+ results = self.diff_results(
+ dlg.solver1, dlg.solver2,
+ scenario1=dlg.scenario1,
+ scenario2=dlg.scenario2
+ )
+
# At least one result not available
if results is None:
return
# Windows already opened
if self.sub_window_exists(
- ResultsWindow,
+ CompareScenariosWindow,
data=[self._study, None] +
[r._solver for r in results] +
[r._repertory for r in results] +
- [r._name for r in results]
+ [r._name for r in results] +
+ [dlg.scenario1, dlg.scenario2]
):
return
@@ -1733,97 +1788,109 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
res.show()
- def diff_results(self, solver1, solver2):
- if solver1 is None:
- # TODO message
- return None
- if solver2 is None:
- # TODO message
+ def msg_diff_results_param(self):
+ self.message_box(
+ window_title=self._trad["Error"],
+ text=self._trad["mb_diff_results_title"],
+ informative_text=self._trad["mb_diff_results_param_msg"]
+ )
+
+ def msg_diff_results_compatibility(self):
+ self.message_box(
+ window_title=self._trad["Error"],
+ text=self._trad["mb_diff_results_title"],
+ informative_text=self._trad["mb_diff_results_compatibility_msg"]
+ )
+
+ @timer
+ def diff_results(self, solver1, solver2,
+ scenario1=None, scenario2=None):
+ if solver1 is None or solver2 is None:
+ self.msg_diff_results_param()
return None
- solver3 = GenericSolver(solver1.name+" - "+solver2.name)
- solver4 = GenericSolver(solver1.name)
- solver5 = GenericSolver(solver2.name)
+ study1 = self._study
+ study2 = self._study
+
+ s3_name = solver1.name + " <> " + solver2.name
+
+ if not (scenario2 is None or scenario2 is None):
+ study1 = self._study.copy_from_scenario(scenario1)
+ study2 = self._study.copy_from_scenario(scenario2)
+
+ s3_name = scenario1.name + " <> " + scenario2.name
+
+ solver3 = GenericSolver(s3_name)
result1 = solver1.results(
- self._study,
- self._solver_workdir(solver1),
- )
-
- if result1 is None:
- # TODO message
- return None
+ study1,
+ self._solver_workdir(solver1, scenario=scenario1),
+ )
result2 = solver2.results(
- self._study,
- self._solver_workdir(solver2),
- )
+ study2,
+ self._solver_workdir(solver2, scenario=scenario2),
+ )
- if result2 is None:
- # TODO message
+ if result1 is None or result2 is None:
+ self.msg_diff_results_param()
return None
- if result2.get("nb_reach") != result1.get("nb_reach"):
- # TODO message
+ if result1.get("nb_reach") != result2.get("nb_reach"):
+ self.msg_diff_results_compatibility()
return None
- if result2.get("nb_profile") != result1.get("nb_profile"):
- # TODO message
+ if result1.get("nb_profile") != result2.get("nb_profile"):
+ self.msg_diff_results_compatibility()
return None
- # return [result1, result2]
+ return self._diff_results(
+ solver1, solver2, solver3,
+ result1, result2,
+ )
+
+ @timer
+ def _diff_results(self, solver1, solver2, solver3, result1, result2):
+ result3 = Results(study=self._study, solver=solver3)
+ ts = sorted(
+ list(
+ result1.get("timestamps").intersection(
+ result2.get("timestamps")
+ )
+ )
+ )
- result3 = Results(self._study, solver3)
- result4 = Results(self._study, solver1)
- result5 = Results(self._study, solver2)
result3.set("nb_reach", result1.get("nb_reach"))
- result4.set("nb_reach", result1.get("nb_reach"))
- result5.set("nb_reach", result1.get("nb_reach"))
result3.set("nb_profile", result1.get("nb_profile"))
- result4.set("nb_profile", result1.get("nb_profile"))
- result5.set("nb_profile", result1.get("nb_profile"))
- ts = sorted(list(result1.get("timestamps").intersection(
- result2.get("timestamps"))))
result3.set("timestamps", ts)
- result4.set("timestamps", ts)
- result5.set("timestamps", ts)
for i in range(int(result1.get("nb_reach"))):
- # Add reach to results reach list
r = result3.river.add(i)
- r = result4.river.add(i)
- r = result5.river.add(i)
for timestamp in result3.get("timestamps"):
for r in range(int(result1.get("nb_reach"))):
reach1 = result1.river.reach(r)
reach2 = result2.river.reach(r)
reach3 = result3.river.reach(r)
- reach4 = result4.river.reach(r)
- reach5 = result5.river.reach(r)
- for p, (profile1, profile2) in enumerate(zip(
- reach1.profiles, reach2.profiles)):
+
+ for profile1, profile2, profile3 in zip(
+ reach1.profiles,
+ reach2.profiles,
+ reach3.profiles):
for key in ["Z", "Q", "V"]:
d1 = profile1.get_ts_key(timestamp, key)
d2 = profile2.get_ts_key(timestamp, key)
- d = d1-d2
- reach3.set(p, timestamp, key, d)
- reach4.set(p, timestamp, key, d1)
- reach5.set(p, timestamp, key, d2)
- limits = reach3.profile(p).geometry.get_water_limits(
- reach3.profile(p).get_ts_key(timestamp, "Z")
- )
- reach3.set(
- p, timestamp,
- "water_limits",
- limits
- )
- limits = profile1.get_ts_key(timestamp, "water_limits")
- reach4.set(p, timestamp, "water_limits", limits)
- limits = profile2.get_ts_key(timestamp, "water_limits")
- reach5.set(p, timestamp, "water_limits", limits)
+ d = d1 - d2
- return [result4, result5, result3]
+ profile3.set(timestamp, key, d)
+
+ limits = profile3.geometry\
+ .get_water_limits(
+ profile3.get_ts_key(timestamp, "Z")
+ )
+ profile3.set(timestamp, "water_limits", limits)
+
+ return [result1, result2, result3]
def open_results_adists(self):
if self._study is None:
@@ -1866,6 +1933,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
solver_results_adists = solver_results.results(
self._study,
repertory=dir_path, qlog=None) # self._output)
+
logger.info(f"Select results: {dir_path}")
if len(bin_list) >= 2 and ("total_sediment.bin" in bin_list):
self.open_solver_results_adists(
diff --git a/src/View/Results/CompareDialog.py b/src/View/Results/CompareDialog.py
new file mode 100644
index 00000000..b77a133d
--- /dev/null
+++ b/src/View/Results/CompareDialog.py
@@ -0,0 +1,252 @@
+import os
+import logging
+import tempfile
+
+from queue import Queue
+from tools import trace, timer, logger_exception
+
+from View.Tools.PamhyrWindow import PamhyrDialog, PamhyrWindow
+
+from PyQt5.QtGui import (
+ QKeySequence,
+)
+
+from PyQt5.QtCore import (
+ Qt, QVariant, QAbstractTableModel,
+ QCoreApplication, QModelIndex, pyqtSlot,
+ QRect, QTimer, QProcess,
+)
+
+from PyQt5.QtWidgets import (
+ QDialogButtonBox, QPushButton, QLineEdit,
+ QFileDialog, QTableView, QAbstractItemView,
+ QUndoStack, QShortcut, QAction, QItemDelegate,
+ QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
+ QTextEdit,
+)
+
+from View.WaitingDialog import WaitingDialog
+
+_translate = QCoreApplication.translate
+
+logger = logging.getLogger()
+
+
+class CompareSolversWindow(PamhyrDialog):
+ _pamhyr_ui = "CompareSolvers"
+ _pamhyr_name = "Compare solvers"
+
+ def __init__(self, study=None, config=None,
+ parent=None):
+ self._solver1 = None
+ self._solver2 = None
+
+ name = _translate("Solver", "Compare solvers")
+ super(CompareSolversWindow, self).__init__(
+ title=name,
+ study=study,
+ config=config,
+ options=[],
+ parent=parent
+ )
+
+ self.setup_solvers()
+ self.setup_connections()
+ self.select_last_solver()
+
+ def setup_solvers(self):
+ solvers = self._config.solvers
+ solvers_name = list(
+ map(
+ self._format_solver_name,
+ solvers
+ )
+ )
+
+ self.combobox_add_items("comboBox1", solvers_name)
+ self.combobox_add_items("comboBox2", solvers_name)
+
+ def setup_connections(self):
+ self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
+ self.find(QPushButton, "pushButton_cancel")\
+ .clicked.connect(self.reject)
+
+ def select_last_solver(self):
+ solvers = self._config.solvers
+ last = self._config.last_solver_name
+
+ solver = list(
+ filter(
+ lambda s: s.name == last,
+ solvers
+ )
+ )
+
+ if len(solver) != 0:
+ self.set_combobox_text(
+ "comboBox1",
+ self._format_solver_name(solver[0])
+ )
+
+ def _format_solver_name(self, solver):
+ return f"{solver.name} - ({solver._type})"
+
+ @property
+ def solver1(self):
+ return self._solver1
+
+ @property
+ def solver2(self):
+ return self._solver2
+
+ def accept(self):
+ solver_name1 = self.get_combobox_text("comboBox1")
+ solver_name1 = solver_name1.rsplit(" - ", 1)[0]
+
+ self._solver1 = next(
+ filter(
+ lambda s: s.name == solver_name1,
+ self._config.solvers
+ )
+ )
+ solver_name2 = self.get_combobox_text("comboBox2")
+ solver_name2 = solver_name2.rsplit(" - ", 1)[0]
+
+ self._solver2 = next(
+ filter(
+ lambda s: s.name == solver_name2,
+ self._config.solvers
+ )
+ )
+
+ super(CompareSolversWindow, self).accept()
+
+
+class CompareScenariosWindow(PamhyrDialog):
+ _pamhyr_ui = "CompareScenarios"
+ _pamhyr_name = "Compare scenarios"
+
+ def __init__(self, study=None, config=None,
+ parent=None):
+ self._solver1 = None
+ self._solver2 = None
+ self._scenario1 = None
+ self._scenario2 = None
+
+ name = _translate("Solver", "Compare solvers")
+ super(CompareScenariosWindow, self).__init__(
+ title=name,
+ study=study,
+ config=config,
+ options=[],
+ parent=parent
+ )
+
+ self.setup_solvers()
+ self.setup_scenarios()
+ self.setup_connections()
+ self.select_last_solver()
+
+ def setup_solvers(self):
+ solvers = self._config.solvers
+ solvers_name = list(
+ map(
+ self._format_solver_name, solvers
+ )
+ )
+
+ self.combobox_add_items("comboBoxSolver1", solvers_name)
+ self.combobox_add_items("comboBoxSolver2", solvers_name)
+
+ def setup_scenarios(self):
+ scenarios = self._study.scenarios.lst
+ scenarios_name = list(
+ map(
+ lambda s: s.name, scenarios
+ )
+ )
+
+ self.combobox_add_items("comboBoxScenario1", scenarios_name)
+ self.combobox_add_items("comboBoxScenario2", scenarios_name)
+
+ def setup_connections(self):
+ self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
+ self.find(QPushButton, "pushButton_cancel")\
+ .clicked.connect(self.reject)
+
+ def select_last_solver(self):
+ solvers = self._config.solvers
+ last = self._config.last_solver_name
+
+ solver = list(
+ filter(
+ lambda s: s.name == last,
+ solvers
+ )
+ )
+
+ if len(solver) != 0:
+ self.set_combobox_text(
+ "comboBoxSolver1",
+ self._format_solver_name(solver[0])
+ )
+ self.set_combobox_text(
+ "comboBoxSolver2",
+ self._format_solver_name(solver[0])
+ )
+
+ def _format_solver_name(self, solver):
+ return f"{solver.name} - ({solver._type})"
+
+ @property
+ def solver1(self):
+ return self._solver1
+
+ @property
+ def solver2(self):
+ return self._solver2
+
+ @property
+ def scenario1(self):
+ return self._scenario1
+
+ @property
+ def scenario2(self):
+ return self._scenario2
+
+ def accept(self):
+ solver_name1 = self.get_combobox_text("comboBoxSolver1")
+ solver_name1 = solver_name1.rsplit(" - ", 1)[0]
+ self._solver1 = next(
+ filter(
+ lambda s: s.name == solver_name1,
+ self._config.solvers
+ )
+ )
+
+ solver_name2 = self.get_combobox_text("comboBoxSolver2")
+ solver_name2 = solver_name2.rsplit(" - ", 1)[0]
+ self._solver2 = next(
+ filter(
+ lambda s: s.name == solver_name2,
+ self._config.solvers
+ )
+ )
+
+ scenario_name1 = self.get_combobox_text("comboBoxScenario1")
+ self._scenario1 = next(
+ filter(
+ lambda s: s.name == scenario_name1,
+ self._study.scenarios
+ )
+ )
+
+ scenario_name2 = self.get_combobox_text("comboBoxScenario2")
+ self._scenario2 = next(
+ filter(
+ lambda s: s.name == scenario_name2,
+ self._study.scenarios
+ )
+ )
+
+ super(CompareScenariosWindow, self).accept()
diff --git a/src/View/RunSolver/Window.py b/src/View/RunSolver/Window.py
index 22b24bbc..64134c7d 100644
--- a/src/View/RunSolver/Window.py
+++ b/src/View/RunSolver/Window.py
@@ -138,107 +138,6 @@ class SelectSolverWindow(PamhyrDialog):
super(SelectSolverWindow, self).accept()
-class CompareSolversWindow(PamhyrDialog):
- _pamhyr_ui = "CompareSolvers"
- _pamhyr_name = "Compare solvers"
-
- def __init__(self, study=None, config=None,
- parent=None):
- self._solver1 = None
- self._solver2 = None
-
- name = _translate("Solver", "Compare solvers")
- super(CompareSolversWindow, self).__init__(
- title=name,
- study=study,
- config=config,
- options=[],
- parent=parent
- )
-
- self.setup_combobox1()
- self.setup_combobox2()
- self.setup_connections()
- self.select_last_solver()
-
- def setup_combobox1(self):
- solvers = self._config.solvers
- solvers_name = list(
- map(
- self._format_solver_name,
- solvers
- )
- )
-
- self.combobox_add_items("comboBox1", solvers_name)
-
- def setup_combobox2(self):
- solvers = self._config.solvers
- solvers_name = list(
- map(
- self._format_solver_name,
- solvers
- )
- )
-
- self.combobox_add_items("comboBox2", solvers_name)
-
- def setup_connections(self):
- self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
- self.find(QPushButton, "pushButton_cancel")\
- .clicked.connect(self.reject)
-
- def select_last_solver(self):
- solvers = self._config.solvers
- last = self._config.last_solver_name
-
- solver = list(
- filter(
- lambda s: s.name == last,
- solvers
- )
- )
-
- if len(solver) != 0:
- self.set_combobox_text(
- "comboBox1",
- self._format_solver_name(solver[0])
- )
-
- def _format_solver_name(self, solver):
- return f"{solver.name} - ({solver._type})"
-
- @property
- def solver1(self):
- return self._solver1
-
- @property
- def solver2(self):
- return self._solver2
-
- def accept(self):
- solver_name1 = self.get_combobox_text("comboBox1")
- solver_name1 = solver_name1.rsplit(" - ", 1)[0]
-
- self._solver1 = next(
- filter(
- lambda s: s.name == solver_name1,
- self._config.solvers
- )
- )
- solver_name2 = self.get_combobox_text("comboBox2")
- solver_name2 = solver_name2.rsplit(" - ", 1)[0]
-
- self._solver2 = next(
- filter(
- lambda s: s.name == solver_name2,
- self._config.solvers
- )
- )
-
- super(CompareSolversWindow, self).accept()
-
-
class SolverLogWindow(PamhyrWindow):
_pamhyr_ui = "SolverLog"
_pamhyr_name = "Solver Log"
diff --git a/src/View/Translate.py b/src/View/Translate.py
index e9a24a4d..30434d4f 100644
--- a/src/View/Translate.py
+++ b/src/View/Translate.py
@@ -228,6 +228,18 @@ class MainTranslate(UnitTranslate):
"Do you still want to open those results?"
)
+ self._dict["mb_diff_results_title"] = _translate(
+ "MainWindow", "Results compare"
+ )
+ self._dict["mb_diff_results_param_msg"] = _translate(
+ "MainWindow", "Results comparison parameters is invalid"
+ )
+ self._dict["mb_diff_results_compatibility_msg"] = _translate(
+ "MainWindow",
+ "Results comparison with two "
+ "incompatible study version"
+ )
+
self._dict["x"] = _translate("MainWindow", "X (m)")
self._dict["y"] = _translate("MainWindow", "Y (m)")
self._dict["Yes"] = _translate("MainWindow", "Yes")
diff --git a/src/View/ui/CompareScenarios.ui b/src/View/ui/CompareScenarios.ui
new file mode 100644
index 00000000..8f1b18a5
--- /dev/null
+++ b/src/View/ui/CompareScenarios.ui
@@ -0,0 +1,79 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 488
+ 117
+
+
+
+ Dialog
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Cancel
+
+
+
+ -
+
+
+ OK
+
+
+
+
+
+
+
+
+
+
diff --git a/src/View/ui/MainWindow.ui b/src/View/ui/MainWindow.ui
index 22d1c542..96661c4f 100644
--- a/src/View/ui/MainWindow.ui
+++ b/src/View/ui/MainWindow.ui
@@ -160,9 +160,9 @@
&Results
-
-
-
+
+
+