wolfhece 1.8.13__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. wolfhece/GraphNotebook.py +0 -1
  2. wolfhece/PyCrosssections.py +591 -5
  3. wolfhece/PyDraw.py +1151 -413
  4. wolfhece/PyGui.py +2 -4
  5. wolfhece/PyParams.py +1515 -852
  6. wolfhece/PyVertex.py +73 -73
  7. wolfhece/PyVertexvectors.py +226 -808
  8. wolfhece/RatingCurve.py +19 -6
  9. wolfhece/apps/wolf2D.py +11 -0
  10. wolfhece/apps/wolfcompare2Darrays.py +51 -22
  11. wolfhece/bernoulli/NetworkOpenGL.py +337 -341
  12. wolfhece/drawing_obj.py +25 -0
  13. wolfhece/hydrology/Catchment.py +77 -77
  14. wolfhece/hydrology/Optimisation.py +206 -53
  15. wolfhece/hydrology/PostProcessHydrology.py +22 -22
  16. wolfhece/hydrology/SubBasin.py +17 -17
  17. wolfhece/hydrology/constant.py +4 -0
  18. wolfhece/hydrology/cst_exchanges.py +2 -1
  19. wolfhece/lazviewer/processing/estimate_normals/estimate_normals.cp310-win_amd64.pyd +0 -0
  20. wolfhece/lazviewer/vfuncs/vfuncs.cp310-win_amd64.pyd +0 -0
  21. wolfhece/libs/WolfDll.dll +0 -0
  22. wolfhece/libs/wolfogl.cp310-win_amd64.pyd +0 -0
  23. wolfhece/libs/wolfpy.cp310-win_amd64.pyd +0 -0
  24. wolfhece/mesh2d/wolf2dprev.py +4 -4
  25. wolfhece/multiprojects.py +13 -13
  26. wolfhece/pylogging.py +1 -1
  27. wolfhece/pyviews.py +23 -23
  28. wolfhece/wolf_array.py +69 -152
  29. wolfhece/wolf_texture.py +39 -16
  30. wolfhece/wolfresults_2D.py +1 -1
  31. {wolfhece-1.8.13.dist-info → wolfhece-2.0.0.dist-info}/METADATA +3 -2
  32. {wolfhece-1.8.13.dist-info → wolfhece-2.0.0.dist-info}/RECORD +37 -33
  33. wolfhece/apps/wolfgpu.py +0 -19
  34. /wolfhece/lazviewer/processing/estimate_normals/{estimate_normals.pyd → estimate_normals.cp39-win_amd64.pyd} +0 -0
  35. /wolfhece/lazviewer/vfuncs/{vfuncs.pyd → vfuncs.cp39-win_amd64.pyd} +0 -0
  36. {wolfhece-1.8.13.dist-info → wolfhece-2.0.0.dist-info}/LICENCE +0 -0
  37. {wolfhece-1.8.13.dist-info → wolfhece-2.0.0.dist-info}/WHEEL +0 -0
  38. {wolfhece-1.8.13.dist-info → wolfhece-2.0.0.dist-info}/top_level.txt +0 -0
wolfhece/PyParams.py CHANGED
@@ -5,6 +5,9 @@ import os.path
5
5
  import json
6
6
  import logging
7
7
  from typing import Union, Literal
8
+ from enum import Enum
9
+ from copy import deepcopy
10
+ import numpy as np
8
11
 
9
12
  try:
10
13
  from .PyTranslate import _
@@ -17,112 +20,290 @@ if not '_' in __builtins__:
17
20
 
18
21
  PARAM_TRUE = '.True.'
19
22
  PARAM_FALSE = '.False.'
20
-
21
- def new_json(values:dict, fullcomment:str=''):
23
+ PREFIX_DEFAULT = 'def'
24
+
25
+ class Type_Param(Enum):
26
+ """
27
+ Enum to define the type of a parameter
28
+
29
+ Strings are also used by Fortran Code -- modify with care
30
+ """
31
+ Integer_or_Float = 'Integer_or_Float'
32
+ Integer = 'integer'
33
+ Logical = 'Logical'
34
+ Float = 'Float'
35
+ File = 'File'
36
+ file = 'file'
37
+ Directory = 'Directory'
38
+ directory = 'directory'
39
+ Color = 'Color'
40
+ Fontname = 'Fontname'
41
+ String = 'String'
42
+ Empty = ''
43
+ Double = 'double'
44
+ Real = 'real'
45
+
46
+
47
+ class key_Param(Enum):
48
+ """
49
+ Enum to define the keys of a parameter
50
+
51
+ """
52
+ NAME = 'name'
53
+ TYPE = 'type'
54
+ VALUE = 'value'
55
+ COMMENT = 'comment'
56
+ ADDED_JSON = 'added_json'
57
+
58
+ class Buttons(Enum):
59
+ """ Enum to define the buttons """
60
+ Load = 'Load'
61
+ Save = 'Save'
62
+ Apply = 'Apply'
63
+ Reload = 'Reload'
64
+
65
+ def new_json(values:dict, fullcomment:str='') -> dict:
66
+ """
67
+ Create a new JSON string from values and fullcomment
68
+
69
+ @param values : values to store in the JSON string - dict of key:value - value must be integer
70
+ @param fullcomment : full comment to store in the JSON string - str - can be multiline with \n
71
+ """
22
72
  return {"Values":values, "Full_Comment":fullcomment}
23
73
 
24
- #Gestion des paramètres au format WOLF
74
+ def new_infos_incr(groupname:str= None, paramname:str='nb', min:int=1, max:int=100) -> tuple[str]:
75
+ """
76
+ Create a new string for an incrementable group or parameter
77
+
78
+ @param groupname : name of the reference group (optional) -- if ommitted, the reference group is in the same group as the parameter
79
+ @param paramname : name of the reference parameter
80
+ @param min : minimum value
81
+ @param max : maximum value
82
+ """
83
+ if groupname is None:
84
+ return paramname, str(min), str(max)
85
+ else:
86
+ return groupname, paramname, str(min), str(max)
87
+
88
+ # FIXME : Généliser avec les dictionnaire avec Enum
89
+ def search_type(chain:str) -> Type_Param:
90
+ """ recherche du typage dans une chaîne de caractères """
91
+
92
+ if chain.lower().find(Type_Param.Integer.value)>-1 and chain.find(Type_Param.Double.value)>-1:
93
+ return Type_Param.Integer_or_Float
94
+ elif chain.lower().find(Type_Param.Integer.value)>-1:
95
+ return Type_Param.Integer
96
+ elif chain.find(Type_Param.Logical.value)>-1:
97
+ return Type_Param.Logical
98
+ elif chain.find(Type_Param.Double.value)>-1 or chain.find('dble')>-1 or chain.find(Type_Param.Real.value)>-1:
99
+ return Type_Param.Float
100
+ elif chain.find('({})'.format(Type_Param.file))>-1:
101
+ return Type_Param.File
102
+ elif chain.find('({})'.format(Type_Param.directory))>-1 or chain.find('(dir)')>-1:
103
+ return Type_Param.Directory
104
+ else:
105
+ return Type_Param.String
106
+
107
+
25
108
  class Wolf_Param(wx.Frame):
26
- #Définition des propriétés
27
- filename:str
28
- myparams:dict[str, dict]
29
- myparams_default:dict[str, dict]
30
- myIncGroup:dict
31
- myIncParam:dict
32
- prop:pg.PropertyGridManager
33
- wx_exists:bool
109
+ """
110
+ **FR**
111
+ Gestion des paramètres au format WOLF.
34
112
 
35
- def addparam(self,
36
- groupname='',
37
- name='',
38
- value='',
39
- type:Literal['Integer_or_Float',
40
- 'Integer',
41
- 'Logical',
42
- 'Float',
43
- 'File',
44
- 'Directory',
45
- 'Color',
46
- 'Fontname',
47
- '']='',
48
- comment='',
49
- jsonstr=None,
50
- whichdict:Literal['All', 'Default', 'Active', '']=''):
51
- """
52
- Add or update a parameter
113
+ Fichier texte
114
+ -------------
53
115
 
54
- @param groupname : groupe in which the new param will be strored - If it does not exist, it will be created
55
- @param name : param's name - If it does not exist, it will be created
56
- @param value : param'a value
57
- @param type : type -> will influence the GUI
58
- @param comment : param's comment -- helpful to understand the parameter
59
- @param jsonstr : string containing JSON data -- used in GUI
60
- param whichdict : where to store the param -- Default, Active or All
116
+ Les fichiers '*.param' sont des fichiers texte contenant des paramètres de type nom=valeur et compatibles avec les codes Fortran.
117
+ L'extension '.param.default' est utilisée pour stocker les paramètres par défaut.
61
118
 
62
- jsonstr can be a dict i.e. '{"Values:{choice1:1, choice2:2, choice3:3}, Full_Comment:'Yeah baby !'}'
63
- """
64
- if whichdict=='All':
65
- locparams=[self.myparams,self.myparams_default]
66
- elif whichdict=='Default':
67
- locparams=[self.myparams_default]
68
- elif whichdict=='Active' or whichdict=='':
69
- locparams=[self.myparams]
119
+ Une autre extension est possible mais un fichier '.default' sera créé automatiquement si ce fichier n'existe pas.
70
120
 
71
- for curdict in locparams:
72
- if not groupname in curdict.keys():
73
- curdict[groupname]={}
121
+ Le séparateur (nom, valeur, commentaire) est la tabulation '\t'. Aucun autre caractère ne doit être utilisé comme séparateur.
74
122
 
75
- if not name in curdict[groupname].keys():
76
- curdict[groupname][name]={}
123
+ Les groupes sont définis par un nom suivi de ':'. Cela signifie que ':' ne peut pas être utilisé dans un nom de paramètre.
77
124
 
78
- curpar=curdict[groupname][name]
125
+ Les lignes débutant par '%' sont des commentaires. Il est possible d'ajouter du code JSON dans un commentaire. Pour cela, il faut ajouter '%json' au début de la ligne suivi d'un dictionnaire (e.g. %json{"Values":{'key1':1, 'key2':2}, "Full_Comment":"fullcomment"} ).
79
126
 
80
- curpar['name']=name
81
- curpar['type']=type
82
- curpar['value']=value
83
- curpar['comment']=comment
127
+ Organisation Python
128
+ -------------------
84
129
 
85
- if jsonstr is not None:
86
- if isinstance(jsonstr, str):
87
- parsed_json = json.loads(jsonstr)
88
- elif isinstance(jsonstr, dict):
89
- parsed_json = jsonstr
130
+ L'objet Python est basé sur des dictionnaires Python. Un dictionnaire par groupe de paramètres.
90
131
 
91
- curpar['added_json']=parsed_json
132
+ Les paramètres sont donc stockés dans un dictionnaire de dictionnaires. Le premier niveau est le nom du groupe, le second niveau est le nom du paramètre.
92
133
 
93
- def __getitem__(self, key):
94
- """
95
- Retrieve :
96
- - value's parameter from group if key is a tuple or a list (group, param_name)
97
- - group dict if key is a string
98
- """
99
- if isinstance(key, tuple) or isinstance(key, list):
100
- group, name = key
101
- return self.get_param(group, name)
102
- elif isinstance(key, str):
103
- return self.get_group(key)
134
+ Les paramètres disposent des clés suivantes :
135
+ - name : nom du paramètre (str)
136
+ - type : type du paramètre (str) -- see Type_Param
137
+ - value : valeur du paramètre (str) -- peut être converti dynamiquement en int, float, bool, str, ... sur base du type
138
+ - comment : commentaire du paramètre (str) -- helpful to understand the parameter
139
+ - added_json : dictionnaire contenant des informations supplémentaires (optionnel) -- permet de stocker des informations supplémentaires sur le paramètre (ex : valeurs possibles, commentaires étendus, ...)
104
140
 
105
- def __setitem__(self, key, value):
106
- """set item, key is a tuple or a list (group, param_name)"""
141
+ Dictionnaires
142
+ -------------
107
143
 
108
- if isinstance(key, tuple) or isinstance(key, list):
109
- group, name = key
110
- if self.get_param(group, name) is not None:
111
- self.change_param(group, name, value)
112
- else:
113
- self.addparam(group, name, value)
144
+ Il existe un dictionnaire de valeurs par défaut "myparams_default". Pour l'interaction Python-Fortran, c'est le Fortran qui écrit ces paramètres.
145
+ Il existe un dictionnaire de paramètres actifs "myparams". Il est utilisé pour stocker les paramètres modifiés par l'utilisateur. Normalement, seuls les paramètres modifiés par l'utilisateur sont stockés dans ce dictionnaire et sont écrits sur disque.
146
+
147
+ Groupe/Paramètre incrémentable
148
+ ------------------------------
149
+
150
+ Il est également possible de définir des groupes ou des paramètres incrémentables.
151
+ Pour cela, dans le nom du groupe/paramètre, il faut ajouter, à l'emplacement souhaité du **numéro** du groupe/paramètre, des informations entre '$...$' :
152
+ - groupname : nom du groupe (uniquement pour groupe incrémentable)
153
+ - paramname : nom du paramètre contenant le nombre de groupe/paramètre
154
+ - min : valeur minimale
155
+ - max : valeur maximale
156
+
157
+ Le nombre de groupes est ainsi défini par le couple (key:value) = (groupname:paramname). Le nombre de groupes doit donc être logiquement positionné dans un groupe distinct.
158
+ Le nombre de paramètres est ainsi défini par le paramètre "paramname" qui est attendu dans le groupe contenant le paramètre incrémentable.
159
+ Le nombre de groupes/paramètres est un entier compris entre 'min' et 'max'.
160
+
161
+ Les informations génériques sont stockées dans les dictionnaires "myIncGroup" et "myIncParam".
162
+
163
+ UI
164
+ --
165
+
166
+ Une interface graphique est disponible pour modifier les paramètres. Elle est basée sur "wxPython" et la classe "wxPropertyGrid".
167
+ L'attribut wx_exists permet de savoir si wxPython est en cours d'excution ou non.
168
+
169
+ Accès aux données
170
+ -----------------
171
+
172
+ Les paramètres sont accessibles via la procédure __getitem__ en fournissant un tuple (groupname, paramname).
173
+ Il est possible de modifier un paramètre via la procédure __setitem__.
174
+
175
+ Il est possible :
176
+ - d'ajoutrer un groupe via la procédure add_group. Pour un groupe incrémentabe, le nom doit contenir les infos génériques entre '$...$'.
177
+ - d'ajouter un paramètre ou un paramètre incrémentable via la procédure addparam :
178
+ - pour un paramètre classique en choisissant le dictionnaire cible ['All', 'Default', 'Active', 'IncGroup', '']
179
+ - '' == 'Active'
180
+ - 'All' == 'Default' + 'Active'
181
+ - 'IncGroup' pour ajouter un paramètre au template du groupe incrémentable --> sera dupliqué lors de la MAJ du nompbre réel de groupes
182
+ - pour un paramètre incrémentable, en fournissant les données nécessaires dans une chaîne $n(refname,min,max)$ ou $n(groupname,refname,min,max)$
183
+ - si le groupe visé n'existe pas, il sera créé si toutes les infos sont disponibles.
184
+ - d'ajouter seulement un paramètre incrémentable via la procédure add_IncParam.
185
+
186
+ **EN**
187
+ Management of parameters in WOLF format.
188
+
189
+ Text File
190
+ ----------
191
+
192
+ '*.param' files are text files containing parameters in the name=value format and compatible with Fortran codes.
193
+ The '.param.default' extension is used to store default parameters.
194
+
195
+ Another extension is possible, but a '.default' file will be automatically created if this file does not exist.
196
+
197
+ The separator (name, value, comment) is the tab character '\t'. No other character should be used as a separator.
198
+
199
+ Groups are defined by a name followed by ':'. This means that ':' cannot be used in a parameter name.
200
+
201
+ Lines starting with '%' are comments. It is possible to add JSON code in a comment. To do this, add '%json' at the beginning of the line followed by a dictionary (e.g., %json{"Values":{'key1':1, 'key2':2}, "Full_Comment":"fullcomment"}).
202
+
203
+ Python Organization
204
+ -------------------
205
+
206
+ The Python object is based on Python dictionaries. One dictionary per parameter group.
207
+
208
+ Therefore, parameters are stored in a dictionary of dictionaries. The first level is the group name, and the second level is the parameter name.
209
+
210
+ Parameters have the following keys:
211
+ - name: parameter name (str)
212
+ - type: parameter type (str) -- see Type_Param
213
+ - value: parameter value (str) -- can be dynamically converted to int, float, bool, str, ... based on the type
214
+ - comment: parameter comment (str) -- helpful to understand the parameter
215
+ - added_json: dictionary containing additional information (optional) -- used to store additional information about the parameter (e.g., possible values, extended comments, ...)
216
+
217
+ Dictionaries
218
+ -------------
219
+
220
+ There is a default values dictionary "myparams_default." For Python-Fortran interaction, Fortran writes these parameters.
221
+ There is an active parameters dictionary "myparams." It is used to store parameters modified by the user. Normally, only parameters modified by the user are stored in this dictionary and written to disk.
222
+
223
+ Incrementable Group/Parameter
224
+ ------------------------------
225
+
226
+ It is also possible to define incrementable groups or parameters.
227
+ To do this, in the group/parameter name, add information between '$...$' at the desired **number** location of the group/parameter:
228
+ - groupname: group name (only for incrementable group)
229
+ - paramname: parameter name containing the group/parameter number
230
+ - min: minimum value
231
+ - max: maximum value
232
+
233
+ The number of groups is defined by the (key:value) pair (groupname:paramname). The number of groups must logically be positioned in a distinct group.
234
+ The number of parameters is defined by the "paramname" parameter expected in the group containing the incrementable parameter.
235
+ The number of groups/parameters is an integer between 'min' and 'max'.
236
+
237
+ Generic information is stored in the "myIncGroup" and "myIncParam" dictionaries.
238
+
239
+ UI
240
+ --
241
+
242
+ A graphical interface is available to modify parameters. It is based on "wxPython" and the "wxPropertyGrid" class.
243
+ The wx_exists attribute indicates whether wxPython is currently running or not.
244
+
245
+ Data Access
246
+ -----------------
247
+
248
+ Parameters are accessible via the __getitem__ method by providing a tuple (groupname, paramname).
249
+ It is possible to modify a parameter via the __setitem__ method.
250
+
251
+ It is possible to:
252
+ - add a group via the add_group method. For an incrementable group, the name must contain generic information between '$...$'.
253
+ - add a parameter or an incrementable parameter via the addparam method:
254
+ - for a regular parameter by choosing the target dictionary ['All', 'Default', 'Active', 'IncGroup', '']
255
+ - '' == 'Active'
256
+ - 'All' == 'Default' + 'Active'
257
+ - 'IncGroup' to add a parameter to the template of the incrementable group --> will be duplicated when updating the actual number of groups
258
+ - for an incrementable parameter, by providing the necessary data in a string $n(refname,min,max)$ or $n(groupname,refname,min,max)$
259
+ - if the targeted group does not exist, it will be created if all information is available.
260
+ - only add an incrementable parameter via the add_IncParam method.
261
+ ```
262
+ """
263
+
264
+ # Définition des propriétés
265
+ filename:str # File name
266
+ myparams:dict[str, dict] # dict for active parameters, see key_Param for keys
267
+ myparams_default:dict[str, dict] # dict for default parameters, see key_Param for keys
268
+
269
+ myIncGroup:dict # dict for incrementable groups
270
+ myIncParam:dict # dict for incrementable parameters
271
+
272
+ prop:pg.PropertyGridManager # wxPropertyGridManager -- see UI
273
+ wx_exists:bool # True if wxPython is running
114
274
 
115
- #Initialisation
116
275
  def __init__(self,
117
- parent=None,
118
- title="Default Title",
119
- w=500, h=800,
120
- ontop=False,
121
- to_read=True,
122
- filename='',
123
- withbuttons=True,
124
- DestroyAtClosing=True,
125
- toShow=True):
276
+ parent:wx.Window = None,
277
+ title:str = "Default Title",
278
+ w:int = 500,
279
+ h:int = 800,
280
+ ontop:bool = False,
281
+ to_read:bool = True,
282
+ filename:str = '',
283
+ withbuttons: bool = True,
284
+ DestroyAtClosing:bool = True,
285
+ toShow:bool = True,
286
+ init_GUI:bool = True):
287
+ """
288
+ Initialisation
289
+
290
+ @param parent : parent frame (wx.Window)
291
+ @param title : title of the frame
292
+ @param w : width of the frame
293
+ @param h : height of the frame
294
+ @param ontop : if True, the frame will be on top of all other windows
295
+ @param to_read : if True, the file will be read
296
+ @param filename : filename to read
297
+ @param withbuttons : if True, buttons will be displayed
298
+ @param DestroyAtClosing : if True, the frame will be destroyed when closed
299
+ @param toShow : if True, the frame will be displayed
300
+
301
+
302
+ Callbacks (see 'set_callbacks'):
303
+ - callback : callback function when 'apply' is pressed
304
+ - callbackdestroy : callback function before destroying the frame
305
+
306
+ """
126
307
 
127
308
  # Initialisation des propriétés
128
309
  self.filename=filename
@@ -130,35 +311,98 @@ class Wolf_Param(wx.Frame):
130
311
  self.myparams_default={}
131
312
  self.myIncGroup={}
132
313
  self.myIncParam={}
314
+ self.update_incr_at_every_change = True
133
315
 
134
- self._callback = None
135
- self._callbackdestroy=None
316
+ self._callback:function = None
317
+ self._callbackdestroy:function = None
136
318
 
137
319
  self.wx_exists = wx.App.Get() is not None # test if wx App is running
320
+ self.show_in_active_if_default = False
138
321
 
139
322
  if to_read:
140
323
  self.ReadFile(filename)
141
324
 
142
- if self.wx_exists:
143
- self.set_gui(parent,title,w,h,ontop,to_read,withbuttons,DestroyAtClosing,toShow)
325
+ if self.wx_exists and init_GUI:
326
+ self._set_gui(parent,title,w,h,ontop,to_read,withbuttons,DestroyAtClosing,toShow)
327
+ else:
328
+ self.prop = None
329
+
330
+ def set_callbacks(self, callback_update, callback_destroy):
331
+ """ Set the callbacks for the update and destroy events """
332
+
333
+ self.callback = callback_update
334
+ self.callbackdestroy = callback_destroy
335
+
336
+ def get_nb_groups(self) -> tuple[int, int]:
337
+ """ Return the number of groups in active and default parameters """
338
+ return len(self.myparams.keys()), len(self.myparams_default.keys())
339
+
340
+ def get_nb_params(self, group:str) -> tuple[int, int]:
341
+ """ Return the number of parameters in a group in active and default parameters """
342
+ return len(self.myparams[group].keys()) if group in self.myparams.keys() else None, len(self.myparams_default[group].keys()) if group in self.myparams_default.keys() else None
343
+
344
+ def get_nb_inc_params(self) -> int:
345
+ """ Return the number of incrementable parameters """
346
+ return len(self.myIncParam.keys())
347
+
348
+ def get_nb_inc_groups(self) -> int:
349
+ """ Return the number of incrementable groups """
350
+ return len(self.myIncGroup.keys())
144
351
 
145
352
  @property
146
353
  def callback(self):
354
+ """ Return the callback function """
147
355
  return self._callback
148
356
 
149
357
  @callback.setter
150
358
  def callback(self, value):
359
+ """ Set the callback function """
151
360
  self._callback= value
152
361
 
153
362
  @property
154
363
  def callbackdestroy(self):
155
- return self.callbackdestroy
364
+ """ Return the callback function """
365
+ return self._callbackdestroy
156
366
 
157
367
  @callbackdestroy.setter
158
368
  def callbackdestroy(self, value):
369
+ """ Set the callback function """
159
370
  self._callbackdestroy= value
160
371
 
161
- def set_gui(self, parent=None, title="Default Title", w=500,h=800,ontop=False,to_read=True,withbuttons=True,DestroyAtClosing=True, toShow=True):
372
+ # GUI Events - WxPython
373
+ # ---------------------
374
+
375
+ def _set_gui(self,
376
+ parent:wx.Window = None,
377
+ title:str = "Default Title",
378
+ w:int = 500,
379
+ h:int = 800,
380
+ ontop:bool = False,
381
+ to_read:bool = True,
382
+ withbuttons:bool = True,
383
+ DestroyAtClosing:bool = True,
384
+ toShow:bool = True):
385
+ """
386
+ Set the GUI if wxPython is running
387
+
388
+ Gui is based on wxPropertyGridManager.
389
+
390
+ On the left, there is a group of buttons to load, save, apply or reload the parameters.
391
+ On the right, there is the wxPropertyGridManager for the default and active parameters. Active parameters are displayed in bold.
392
+
393
+ To activate a parameter, double-click on it in the default tab. It will be copied to the active tab and the value will be modifiable.
394
+
395
+ @param parent : parent frame
396
+ @param title : title of the frame
397
+ @param w : width of the frame
398
+ @param h : height of the frame
399
+ @param ontop : if True, the frame will be on top of all other windows
400
+ @param to_read : if True, the file will be read
401
+ @param withbuttons : if True, buttons will be displayed
402
+ @param DestroyAtClosing : if True, the frame will be destroyed when closed
403
+ @param toShow : if True, the frame will be displayed
404
+
405
+ """
162
406
 
163
407
  #Appel à l'initialisation d'un frame général
164
408
  if ontop:
@@ -221,677 +465,1116 @@ class Wolf_Param(wx.Frame):
221
465
 
222
466
  #ajout du sizert à la page
223
467
  self.SetSizer(self.sizer)
224
- #self.SetSize(w,h)
468
+ # self.SetSize(w,h)
225
469
  self.SetAutoLayout(1)
226
470
  self.sizer.Fit(self)
227
471
 
228
472
  #affichage de la page
229
473
  self.Show(toShow)
230
474
 
231
- def hide_selected_buttons(self):
232
- """ Mask selected buttons but conserve 'Apply change'"""
233
- self.sizerbut.Hide(self.loadme)
234
- self.sizerbut.Hide(self.saveme)
235
- # self.sizerbut.Hide(self.applychange)
236
- self.sizerbut.Hide(self.reloadme)
475
+ def hide_selected_buttons(self, to_hide:list[Buttons] = [Buttons.Load, Buttons.Save, Buttons.Reload]):
476
+ """ Mask selected buttons - Default conserve only 'Apply change' """
477
+
478
+ for locbutton in to_hide:
479
+ if locbutton == Buttons.Load:
480
+ self.sizerbut.Hide(self.loadme)
481
+ elif locbutton == Buttons.Save:
482
+ self.sizerbut.Hide(self.saveme)
483
+ elif locbutton == Buttons.Reload:
484
+ self.sizerbut.Hide(self.reloadme)
485
+ elif locbutton == Buttons.Apply:
486
+ self.sizerbut.Hide(self.applychange)
237
487
 
238
- #Gestion du double-click pour ajouter des éléments ou remise à valeur par défaut
239
- def OnDblClick(self, event):
488
+ def OnDblClick(self, event:wx.MouseEvent):
489
+ """
490
+ Double-click event handler to add a parameter to the active tab or reset to default value.
491
+ Gestion du double-click pour ajouter des éléments ou remise à valeur par défaut.
492
+ """
240
493
 
241
- #obtention de la propriété sur laquelle on a cliqué
494
+ # obtention de la propriété sur laquelle on a cliqué
242
495
  p = event.GetProperty()
243
- #nom et valeur du paramètre
496
+ # nom et valeur du paramètre
244
497
  name = p.GetName()
245
- val = p.GetValue()
498
+ from_default_page = name[0:3]==PREFIX_DEFAULT
499
+ # val = p.GetValue()
246
500
 
247
- #nom du groupe
501
+ # nom du groupe
248
502
  group=p.GetParent()
249
503
  groupname=group.GetName()
250
504
 
251
- #on se place sur la page des paramètres actifs
252
- page = self.prop.GetPage(0)
253
- if name[0:3]=='def':
254
- propname = name[3+len(groupname):]
505
+ # on se place sur la page des paramètres actifs
506
+ page_active:pg.PropertyGridPage
507
+ page_active = self.prop.GetPage(0)
508
+ # on récupère le nom du paramètre sans le nom du groupe
509
+ if from_default_page:
510
+ paramname = name[3+len(groupname):]
255
511
  else:
256
- propname = name[len(groupname):]
512
+ paramname = name[len(groupname):]
513
+
514
+ if not self.is_in_default(groupname):
515
+ logging.debug(_('Group {} not found in default parameters -- Maybe an incrementable group'.format(groupname)))
516
+ return
517
+
518
+ if not self.is_in_default(groupname, paramname):
519
+ logging.warning(_('Param {} not found in default parameters -- Maybe an incrementable param '.format(paramname)))
520
+ return
257
521
 
258
522
  #pointage vers le paramètre par défaut
259
- param_def = self.myparams_default[groupname][propname]
523
+ param_def = self.myparams_default[groupname][paramname]
260
524
 
261
- if name[0:3]=='def':
525
+ if from_default_page:
262
526
  #click depuis la page des param par défaut
263
527
 
264
528
  #essai pour voir si le groupe existe ou non dans les params actifs
265
- try:
266
- locgroup = self.myparams[groupname]
267
- except:
268
- locgroup=None
269
-
270
- #si groupe non existant on ajoute
271
- if locgroup is None:
272
- page.Append(pg.PropertyCategory(groupname))
529
+ if not self.is_in_active(groupname):
530
+ page_active.Append(pg.PropertyCategory(groupname))
273
531
 
274
532
  #teste si param existe
275
- try:
276
- param = self.myparams[groupname][propname]
277
- except:
278
- param=None
279
-
280
- if param is None:
281
- #si non existant --> on ajoute, si existant --> rien
282
- locname = groupname + propname
283
- if 'added_json' in param_def.keys():
284
- list_keys = [ k for k in param_def['added_json']['Values'].keys()]
285
- list_values = [ k for k in param_def['added_json']['Values'].values()]
286
- page.AppendIn(groupname,pg.EnumProperty(propname,name=locname,labels=list_keys,values=list_values,value=int(param_def['value'])))
287
- else:
288
- if param_def['type']=='Integer_or_Float':
289
- page.AppendIn(groupname,pg.IntProperty(propname,name=locname,value=float(param_def['value'])))
290
- elif param_def['type']=='Integer':
291
- page.AppendIn(groupname,pg.IntProperty(propname,name=locname,value=int(param_def['value'])))
292
- elif param_def['type']=='Logical':
293
- if param_def['value']=='.true.' or param_def['value']=='.True.':
294
- mybool=True
295
- elif param_def['value']=='.false.' or param_def['value']=='.False.':
296
- mybool=False
297
- page.AppendIn(groupname,pg.BoolProperty(propname,name=locname,value=mybool))
298
- elif param_def['type']=='Float':
299
- page.AppendIn(groupname,pg.FloatProperty(propname,name=locname,value=float(param_def['value'])))
300
- elif param_def['type']=='File':
301
- page.AppendIn(groupname,pg.FileProperty(propname,name=locname,value=param_def['value']))
302
- elif param_def['type']=='Directory':
303
- newobj=pg.DirProperty(locname,locname,value=param_def['value'])
304
- newobj.SetLabel(propname)
305
- page.Append(newobj)
306
- elif param_def['type']=='Color':
307
- page.AppendIn(groupname,pg.ColourProperty(propname,name=locname,value=param_def['value']))
308
- elif param_def['type']=='Fontname':
309
- page.AppendIn(groupname,pg.FontProperty(propname,name=locname,value=param_def['value']))
310
- else:
311
- page.AppendIn(groupname,pg.StringProperty(propname,name=locname,value=param_def['value']))
312
-
313
- try:
314
- self.prop.SetPropertyHelpString(locname,param_def['added_json']['Full_Comment'])
315
- except:
316
- self.prop.SetPropertyHelpString(locname,param_def['comment'])
533
+ if not self.is_in_active(groupname, paramname):
534
+ #si non existant --> on ajoute, si existant --> rien à faire
535
+ self._add_elem_to_page(page_active, groupname, param_def)
317
536
 
318
537
  else:
319
538
  #recopiage de la valeur par défaut
320
- if param_def['type']=='Integer_or_Float':
321
- self.prop.SetPropertyValue(groupname + propname,float(param_def['value']))
322
- elif param_def['type']=='Integer':
323
- self.prop.SetPropertyValue(groupname + propname,int(param_def['value']))
324
- elif param_def['type']=='Logical':
325
- if param_def['value']=='.true.' or param_def['value']=='.True.':
326
- mybool=True
327
- elif param_def['value']=='.false.' or param_def['value']=='.False.':
328
- mybool=False
329
- self.prop.SetPropertyValue(groupname + propname,mybool)
330
- elif param_def['type']=='Float':
331
- self.prop.SetPropertyValue(groupname + propname,float(param_def['value']))
332
- elif param_def['type']=='File':
333
- self.prop.SetPropertyValue(groupname + propname,param_def['value'])
334
- elif param_def['type']=='Directory':
335
- self.prop.SetPropertyValue(groupname + propname,param_def['value'])
336
- elif param_def['type']=='Color':
337
- self.prop.SetPropertyValue(groupname + propname,param_def['value'])
338
- elif param_def['type']=='Fontname':
339
- self.prop.SetPropertyValue(groupname + propname,param_def['value'])
340
- else:
341
- self.prop.SetPropertyValue(groupname + propname,param_def['value'])
539
+ defvalue = self.value_as_type(param_def[key_Param.VALUE],
540
+ param_def[key_Param.TYPE],
541
+ bool_as_int=True,
542
+ color_as=str)
543
+ self.prop.SetPropertyValue(groupname + paramname, defvalue)
544
+
545
+ def OnClose(self, event:wx.MouseEvent):
546
+ """ Close event of the frame """
547
+ if not self._callbackdestroy is None:
548
+ self._callbackdestroy()
342
549
 
343
- def LoadFromFile(self,event):
344
- self.myparams.clear()
345
- self.myparams_default.clear()
550
+ if self.DestroyAtClosing:
551
+ self.Destroy()
552
+ else:
553
+ self.Hide()
554
+ pass
555
+
556
+ def SavetoFile(self, event:wx.MouseEvent):
557
+ """ sauvegarde dans le fichier texte """
558
+
559
+ with open(self.filename, 'w') as myfile:
560
+
561
+ for group in self.myparams.keys():
562
+ myfile.write(' ' + group +':\n')
563
+ for param_name in self.myparams[group].keys():
564
+ myfile.write(param_name +'\t' + str(self.myparams[group][param_name][key_Param.VALUE])+'\n')
565
+
566
+ myfile.close()
567
+
568
+ def Reload(self, event:wx.MouseEvent):
569
+ """ relecture du fichier sur base du nom déjà connu """
570
+ if self.filename=='':
571
+ logging.warning(_('No filename given'))
572
+ return
573
+ self.Clear()
574
+ self.ReadFile(self.filename)
575
+ self.Populate()
576
+
577
+ def LoadFromFile(self, event:wx.MouseEvent):
578
+ """ Load parameters from file """
579
+
580
+ self.Clear()
581
+ # read the file
346
582
  self.ReadFile()
583
+ # populate the property grid
347
584
  self.Populate()
348
585
 
349
- #Lecture d'un fichier .param
350
- def ReadFile(self,*args):
351
- if len(args)>0:
352
- #s'il y a un argument on le prend tel quel
353
- self.filename = str(args[0])
354
- else:
355
- #ouverture d'une boîte de dialogue
356
- file=wx.FileDialog(self,"Choose .param file", wildcard="param (*.param)|*.param|all (*.*)|*.*")
357
- if file.ShowModal() == wx.ID_CANCEL:
358
- return
359
- else:
360
- #récuparétaion du nom de fichier avec chemin d'accès
361
- self.filename =file.GetPath()
586
+ def ApplytoMemory(self, event:wx.MouseEvent):
587
+ """ Transfert des données en mémoire --> remplissage des dictionnaires """
362
588
 
363
- haveDefaultParams = True
364
- #idem pour les param par défaut le cas échéant
365
- if os.path.isfile(self.filename + '.default') :
366
- with open(self.filename+'.default', 'r') as myfile:
367
- myparamsline = myfile.read().splitlines()
368
- myfile.close()
369
- self.ParseFile(myparamsline,self.myparams_default)
370
- else:
371
- haveDefaultParams = False
372
-
373
- #lecture du contenu
374
- if os.path.isfile(self.filename):
375
- with open(self.filename, 'r') as myfile:
376
- #split des lignes --> récupération des infos sans '\n' en fin de ligne
377
- # différent de .readlines() qui lui ne supprime pas les '\n'
378
- myparamsline = myfile.read().splitlines()
379
- myfile.close()
589
+ if self.prop.IsPageModified(0):
590
+ #on boucle sur tous les paramètres pour les ajouter au dictionnaire si non présents
591
+ for group in self.myparams_default.keys():
592
+ for param_name in self.myparams_default[group].keys():
593
+ self._Apply1ParamtoMemory(group, param_name)
594
+ self._update_IncGroup(withGUI=True)
595
+ self._update_IncParam(withGUI=True)
596
+
597
+ if not self._callback is None:
598
+ self._callback()
380
599
  else:
381
- logging.warning("ERROR : cannot find the following file :")
382
- logging.warning(self.filename)
383
- return
600
+ wx.MessageDialog(self,'Nothing to do!')
384
601
 
385
- #conversion et remplissage du dictionnaire
386
- self.ParseFile(myparamsline,self.myparams)
387
- if(not(haveDefaultParams)):
388
- self.ParseFile(myparamsline,self.myparams_default)
389
- self.createDefaultFile()
602
+ def _Apply1ParamtoMemory(self,
603
+ group:str,
604
+ param_name:str,
605
+ isIncrementable:bool=False,
606
+ genGroup:str="",
607
+ genParam:str=""):
608
+ """
609
+ Routine interne de MAJ d'un paramètre
390
610
 
391
- self.Update_IncGroup()
392
- self.Update_IncParam()
611
+ @param group : nom du groupe
612
+ @param param_name : nom du paramètre
613
+ @param isIncrementable : True si le paramètre est incrémentable
614
+ @param genGroup : generic name of an incrementable group
615
+ @param genParam : generic name of an incrementable param
393
616
 
394
- #Parsing du fichier pour trouver groupes et paramètres et remplissage d'un dictionnaire
395
- def ParseFile(self,myparamsline,todict):
617
+ """
396
618
 
397
- for param in myparamsline:
398
- if param.endswith(':'):
399
- #création d'un dict sur base du nom de groupe, sans le :
400
- curgroup = param.replace(':','')
401
- curgroup = curgroup.strip()
402
- #Par défaut un groupe n'est pas incrémentable
403
- isInc = False
404
- #On verifie si le groupe est incrémentable
405
- curgroup,iterInfo = self.Extract_IncrInfo(curgroup)
406
- # Groupe avec indice incrémentable
407
- if iterInfo is not None:
408
- isInc = True
409
- iterGroup = iterInfo[0]
410
- iterParam = iterInfo[1]
411
- iterMin = iterInfo[2]
412
- iterMax = iterInfo[3]
413
- if not curgroup in self.myIncGroup:
414
- self.myIncGroup[curgroup] = {}
415
- # self.myIncGroup[curgroup] = {}
416
- self.myIncGroup[curgroup]["Ref group"] = iterGroup
417
- self.myIncGroup[curgroup]["Ref param"] = iterParam
418
- self.myIncGroup[curgroup]["Min"] = iterMin
419
- self.myIncGroup[curgroup]["Max"] = iterMax
420
- self.myIncGroup[curgroup]["Dict"] = {}
421
- if not "Saved" in self.myIncGroup[curgroup]:
422
- self.myIncGroup[curgroup]["Saved"] = {}
423
- # Groupe classique
424
- else:
425
- todict[curgroup]={}
426
- elif param.startswith('%'):
427
- #c'est du commentaire --> rien à faire sauf si c'est du code json
428
- if param.startswith('%json'):
429
- #c'est du code json --> on le prend tel quel
430
- parsed_json = json.loads(param.replace('%json',''))
431
- curparam['added_json']=parsed_json
432
- elif param.strip() == '':
433
- if self.wx_exists:
434
- wx.LogWarning(_("WARNING : A void line is present where it should not be. Removing the blank line"))
435
- else:
436
- logging.warning(_('A void line is present where it should not be. Removing the blank line'))
437
- myparamsline.remove(param)
438
- else:
439
- #split sur base d'une tabulation
440
- paramloc=param.split('\t')
441
- #on enlève les espaces avant et après toutes les variables
442
- for i in range(len(paramloc)) :
443
- paramloc[i] = paramloc[i].strip()
444
- paramloc[0], iterInfo = self.Extract_IncrInfo(paramloc[0])
445
- #le parametre courant est pas incrémentable -> ajout au dictionnaire particulier des paramètres
446
- if iterInfo is not None:
447
- if not curgroup in self.myIncParam:
448
- self.myIncParam[curgroup] = {}
449
- if not paramloc[0] in self.myIncParam[curgroup]:
450
- self.myIncParam[curgroup][paramloc[0]] = {}
451
- # self.myIncParam[curgroup][paramloc[0]] = {}
452
- self.myIncParam[curgroup][paramloc[0]]["Group"] = curgroup
453
- if len(iterInfo)>1:
454
- self.myIncParam[curgroup][paramloc[0]]["Ref param"] = iterInfo[0]
455
- self.myIncParam[curgroup][paramloc[0]]["Min"] = iterInfo[1]
456
- self.myIncParam[curgroup][paramloc[0]]["Max"] = iterInfo[2]
457
- self.myIncParam[curgroup][paramloc[0]]["Dict"] = {}
458
-
459
- if not "Saved" in self.myIncParam[curgroup][paramloc[0]]:
460
- self.myIncParam[curgroup][paramloc[0]]["Saved"] = {}
461
- # self.myIncGroup[curgroup]["Dict"][paramloc[0]]=self.myIncParam[paramloc[0]]
462
- #pointage du param courant dans le dict de référence
463
- curparam=self.myIncParam[curgroup][paramloc[0]]["Dict"]
464
- else:
465
- #on verifie si le groupe est incrémentable pour pointer vers le bon dictionnaire
466
- if isInc:
467
- #création d'un dict sur base du nom de paramètre
468
- self.myIncGroup[curgroup]["Dict"][paramloc[0]]={}
469
- #pointage du param courant dans le dict de référence
470
- curparam=self.myIncGroup[curgroup]["Dict"][paramloc[0]]
471
- else:
472
- #création d'un dict sur base du nom de paramètre
473
- todict[curgroup][paramloc[0]]={}
474
- #pointage du param courant dans le dict
475
- curparam=todict[curgroup][paramloc[0]]
619
+ assert self.wx_exists, "wxPython is not running"
476
620
 
477
- #ajout de la valeur et du commentaire
478
- curparam['value']=paramloc[1]
479
- try:
480
- curparam['comment']=paramloc[2]
481
-
482
- #recherche du typage
483
- if paramloc[2].find('integer')>-1 and paramloc[2].find('double')>-1:
484
- curparam['type']='Integer_or_Float'
485
- elif paramloc[2].find('integer')>-1:
486
- curparam['type']='Integer'
487
- elif paramloc[2].find('logical')>-1:
488
- curparam['type']='Logical'
489
- elif paramloc[2].find('double')>-1 or param.find('dble')>-1 or param.find('real')>-1:
490
- curparam['type']='Float'
491
- elif paramloc[2].find('(file)')>-1:
492
- curparam['type']='File'
493
- elif paramloc[2].find('(directory)')>-1 or param.find('(dir)')>-1:
494
- curparam['type']='Directory'
495
- else:
496
- if not 'type' in curparam:
497
- curparam['type'] = None
498
-
499
- try:
500
- param_def=self.myparams_default[curgroup][paramloc[0]]
501
- curparam['type']=param_def['type']
502
- except:
503
- pass
504
- except:
505
- curparam['comment']=''
506
- if not 'type' in curparam:
507
- curparam['type'] = None
621
+ if isIncrementable:
622
+ if(genParam != ""):
623
+ if(genGroup != ""):
624
+ dict_param_def = self.myIncParam[genGroup][genParam]["Dict"][genParam]
625
+ else:
626
+ dict_param_def = self.myIncParam[group][genParam]["Dict"]
508
627
 
509
- #Remplissage de l'objet de gestion de propriétés sur base des dictionnaires
510
- def Populate(self):
511
- #gestion des paramètres actifs
512
- try:
513
- self.prop.Clear()
514
- except:
515
- pass
516
- page = self.prop.AddPage("Active Parameters")
517
- page = self.prop.AddPage("Default Parameters")
628
+ elif(genGroup != ""):
629
+ dict_param_def = self.myIncGroup[genGroup]["Dict"][param_name]
518
630
 
519
- page = self.prop.GetPage(self.prop.GetPageByName("Active Parameters"))
631
+ else:
632
+ dict_param_def = self.myparams_default[group][param_name]
520
633
 
521
- if len(self.myparams)>0:
522
- for group in self.myparams.keys():
523
- page.Append(pg.PropertyCategory(group))
634
+ if self.is_in_default(group, param_name):
524
635
 
525
- for param_name in self.myparams[group].keys():
636
+ # récupératrion de la valeur par défaut
637
+ val_default = self.prop.GetPropertyByName(PREFIX_DEFAULT + group + param_name).m_value
526
638
 
527
- param=self.myparams[group][param_name]
528
- locname=group + param_name
639
+ # on tente de rcupérer la valeur active mais il ets possible qu'elle n'existe pas si sa valeur est identique à la valeur par défaut
640
+ if self.is_in_active(group, param_name):
641
+ val_active = self.prop.GetPropertyByName(group + param_name).m_value
642
+ else:
643
+ val_active = val_default
529
644
 
530
- try:
531
- #on donne priorité aux données (type, commentaire) du groupe par défaut mais on utilise la valeur des paramètres actifs
532
- param_def=self.myparams_default[group][param_name]
533
- if 'added_json' in param_def.keys():
534
- list_keys = [ k for k in param_def['added_json']['Values'].keys()]
535
- list_values = [ k for k in param_def['added_json']['Values'].values()]
536
- page.Append(pg.EnumProperty(param_name,name=locname,labels=list_keys,values=list_values,value=int(float(param['value']))))
537
- else:
538
- if param_def['type']=='Integer_or_Float':
539
- page.Append(pg.IntProperty(label=param_name,name=locname,value=float(param['value'])))
540
- elif param_def['type']=='Integer':
541
- page.Append(pg.IntProperty(label=param_name,name=locname,value=int(param['value'])))
542
- elif param_def['type']=='Logical':
543
- if param['value']=='.true.' or param['value']=='.True.':
544
- mybool=True
545
- elif param['value']=='.false.' or param['value']=='.False.':
546
- mybool=False
547
- elif param['value']==False or param['value']=='false':
548
- mybool=False
549
- elif param['value']==True or param['value']=='true':
550
- mybool=True
551
- page.Append(pg.BoolProperty(label=param_name,name=locname,value=mybool))
552
- elif param_def['type']=='Float':
553
- page.Append(pg.FloatProperty(label=param_name,name=locname,value=float(param['value'])))
554
- elif param_def['type']=='File':
555
- page.Append(pg.FileProperty(label=param_name,name=locname,value=param['value']))
556
- elif param_def['type']=='Directory':
557
- newobj=pg.DirProperty(locname,locname,value=param['value'])
558
- newobj.SetLabel(param_name)
559
- page.Append(newobj)
560
- elif param_def['type']=='Color':
561
- page.Append(pg.ColourProperty(label=param_name,name=locname,value=param['value']))
562
- elif param_def['type']=='Fontname':
563
- page.Append(pg.FontProperty(label=param_name,name=locname,value=param['value']))
564
- else:
565
- page.Append(pg.StringProperty(label=param_name,name=locname,value=param['value']))
566
- except:
567
- #si le groupe par défaut n'a pas de groupe équivalent, on lit les élément du groupe actif
568
- if 'added_json' in param.keys():
569
- list_keys = [ k for k in param['added_json']['Values'].keys()]
570
- list_values = [ k for k in param['added_json']['Values'].values()]
571
- page.Append(pg.EnumProperty(label=param_name,name=locname,labels=list_keys,values=list_values,value=int(param['value'])))
572
- else:
573
- if param['type']=='Integer_or_Float':
574
- page.Append(pg.IntProperty(label=param_name,name=locname,value=float(param['value'])))
575
- elif param['type']=='Integer':
576
- page.Append(pg.IntProperty(label=param_name,name=locname,value=int(param['value'])))
577
- elif param['type']=='Logical':
578
- if param['value']=='.true.' or param['value']=='.True.':
579
- mybool=True
580
- elif param['value']=='.false.' or param['value']=='.False.':
581
- mybool=False
582
- elif param['value']==False or param['value']=='false':
583
- mybool=False
584
- elif param['value']==True or param['value']=='true':
585
- mybool=True
586
- page.Append(pg.BoolProperty(label=param_name,name=locname,value=mybool))
587
- elif param['type']=='Float':
588
- page.Append(pg.FloatProperty(label=param_name,name=locname,value=float(param['value'])))
589
- elif param['type']=='File':
590
- page.Append(pg.FileProperty(label=param_name,name=locname,value=param['value']))
591
- elif param['type']=='Directory':
592
- newobj=pg.DirProperty(locname,locname,value=param['value'])
593
- newobj.SetLabel(param_name)
594
- page.Append(newobj)
595
- elif param['type']=='Color':
596
- page.Append(pg.ColourProperty(label=param_name,name=locname,value=param['value']))
597
- elif param['type']=='Fontname':
598
- page.Append(pg.FontProperty(label=param_name,name=locname,value=param['value']))
599
- else:
600
- page.Append(pg.StringProperty(label=param_name,name=locname,value=param['value']))
601
-
602
- try:
603
- self.prop.SetPropertyHelpString(locname,param_def['added_json']['Full_Comment'])
604
- except:
605
- try:
606
- self.prop.SetPropertyHelpString(locname,param_def['comment'])
607
- except:
608
- self.prop.SetPropertyHelpString(locname,param['comment'])
645
+ val_active = self.value_as_type(val_active, dict_param_def[key_Param.TYPE])
646
+ val_default = self.value_as_type(val_default, dict_param_def[key_Param.TYPE])
609
647
 
610
- #gestion des paramètres par défaut
611
- page = self.prop.GetPage(self.prop.GetPageByName("Default Parameters"))
648
+ if val_active != val_default:
649
+ self[(group, param_name)] = val_active
650
+ else:
651
+ logging.debug(_('Parameter {} not modified'.format(param_name)))
612
652
 
613
- if len(self.myparams_default)>0:
614
- for group in self.myparams_default.keys():
615
- page.Append(pg.PropertyCategory(group))
653
+ else:
654
+ # La valeur par défaut n'existe pas --> on prend la valeur active car c'est certainement une valeur incrémentable ou d'un groupe incrémentable
655
+ # Si la valeur n'est pas présente, on ne fait rien
656
+ if self.is_in_active(group, param_name):
657
+ val_active = self.prop.GetPropertyByName(group + param_name).m_value
658
+ val_active = self.value_as_type(val_active, dict_param_def[key_Param.TYPE])
616
659
 
617
- for param_name in self.myparams_default[group].keys():
660
+ self[(group, param_name)] = val_active
661
+ else:
662
+ logging.debug(_('Parameter {} not found in default parameters'.format(param_name)))
618
663
 
619
- param=self.myparams_default[group][param_name]
620
- locname='def' + group + param_name
664
+ def position(self,position):
665
+ """ Position the frame """
666
+ self.SetPosition(wx.Point(position[0],position[1]+50))
621
667
 
622
- addJson=False
623
- if 'added_json' in param.keys():
624
- addJson=True
668
+ def Populate(self):
669
+ """
670
+ Filling the property management object based on dictionaries
625
671
 
626
- if addJson:
627
- list_keys = [ k for k in param['added_json']['Values'].keys()]
628
- list_values = [ k for k in param['added_json']['Values'].values()]
629
- page.Append(pg.EnumProperty(param_name,name=locname,labels=list_keys,values=list_values,value=int(float(param['value']))))
630
- else:
631
- if param['type']=='Integer_or_Float':
632
- page.Append(pg.IntProperty(label=param_name,name=locname,value=float(param['value'])))
633
- elif param['type']=='Integer':
634
- page.Append(pg.IntProperty(label=param_name,name=locname,value=int(param['value'])))
635
- elif param['type']=='Logical':
636
- if param['value']=='.true.' or param['value']=='.True.':
637
- mybool=True
638
- elif param['value']=='.false.' or param['value']=='.False.':
639
- mybool=False
640
- elif param['value']==False or param['value']=='false':
641
- mybool=False
642
- elif param['value']==True or param['value']=='true':
643
- mybool=True
644
- page.Append(pg.BoolProperty(label=param_name,name=locname,value=mybool))
645
- elif param['type']=='Float':
646
- page.Append(pg.FloatProperty(label=param_name,name=locname,value=float(param['value'])))
647
- elif param['type']=='File':
648
- page.Append(pg.FileProperty(label=param_name,name=locname,value=param['value']))
649
- elif param['type']=='Directory':
650
- newobj=pg.DirProperty(locname,locname,value=param['value'])
651
- newobj.SetLabel(param_name)
652
- page.Append(newobj)
653
- elif param['type']=='Color':
654
- page.Append(pg.ColourProperty(label=param_name,name=locname,value=param['value']))
655
- elif param_def['type']=='Fontname':
656
- page.Append(pg.FontProperty(label=param_name,name=locname,value=param['value']))
657
- else:
658
- page.Append(pg.StringProperty(label=param_name,name=locname,value=param['value']))
672
+ Use default AND active parameters
659
673
 
660
- try:
661
- self.prop.SetPropertyHelpString(locname,param['added_json']['Full_Comment'])
662
- except:
663
- self.prop.SetPropertyHelpString(locname,param['comment'])
674
+ Useful only if wxPython is running
675
+ """
664
676
 
665
- # Display a header above the grid
666
- self.prop.ShowHeader()
667
- self.prop.Refresh()
677
+ if self.prop is None:
678
+ logging.error("ERROR : wxPython is not running - Impossible to populate the property grid")
679
+ return
668
680
 
669
- def PopulateOnePage(self):
670
- #gestion des paramètres actifs
671
681
  self.prop.Clear()
672
- page = self.prop.AddPage("Current")
673
-
674
- if len(self.myparams)>0:
675
- for group in self.myparams.keys():
676
- page.Append(pg.PropertyCategory(group))
677
682
 
678
- for param_name in self.myparams[group].keys():
679
-
680
- param=self.myparams[group][param_name]
681
- locname=group + param_name
683
+ page_active:pg.PropertyGridPage
684
+ page_default:pg.PropertyGridPage
685
+ page_active = self.prop.AddPage(_("Active Parameters"))
686
+ page_default = self.prop.AddPage(_("Default Parameters"))
682
687
 
683
- if 'added_json' in param.keys():
684
- list_keys = [ k for k in param['added_json']['Values'].keys()]
685
- list_values = [ k for k in param['added_json']['Values'].values()]
686
- page.Append(pg.EnumProperty(param_name,name=locname,labels=list_keys,values=list_values,value=int(param['value'])))
687
- else:
688
- if param['type']=='Integer_or_Float':
689
- page.Append(pg.IntProperty(label=param_name,name=locname,value=float(param['value'])))
690
- elif param['type']=='Integer':
691
- page.Append(pg.IntProperty(label=param_name,name=locname,value=int(param['value'])))
692
- elif param['type']=='Logical':
693
- if param['value']=='.true.' or param['value']=='.True.':
694
- mybool=True
695
- elif param['value']=='.false.' or param['value']=='.False.':
696
- mybool=False
697
- page.Append(pg.BoolProperty(label=param_name,name=locname,value=mybool))
698
- elif param['type']=='Float':
699
- page.Append(pg.FloatProperty(label=param_name,name=locname,value=float(param['value'])))
700
- elif param['type']=='File':
701
- page.Append(pg.FileProperty(label=param_name,name=locname,value=param['value']))
702
- elif param['type']=='Directory':
703
- newobj=pg.DirProperty(locname,locname,value=param['value'])
704
- newobj.SetLabel(param_name)
705
- page.Append(newobj)
706
- elif param['type']=='Color':
707
- page.Append(pg.ColourProperty(label=param_name,name=locname,value=param['value']))
708
- elif param['type']=='Fontname':
709
- page.Append(pg.FontProperty(label=param_name,name=locname,value=param['value']))
710
- else:
711
- page.Append(pg.StringProperty(label=param_name,name=locname,value=param['value']))
688
+ #gestion des paramètres actifs
689
+ for group, params in self.myparams.items():
690
+ page_active.Append(pg.PropertyCategory(group))
691
+ for param_name, param in params.items():
692
+ param:dict
693
+ if self.is_in_default(group, param_name):
694
+ param_def = self.myparams_default[group][param_name]
695
+
696
+ if self.show_in_active_if_default:
697
+ self._add_elem_to_page(page_active, group, param, param_def = param_def)
698
+ elif param[key_Param.VALUE] != param_def[key_Param.VALUE]:
699
+ self._add_elem_to_page(page_active, group, param, param_def = param_def)
700
+ else:
701
+ logging.debug(_('Parameter {} not found in default parameters'.format(param_name)))
702
+ param_def = None
703
+ self._add_elem_to_page(page_active, group, param, param_def = param_def)
712
704
 
713
- if 'added_json' in param.keys():
714
- self.prop.SetPropertyHelpString(locname,param['added_json']['Full_Comment'])
715
- else:
716
- self.prop.SetPropertyHelpString(locname,param['comment'])
705
+ #gestion des paramètres par défaut
706
+ for group, params in self.myparams_default.items():
707
+ page_default.Append(pg.PropertyCategory(group))
708
+ for param_name, param in params.items():
709
+ param:dict
710
+ self._add_elem_to_page(page_default, group, param, prefix = PREFIX_DEFAULT)
717
711
 
718
712
  # Display a header above the grid
719
713
  self.prop.ShowHeader()
720
714
  self.prop.Refresh()
721
715
 
722
- #sauvegarde dans le fichier texte
723
- def SavetoFile(self,event):
724
- # self.ApplytoMemory(0)
716
+ def _add_elem_to_page(self, page:pg.PropertyGridPage, group:str, param:dict, param_def:dict = None, prefix:str=''):
717
+ """ Add an element to a page """
725
718
 
726
- with open(self.filename, 'w') as myfile:
719
+ param_name = param[key_Param.NAME]
720
+ locname = prefix + group + param_name
727
721
 
728
- for group in self.myparams.keys():
729
- myfile.write(' ' + group +':\n')
730
- for param_name in self.myparams[group].keys():
731
- myfile.write(param_name +'\t' + str(self.myparams[group][param_name]['value'])+'\n')
722
+ if param_def is not None:
723
+ # priority to default parameters
724
+ if key_Param.ADDED_JSON in param_def.keys():
725
+ param[key_Param.ADDED_JSON] = param_def[key_Param.ADDED_JSON]
726
+ param[key_Param.COMMENT] = param_def[key_Param.COMMENT]
727
+ param[key_Param.TYPE] = param_def[key_Param.TYPE]
732
728
 
733
- myfile.close()
729
+ if key_Param.ADDED_JSON in param.keys() and param[key_Param.ADDED_JSON] is not None:
730
+ # Ajout des choix via chaîne JSON
731
+ list_keys = [ k for k in param[key_Param.ADDED_JSON]['Values'].keys()]
732
+ list_values = [ k for k in param[key_Param.ADDED_JSON]['Values'].values()]
734
733
 
735
- #relecture du fichier sur base du nom déjà connu
736
- def Reload(self,event):
737
- self.myparams.clear()
738
- self.myparams_default.clear()
739
- self.ReadFile(self.filename)
740
- self.Populate()
734
+ page.Append(pg.EnumProperty(param_name, name=locname, labels=list_keys, values=list_values, value=int(param[key_Param.VALUE])))
741
735
 
742
- #Transfert des données en mémoire --> remplissage des dictionnaires
743
- def ApplytoMemory(self,event):
736
+ self.prop.SetPropertyHelpString(locname , param[key_Param.ADDED_JSON]['Full_Comment'])
737
+ else:
744
738
 
745
- #on vide le dictionnaire des paramètres actifs
746
- self.myparams={}
739
+ locvalue = self[(group, param_name)]
747
740
 
748
- if self.prop.IsPageModified:
749
- #on boucle sur tous les paramètres du défault
750
- for group in self.myparams_default.keys():
751
- groupexists = False
752
- for param_name in self.myparams_default[group].keys():
753
- groupexists = self.Apply1ParamtoMemory(group, param_name, groupexists=groupexists)
741
+ if isinstance(locvalue, float):
742
+ page.Append(pg.FloatProperty(label = param_name, name = locname, value = locvalue))
754
743
 
744
+ elif isinstance(locvalue, int):
745
+ # bool is also an int
746
+ if isinstance(locvalue, bool):
747
+ page.Append(pg.BoolProperty(label=param_name, name = locname, value = locvalue))
748
+ else:
749
+ page.Append(pg.IntProperty(label = param_name, name = locname, value = locvalue))
755
750
 
756
- if not self._callback is None:
757
- self._callback()
758
- self.Update_IncGroup(withGUI=True)
759
- self.Update_IncParam(withGUI=True)
760
- else:
761
- wx.MessageDialog(self,'Nothing to do!')
751
+ elif param[key_Param.TYPE]==Type_Param.File:
752
+ page.Append(pg.FileProperty(label=param_name, name = locname, value = param[key_Param.VALUE]))
762
753
 
763
- def position(self,position):
764
- self.SetPosition(wx.Point(position[0],position[1]+50))
754
+ elif param[key_Param.TYPE]==Type_Param.Directory:
755
+ page.Append(pg.DirProperty(label = param_name, name = locname, value = locvalue))
756
+ # newobj.SetLabel(param_name)
757
+ # page.Append(newobj)
765
758
 
766
- def OnClose(self, event):
767
- if not self._callbackdestroy is None:
768
- self._callbackdestroy()
759
+ elif param[key_Param.TYPE]==Type_Param.Color:
760
+ page.Append(pg.ColourProperty(label = param_name, name = locname, value = locvalue))
769
761
 
770
- if self.DestroyAtClosing:
771
- self.Destroy()
772
- else:
773
- self.Hide()
774
- pass
762
+ elif param[key_Param.TYPE]==Type_Param.Fontname:
763
+ page.Append(pg.FontProperty(label = param_name, name = locname, value = locvalue))
775
764
 
776
- def createDefaultFile(self):
765
+ else:
766
+ page.Append(pg.StringProperty(label = param_name, name = locname, value = locvalue))
777
767
 
778
- with open(self.filename+'.default', 'w') as myfile:
768
+ self.prop.SetPropertyHelpString(locname, param[key_Param.COMMENT])
779
769
 
780
- for group in self.myparams.keys():
781
- myfile.write(' ' + group +':\n')
782
- for param_name in self.myparams[group].keys():
783
- myfile.write(param_name +'\t' + str(self.myparams[group][param_name]['value'])+'\n')
770
+ def PopulateOnePage(self):
771
+ """
772
+ Filling the property management object based on dictionaries
784
773
 
785
- myfile.close()
774
+ Use ONLY active parameters
786
775
 
776
+ Useful only if wxPython is running -- e.g. class "PyDraw"
777
+ """
787
778
 
788
- # Returns the value of the parameter if found, otherwise None obj
789
- def get_param(self, group, name):
779
+ if self.prop is None:
780
+ logging.error("ERROR : wxPython is not running - Impossible to populate the property grid")
781
+ return
790
782
 
791
- try:
792
- element = self.myparams[group][name]["value"]
793
- except:
794
- try:
795
- element = self.myparams_default[group][name]["value"]
796
- except:
797
- element = None
798
- return element
783
+ #gestion des paramètres actifs
784
+ self.prop.Clear()
785
+ page:pg.PropertyGridPage
786
+ page = self.prop.AddPage("Current")
799
787
 
800
- # String conversion according to its type
801
- try:
802
- curType = self.myparams[group][name]["type"]
803
- if curType is None:
804
- curType = self.myparams_default[group][name]["type"]
805
- if curType is None:
806
- curType = 'String'
807
- except:
808
- try:
809
- curType = self.myparams_default[group][name]["type"]
810
- except:
811
- curType = 'String'
788
+ for group, params in self.myparams.items():
789
+ page.Append(pg.PropertyCategory(group))
790
+ for param_name, param in params.items():
791
+ param:dict
792
+ self._add_elem_to_page(page, group, param)
812
793
 
794
+ # Display a header above the grid
795
+ self.prop.ShowHeader()
796
+ self.prop.Refresh()
813
797
 
814
- if curType == 'Integer':
815
- element = int(element)
816
- elif curType == 'Logical':
817
- element = bool(element)
818
- elif curType == 'Float':
819
- element = float(element)
820
- elif curType == 'Integer_or_Float':
821
- element = float(element)
822
- elif curType == 'Color':
823
- if isinstance(element,str):
824
- element = element.replace('(','')
825
- element = element.replace(')','')
826
- element = element.split(',')
827
798
 
828
- return element
799
+ # File management
800
+ # ---------------
829
801
 
802
+ def check_default_file(self, filename:str):
803
+ """ Check if a default file exists """
830
804
 
831
- # Return the group dictionnary if found, otherwise None obj
832
- def get_group(self, group) -> dict:
805
+ if os.path.isfile(filename + '.default'):
806
+ return True
807
+ else:
808
+ return False
833
809
 
834
- try:
835
- element = self.myparams[group]
836
- except:
837
- element = None
810
+ def ReadFile(self,*args):
811
+ """ Lecture d'un fichier .param et remplissage des dictionnaires myparams et myparams_default """
838
812
 
839
- return element
813
+ if len(args)>0:
814
+ #s'il y a un argument on le prend tel quel
815
+ self.filename = str(args[0])
816
+ else:
817
+ if self.wx_exists:
818
+ #ouverture d'une boîte de dialogue
819
+ file=wx.FileDialog(self,"Choose .param file", wildcard="param (*.param)|*.param|all (*.*)|*.*")
820
+ if file.ShowModal() == wx.ID_CANCEL:
821
+ return
822
+ else:
823
+ #récuparétaion du nom de fichier avec chemin d'accès
824
+ self.filename =file.GetPath()
825
+ else:
826
+ logging.warning("ERROR : no filename given and wxPython is not running")
827
+ return
840
828
 
829
+ if not os.path.isfile(self.filename):
830
+ logging.warning("ERROR : cannot find the following file : {}".format(self.filename))
831
+ return
841
832
 
842
- def change_param(self, group, name, value):
843
- #essai pour voir si le groupe existe ou non dans les params actifs
844
- try:
845
- locgroup = self.myparams[group]
846
- except:
847
- locgroup=None
833
+ myparamsline_default = None
834
+ if self.check_default_file(self.filename):
835
+ with open(self.filename+'.default', 'r') as myfile:
836
+ myparamsline_default = myfile.read()
837
+
838
+ # lecture du contenu
839
+ with open(self.filename, 'r') as myfile:
840
+ myparamsline = myfile.read()
841
+
842
+ self.fill_from_strings(myparamsline, myparamsline_default)
843
+
844
+ if not self.check_default_file(self.filename):
845
+ self._CreateDefaultFile()
846
+
847
+ def fill_from_strings(self, chain:str, chaindefault:str = None):
848
+ """ Fill the dictionaries from a string """
849
+
850
+ myparamsline = chain.splitlines()
851
+ self.ParseFile(myparamsline,self.myparams)
852
+
853
+ if chaindefault is not None:
854
+ myparamsline = chaindefault.splitlines()
855
+
856
+ self.ParseFile(myparamsline,self.myparams_default)
857
+
858
+ # mise à jour des groupes incrémentables et des paramètres incrémentables
859
+ self._update_IncGroup()
860
+ self._update_IncParam()
861
+
862
+ def ParseFile(self, myparamsline:list[str], todict:dict):
863
+ """
864
+ Parsing the file to find groups and parameters and filling a dictionary
865
+
866
+ Each parameter is stored in a dictionary associated to the upper group.
867
+
868
+ 'add_group' is used to add a group in the dictionary 'todict'.
869
+ 'groupname' will be there sanitized (strip, decoded if iteratable...) to be used in '_add_param_from_str'.
870
+
871
+ myparamsline format:
872
+ ['groupname1:', 'param1', 'param2', 'groupname2:', 'param1', ...]
873
+
874
+ """
875
+
876
+ if isinstance(myparamsline, str):
877
+ logging.warning("ERROR : myparamsline must be a list of strings -- forcing conversion")
878
+ myparamsline = myparamsline.splitlines()
879
+
880
+ for param in myparamsline:
881
+ if param.endswith(':'):
882
+ # GROUPE
883
+ # ------
884
+
885
+ #création d'un dict sur base du nom de groupe, sans le :
886
+ groupname = param.replace(':','')
887
+ groupname, groupdict = self.add_group(groupname, todict)
888
+
889
+ elif param.startswith('%'):
890
+ # COMMENTAIRE
891
+ # -----------
892
+ #c'est du commentaire --> rien à faire sauf si c'est du code json
893
+ if param.startswith('%json'):
894
+ #c'est du code json --> on le prend tel quel
895
+ parsed_json = json.loads(param.replace('%json',''))
896
+ curparam[key_Param.ADDED_JSON]=parsed_json
897
+
898
+ elif param.strip() == '':
899
+ # VOID LINE
900
+ # ---------
901
+ logging.warning(_('A void line is present where it should not be. Removing the blank line'))
902
+ myparamsline.remove(param)
903
+
904
+ else:
905
+ # PARAMETRE
906
+ # ---------
907
+ curparam = self._add_param_from_str(param, groupname, groupdict)
908
+
909
+ def _CreateDefaultFile(self):
910
+ """ Create a default file """
911
+
912
+ with open(self.filename+'.default', 'w') as myfile:
913
+
914
+ for group in self.myparams.keys():
915
+ myfile.write(' ' + group +':\n')
916
+ for param_name in self.myparams[group].keys():
917
+ myfile.write(param_name +'\t' + str(self.myparams[group][param_name][key_Param.VALUE])+'\n')
918
+
919
+ myfile.close()
920
+
921
+ def _Extract_IncrInfo(self, nameStr:str) -> tuple[str, list[str, str, int, int]]:
922
+ """
923
+ Extract the information of an incrementable group or param
924
+
925
+ The name of an incrementable group or param is of the form: $n(group, param, min, max)$
926
+ """
927
+ iterInfo = []
928
+ newName = ""
929
+
930
+ positions = [i for i, char in enumerate(nameStr) if char == "$"]
931
+
932
+ assert np.mod(len(positions),2) == 0, "ERROR : the number of '$' must be even"
933
+
934
+ if len(positions)>0:
935
+ # indice incrémentable détecté
936
+
937
+ # search for the first '$'
938
+ posSep1 = positions[0] #nameStr.find("$")
939
+ # search for the last '$'
940
+ posSep2 = positions[-1] #nameStr[posSep1+1:].find("$")
941
+
942
+ # select the string between the two '$'
943
+ iterCode = nameStr[posSep1:posSep1+posSep2+1]
944
+
945
+ positions_left = [i for i, char in enumerate(iterCode) if char == "("]
946
+ positions_right = [i for i, char in enumerate(iterCode) if char == ")"]
947
+
948
+ if len(positions_left)==0 and len(positions_right)==0:
949
+ # no '(' and no ')' --> no incrementable information
950
+ return nameStr, None
951
+
952
+ assert len(positions_left)>0, "ERROR : no '(' found in the name of an incrementable group or param"
953
+ assert len(positions_right)>0, "ERROR : no ')' found in the name of an incrementable group or param"
954
+
955
+ # search for the first '('
956
+ posSep1 = positions_left[0] #iterCode.find("(")
957
+ # search for the second ')'
958
+ posSep2 = positions_right[-1] #iterCode[posSep1:].find(")")
959
+
960
+ # select the string between the two '('
961
+ iterCode = iterCode[posSep1:posSep2+1]
962
+
963
+ # remove the incrementable code from the name --> conserve $n$
964
+ newName = nameStr.replace(iterCode,'')
965
+ # remove the spaces before and after the name
966
+ newName = newName.strip()
967
+
968
+ # decode the incrementable information
969
+ iterInfo = iterCode[1:-1].split(',')
970
+ # remove the spaces before and after the information
971
+ iterInfo = [x.strip() for x in iterInfo]
972
+
973
+ if len(iterInfo)==3:
974
+ # no group name provided --> use the current group
975
+ iterInfo = [iterInfo[0], int(iterInfo[1]), int(iterInfo[2])]
976
+ elif len(iterInfo)==4:
977
+ # group name provided --> use the provided group
978
+ iterInfo = [iterInfo[0], iterInfo[1], int(iterInfo[2]), int(iterInfo[3])]
979
+ else:
980
+ logging.error(_("The incrementable information must be of the form: $n(group, param, min, max)$ or $n(param, min, max)$"))
981
+ else:
982
+ newName = nameStr
983
+ iterInfo = None
984
+
985
+ return newName, iterInfo
986
+
987
+
988
+ # Clear/Rest
989
+ # ----------
990
+
991
+ def Clear(self):
992
+ """ Clear all the parameters """
993
+
994
+ self.myparams.clear()
995
+ self.myparams_default.clear()
996
+ self.myIncGroup.clear()
997
+ self.myIncParam.clear()
998
+ if self.prop is not None:
999
+ self.prop.Clear()
1000
+
1001
+ # Object access
1002
+ # -------------
1003
+
1004
+ def add_group(self, groupname:str, todict:dict = None) -> tuple[str,dict]:
1005
+ """
1006
+ Add a group in the dictionary 'todict' if provided or in the IncGroup dictionary
1007
+
1008
+ return sanitized groupname and dictionnary attached to the group
1009
+ """
1010
+
1011
+ groupname = groupname.strip()
1012
+
1013
+ #On verifie si le groupe est incrémentable
1014
+ groupname, iterInfo = self._Extract_IncrInfo(groupname)
1015
+
1016
+ if iterInfo is None:
1017
+ if todict is None:
1018
+ logging.error(_("You must provide a dictionary to store the group -- Retry"))
1019
+ return None
1020
+
1021
+ # Groupe classique
1022
+ todict[groupname]={}
1023
+ return groupname, todict[groupname]
1024
+ else:
1025
+ # Le groupe est incrémentable
1026
+ iterGroup = iterInfo[0] # nom du groupe contenant le paramètre de nombre de groupes
1027
+ iterParam = iterInfo[1] # nom du paramètre contenant le nombre de groupes
1028
+ iterMin = iterInfo[2] # valeur minimale
1029
+ iterMax = iterInfo[3] # valeur maximale
1030
+
1031
+ return groupname, self.add_IncGroup(groupname, iterMin, iterMax, iterGroup, iterParam)
1032
+
1033
+ def _add_param_in_dict(self,
1034
+ group:dict,
1035
+ name:str,
1036
+ value:Union[float, int, str] = '', # la valeur est de toute façon stockée en 'str'
1037
+ type:Type_Param=None,
1038
+ comment:str = '',
1039
+ jsonstr:str = None) -> dict:
1040
+
1041
+ if not name in group.keys():
1042
+ group[name]={}
1043
+
1044
+ curpar=group[name]
1045
+
1046
+ curpar[key_Param.NAME]=name
1047
+ curpar[key_Param.TYPE]=type
1048
+ curpar[key_Param.VALUE]=value
1049
+ curpar[key_Param.COMMENT]=comment
1050
+
1051
+ if jsonstr is not None:
1052
+ if isinstance(jsonstr, str):
1053
+ parsed_json = json.loads(jsonstr.replace('%json',''))
1054
+ elif isinstance(jsonstr, dict):
1055
+ parsed_json = jsonstr
1056
+
1057
+ curpar[key_Param.ADDED_JSON]=parsed_json
1058
+ else:
1059
+ curpar[key_Param.ADDED_JSON]=None
1060
+ return curpar
1061
+
1062
+ def _new_IncParam_dict(self, groupname, refgroupname, refparamname, min_value, max_value) -> dict:
1063
+ """ Create a new dictionary for an incrementable parameter """
1064
+
1065
+ newdict = {}
1066
+ newdict["Group"] = groupname
1067
+ newdict["Ref group"] = refgroupname
1068
+ newdict["Ref param"] = refparamname
1069
+ newdict["Min"] = min_value
1070
+ newdict["Max"] = max_value
1071
+ newdict["Dict"] = {}
1072
+
1073
+ return newdict
1074
+
1075
+ def _add_param_from_str(self, param:str, groupname:str, groupdict:dict, seperator:str = '\t'):
1076
+ """ Add a parameter from a complex string """
1077
+
1078
+ #split sur base du sépérateur
1079
+ paramloc=param.split(seperator)
1080
+
1081
+ #on enlève les espaces avant et après toutes les variables
1082
+ paramloc = [x.strip() for x in paramloc]
1083
+
1084
+ paramloc[0], iterInfo = self._Extract_IncrInfo(paramloc[0])
1085
+
1086
+ if iterInfo is not None:
1087
+ #le parametre courant est incrémentable -> ajout au dictionnaire particulier des paramètres
1088
+ if not groupname in self.myIncParam:
1089
+ self.myIncParam[groupname] = {}
1090
+
1091
+ if not paramloc[0] in self.myIncParam[groupname]:
1092
+ curdict = self.myIncParam[groupname][paramloc[0]] = self._new_IncParam_dict(groupname, groupname if len(iterInfo)==3 else iterInfo[0], iterInfo[-3], iterInfo[-2], iterInfo[-1])
1093
+
1094
+ if not "Saved" in curdict:
1095
+ curdict["Saved"] = {}
1096
+
1097
+ #pointage du param courant dans le dict de référence
1098
+ curparam=curdict["Dict"]
1099
+ else:
1100
+ #création d'un dict sur base du nom de paramètre
1101
+ curparam=groupdict[paramloc[0]]={}
1102
+
1103
+ curparam[key_Param.NAME]=paramloc[0]
1104
+
1105
+ if len(paramloc)>1:
1106
+ #ajout de la valeur
1107
+ curparam[key_Param.VALUE]=paramloc[1]
848
1108
 
849
- #teste si param existe
850
- try:
851
- param = self.myparams[group][name]
852
- except:
853
- param=None
854
1109
  try:
855
- defparam = self.myparams_default[group][name]
1110
+ # tentative d'ajout du commentaire --> pas obligatoirement présent
1111
+ curparam[key_Param.COMMENT]=paramloc[2]
1112
+
1113
+ # recherche du type dans le commentaire
1114
+ curparam[key_Param.TYPE]=search_type(paramloc[2])
1115
+ if curparam[key_Param.TYPE] == Type_Param.String:
1116
+ # recherche du type dans la châine complète
1117
+ type_in_fulchain = search_type(param)
1118
+
1119
+ if type_in_fulchain != Type_Param.String:
1120
+ curparam[key_Param.TYPE] = type_in_fulchain
1121
+ try:
1122
+ # tentaive de recherche du type dans les valeurs par défaut
1123
+ param_def=self.myparams_default[groupname][paramloc[0]]
1124
+ curparam[key_Param.TYPE]=param_def[key_Param.TYPE]
1125
+ except:
1126
+ pass
856
1127
  except:
857
- if self.wx_exists:
858
- wx.MessageBox(_('This parameter is neither in the current file nor in the default file!'), _('Error'), wx.OK|wx.ICON_ERROR)
1128
+ curparam[key_Param.COMMENT]=''
1129
+ try:
1130
+ # tentative de recherche du type dans les valeurs par défaut
1131
+ param_def=self.myparams_default[groupname][paramloc[0]]
1132
+ curparam[key_Param.TYPE]=param_def[key_Param.TYPE]
1133
+ except:
1134
+ if not key_Param.TYPE in curparam:
1135
+ curparam[key_Param.TYPE] = None
1136
+ else:
1137
+ curparam[key_Param.VALUE]=''
1138
+ curparam[key_Param.COMMENT]=''
1139
+ curparam[key_Param.TYPE]=None
1140
+
1141
+ return curparam
1142
+
1143
+ def addparam(self,
1144
+ groupname:str = '',
1145
+ name:str = '',
1146
+ value:Union[float, int, str] = '', # la valeur est de toute façon stockée en 'str'
1147
+ type:Type_Param=None,
1148
+ comment:str = '',
1149
+ jsonstr:str = None,
1150
+ whichdict:Literal['All', 'Default', 'Active', 'IncGroup', '']=''):
1151
+ """
1152
+ Add or update a parameter
1153
+
1154
+ @param groupname : groupe in which the new param will be strored - If it does not exist, it will be created
1155
+ @param name : param's name - If it does not exist, it will be created
1156
+ @param value : param'a value
1157
+ @param type : type -> will influence the GUI
1158
+ @param comment : param's comment -- helpful to understand the parameter
1159
+ @param jsonstr : string containing JSON data -- used in GUI
1160
+ param whichdict : where to store the param -- Default, Active or All, or IncGroup if the param is part of an incrementable group
1161
+
1162
+ jsonstr can be a dict i.e. '{"Values":{choice1:1, choice2:2, choice3:3}, "Full_Comment":'Yeah baby !'}'
1163
+
1164
+ Return 0 if OK, -1 if the group is incrementable and not created, -2 if the group does not exist
1165
+ """
1166
+
1167
+ if isinstance(type, str):
1168
+ if type == 'Integer':
1169
+ type = Type_Param.Integer
1170
+ elif type == 'Float':
1171
+ type = Type_Param.Float
1172
+ elif type == 'Integer_or_Float':
1173
+ type = Type_Param.Integer_or_Float
1174
+ elif type == 'Logical':
1175
+ type = Type_Param.Logical
1176
+ elif type == 'File':
1177
+ type = Type_Param.File
1178
+ elif type == 'Directory':
1179
+ type = Type_Param.Directory
1180
+ elif type == 'Color':
1181
+ type = Type_Param.Color
1182
+ elif type == 'Fontname':
1183
+ type = Type_Param.Fontname
1184
+ elif type == 'String':
1185
+ type = Type_Param.String
1186
+ else:
1187
+ type = None
1188
+
1189
+ name_wo, iterInfo = self._Extract_IncrInfo(name)
1190
+ if iterInfo is not None:
1191
+ return self.add_IncParam(groupname, name, value, comment, type, added_json=jsonstr)
1192
+
1193
+ if '$n$' in groupname:
1194
+ if whichdict != 'IncGroup':
1195
+ logging.warning(_("WARNING : group is incrementable. -- You must use 'IncGroup' for whichdict"))
1196
+ whichdict = 'IncGroup'
1197
+ elif '$n(' in groupname:
1198
+ if whichdict != 'IncGroup':
1199
+ logging.warning(_("WARNING : group is incrementable. -- You must use 'IncGroup' for whichdict"))
1200
+ whichdict = 'IncGroup'
1201
+
1202
+ groupname, iterInfo = self._Extract_IncrInfo(groupname)
1203
+
1204
+ if iterInfo is None:
1205
+ logging.error(_("ERROR : infos not found in {} -- Retry".format(groupname)))
1206
+ return -1
1207
+
1208
+ # Le groupe est incrémentable
1209
+ iterGroup = iterInfo[0] # nom du groupe contenant le paramètre de nombre de groupes
1210
+ iterParam = iterInfo[1] # nom du paramètre contenant le nombre de groupes
1211
+ iterMin = iterInfo[2] # valeur minimale
1212
+ iterMax = iterInfo[3] # valeur maximale
1213
+
1214
+ if groupname not in self.myIncGroup.keys():
1215
+ self.add_IncGroup(groupname, iterMin, iterMax, iterGroup, iterParam)
1216
+
1217
+ if whichdict=='IncGroup':
1218
+ if not groupname in self.myIncGroup.keys():
1219
+ logging.error(_("ERROR : group {} does not exist. -- You must first create it".format(groupname)))
1220
+ logging.error(_(" or pass infos in the group name : $n(group, param, min, max)$"))
1221
+ return -2
1222
+
1223
+ self._add_param_in_dict(self.myIncGroup[groupname]["Dict"], name, value, type, comment, jsonstr)
1224
+ return 0
1225
+
1226
+ else:
1227
+ if whichdict=='All':
1228
+ locparams=[self.myparams, self.myparams_default]
1229
+ elif whichdict=='Default':
1230
+ locparams=[self.myparams_default]
1231
+ elif whichdict=='Active' or whichdict=='':
1232
+ locparams=[self.myparams]
1233
+
1234
+ for curdict in locparams:
1235
+ if not groupname in curdict.keys():
1236
+ curdict[groupname]={}
1237
+
1238
+ self._add_param_in_dict(curdict[groupname], name, value, type, comment, jsonstr)
1239
+
1240
+ return 0
1241
+
1242
+ def add_param(self,
1243
+ groupname:str = '',
1244
+ name:str = '',
1245
+ value:Union[float, int, str] = '', # la valeur est de toute façon stockée en 'str'
1246
+ type:Type_Param=None,
1247
+ comment:str = '',
1248
+ jsonstr:str = None,
1249
+ whichdict:Literal['All', 'Default', 'Active', 'IncGroup', '']=''):
1250
+ """alias of addparam"""
1251
+ return self.addparam(groupname, name, value, type, comment, jsonstr, whichdict)
1252
+
1253
+ def __getitem__(self, key:tuple[str, str]):
1254
+ """
1255
+ Retrieve :
1256
+ - value's parameter from group if key is a tuple or a list (group, param_name)
1257
+ - group dict if key is a string
1258
+ """
1259
+ if isinstance(key, tuple) or isinstance(key, list):
1260
+ group, name = key
1261
+ return self.get_param(group, name)
1262
+ elif isinstance(key, str):
1263
+ return self.get_group(key)
1264
+
1265
+ def __setitem__(self, key:str, value:Union[float, int, str, bool]):
1266
+ """set item, key is a tuple or a list (group, param_name)"""
1267
+
1268
+ if isinstance(key, tuple) or isinstance(key, list):
1269
+ group, name = key
1270
+ if self.get_param(group, name) is not None:
1271
+ self.change_param(group, name, value)
1272
+ else:
1273
+ self.addparam(group, name, value, self._detect_type_from_value(value))
1274
+
1275
+ def is_in_active(self, group:str, name:str = None) -> bool:
1276
+ """ Return True if the parameter is in the active parameters """
1277
+ return self.is_in(group, name, whichdict='Active')
1278
+
1279
+ def is_in_default(self, group:str, name:str = None) -> bool:
1280
+ """ Return True if the parameter is in the default parameters """
1281
+ return self.is_in(group, name, whichdict='Default')
1282
+
1283
+ def is_in(self, group:str, name:str = None, whichdict:Literal['All', 'Default', 'Active', 'IncGroup', '']='Active') -> bool:
1284
+ """ Return True if the parameter is in the whichdict parameters """
1285
+
1286
+ if whichdict=='All':
1287
+ locparams=[self.myparams, self.myparams_default]
1288
+ elif whichdict=='Default':
1289
+ locparams=[self.myparams_default]
1290
+ elif whichdict=='Active' or whichdict=='':
1291
+ locparams=[self.myparams]
1292
+
1293
+ for curdict in locparams:
1294
+ if group in curdict.keys():
1295
+ if name is not None:
1296
+ if name in curdict[group].keys():
1297
+ ret = True
1298
+ else:
1299
+ return False
859
1300
  else:
860
- logging.error(_('This parameter is neither in the current file nor in the default file!'))
861
- self.myparams[group][name] = {}
862
- param = self.myparams[group][name]
1301
+ ret = True
1302
+ else:
1303
+ return False
863
1304
 
864
- if(locgroup is None):
865
- self.myparams[group] = {}
866
- self.myparams[group][name] = self.myparams_default[group].copy()
867
- param = self.myparams[group][name]
868
- elif(param is None):
869
- locgroup[name] = {}
870
- locgroup[name] = self.myparams_default[group][name].copy()
1305
+ return ret
1306
+
1307
+ def get_param_dict(self, group:str, name:str) -> dict[str, Union[str, int, float, bool, tuple[int, int, int]]]:
1308
+ """
1309
+ Returns the parameter dict if found, otherwise None obj
1310
+ """
1311
+
1312
+ if self.is_in_active(group, name):
1313
+ return self.myparams[group][name]
1314
+ elif self.is_in_default(group, name):
1315
+ return self.myparams_default[group][name]
1316
+ else:
1317
+ return None
1318
+
1319
+ def get_type(self, group:str, name:str) -> Type_Param:
1320
+ """
1321
+ Returns the type of the parameter if found, otherwise None obj
1322
+ """
1323
+
1324
+ curtype = None
1325
+
1326
+ if self.is_in_default(group, name):
1327
+ if key_Param.TYPE in self.myparams_default[group][name].keys():
1328
+ curtype = self.myparams_default[group][name][key_Param.TYPE]
1329
+
1330
+ if self.is_in_active(group, name) and curtype is None:
1331
+ if key_Param.TYPE in self.myparams[group][name].keys():
1332
+ curtype = self.myparams[group][name][key_Param.TYPE]
1333
+
1334
+ return curtype if curtype is not None else Type_Param.String
1335
+
1336
+ def value_as_type(self, value, type,
1337
+ bool_as_int:bool = False,
1338
+ color_as:Union[int,str,float]= int) :
1339
+
1340
+ """ Convert the value to the right type """
1341
+ if type == Type_Param.Integer:
1342
+ value = int(value)
1343
+ elif type == Type_Param.Float:
1344
+ value = float(value)
1345
+ elif type == Type_Param.Logical:
1346
+ if isinstance(value,str):
1347
+ if value.lower() in ['.false.', 'false', 'faux']:
1348
+ value = False
1349
+ elif value.lower() in ['.true.', 'true', 'vrai']:
1350
+ value = True
1351
+ else:
1352
+ value = bool(value)
1353
+
1354
+ if bool_as_int:
1355
+ value = int(value)
1356
+
1357
+ elif type == Type_Param.Color:
1358
+ if color_as == str:
1359
+ value = str(value)
1360
+ else:
1361
+ if isinstance(value,str):
1362
+ value = value.replace('(','')
1363
+ value = value.replace(')','')
1364
+ value = value.split(',')
1365
+ elif isinstance(value, wx.Colour):
1366
+ value = [value.Red(), value.Green(), value.Blue(), value.Alpha()]
1367
+
1368
+ if color_as == int:
1369
+ value = tuple([int(x) for x in value])
1370
+
1371
+ elif color_as == float:
1372
+ value = tuple([float(x)/255. for x in value])
1373
+
1374
+ elif type == Type_Param.Integer_or_Float:
1375
+ value = float(value)
1376
+ elif type == Type_Param.String:
1377
+ value = str(value)
1378
+ elif type == Type_Param.File:
1379
+ value = str(value)
1380
+ elif type == Type_Param.Directory:
1381
+ value = str(value)
1382
+ elif type == Type_Param.Fontname:
1383
+ value = str(value)
1384
+ else:
1385
+ value = str(value)
1386
+
1387
+ return value
1388
+
1389
+ def get_param(self, group:str, name:str, default_value=None):
1390
+ """
1391
+ Returns the value of the parameter if found, otherwise None obj
1392
+
1393
+ used in __getitem__
1394
+ """
1395
+
1396
+ if self.is_in_active(group, name):
1397
+ element = self.myparams[group][name][key_Param.VALUE]
1398
+ elif self.is_in_default(group, name):
1399
+ element = self.myparams_default[group][name][key_Param.VALUE]
1400
+ else:
1401
+ element = default_value
1402
+ return element
1403
+
1404
+ # String conversion according to its type
1405
+ curType = self.get_type(group, name)
1406
+ return self.value_as_type(element, curType)
1407
+
1408
+ def get_group(self, group:str) -> dict:
1409
+ """
1410
+ Return the group dictionnary if found, otherwise None obj
1411
+ Check the active parameters first, then the default parameters
1412
+
1413
+ Used in __getitem__
1414
+ """
1415
+
1416
+ if self.is_in_active(group):
1417
+ return self.myparams[group]
1418
+ elif self.is_in_default(group):
1419
+ return self.myparams_default[group]
1420
+ else:
1421
+ return None
1422
+
1423
+ def _detect_type_from_value(self, value:Union[float, int, str, bool, tuple[int, int, int]]) -> Type_Param:
1424
+ """ Detect the type from the value """
1425
+
1426
+ if isinstance(value, float):
1427
+ return Type_Param.Float
1428
+ elif isinstance(value, int):
1429
+ return Type_Param.Integer
1430
+ elif isinstance(value, bool):
1431
+ return Type_Param.Logical
1432
+ elif isinstance(value, tuple):
1433
+ return Type_Param.Color
1434
+ else:
1435
+ return Type_Param.String
1436
+
1437
+ def change_param(self, group:str, name:str, value:Union[float, int, str, bool]):
1438
+ """ Modify the value of the parameter if found, otherwise None obj """
1439
+
1440
+ #essai pour voir si le groupe existe ou non
1441
+ param = self.get_param_dict(group, name)
1442
+
1443
+ if param is None:
1444
+ # le paramètre n'existe pas --> on l'ajoute
1445
+ if self.wx_exists:
1446
+ wx.MessageBox(_('This parameter is neither in the active group nor in the default group!'), _('Error'), wx.OK|wx.ICON_ERROR)
1447
+ else:
1448
+ logging.error(_('This parameter is neither in the current group nor in the default group!'))
1449
+
1450
+ self.add_param(group, name, value,
1451
+ self._detect_type_from_value(value),
1452
+ whichdict='All')
1453
+
1454
+ elif not self.is_in_active(group, name) and self.is_in_default(group, name):
1455
+ # le paramètre est dans les paramètres par défaut mais pas dans les paramètres actifs --> on l'ajoute
1456
+ default_value = self.myparams_default[group][name]
1457
+ self.add_param(group, name, value,
1458
+ default_value[key_Param.TYPE],
1459
+ comment=default_value[key_Param.COMMENT],
1460
+ jsonstr=default_value[key_Param.ADDED_JSON],
1461
+ whichdict='Active')
1462
+ elif self.is_in_active(group, name):
871
1463
  param = self.myparams[group][name]
1464
+ param[key_Param.VALUE] = value
1465
+
1466
+ if self.update_incr_at_every_change:
1467
+ self._update_IncGroup()
1468
+ self._update_IncParam()
1469
+
1470
+
1471
+ # GROUPES/PARAMETRES INCREMENTABLES
1472
+ # ---------------------------------
1473
+
1474
+ def update_incremental_groups_params(self, update_groups=True, update_params=True):
1475
+ """ Update the incremental groups and parameters """
1476
+
1477
+ if update_groups:
1478
+ self._update_IncGroup()
1479
+ if update_params:
1480
+ self._update_IncParam()
1481
+
1482
+
1483
+ def isIncrementable_group(self, group:Union[str, dict]) -> bool:
1484
+ """
1485
+ Return True if the group is incrementable
1486
+ """
1487
+
1488
+ if isinstance(group, str):
1489
+ if group in self.myIncGroup.keys():
1490
+ return True
1491
+ elif '$n$' in group:
1492
+ return True
1493
+ elif '$n(' in group:
1494
+ return True
1495
+ else:
1496
+ return False
1497
+ elif isinstance(group, dict):
1498
+ if group in self.myIncGroup:
1499
+ return True
1500
+ else:
1501
+ return False
1502
+
1503
+
1504
+ def isIncrementable_param(self, param:Union[str, dict]) -> bool:
1505
+ """
1506
+ Return True if the group is incrementable
1507
+ """
1508
+ if isinstance(param, str):
1509
+ if param in self.myIncGroup.keys():
1510
+ return True
1511
+ elif '$n$' in param:
1512
+ return True
1513
+ elif '$n(' in param:
1514
+ return True
1515
+ else:
1516
+ return False
1517
+ elif isinstance(param, dict):
1518
+ if param in self.myIncParam:
1519
+ return True
1520
+ else:
1521
+ return False
1522
+
1523
+ def add_IncGroup(self,
1524
+ group:str,
1525
+ min:int,
1526
+ max:int,
1527
+ refGroup:str,
1528
+ refParam:str):
1529
+
1530
+ if not group in self.myIncGroup:
1531
+ # creation d'un dict sur base du nom de groupe
1532
+ curdict = self.myIncGroup[group] = {}
1533
+ else:
1534
+ curdict = self.myIncGroup[group]
872
1535
 
873
- param["value"] = value
1536
+ curdict["Ref group"] = refGroup
1537
+ curdict["Ref param"] = refParam
1538
+ curdict["Min"] = int(min)
1539
+ curdict["Max"] = int(max)
1540
+ curdict["Dict"] = {}
874
1541
 
1542
+ if not "Saved" in curdict:
1543
+ curdict["Saved"] = {}
875
1544
 
1545
+ return curdict["Dict"]
876
1546
 
877
- # Mise à jour des groupes inctrémmentables:
878
- # Les groupes existants dans les paramètres courants seront sauvés dans le dicionnaire myIncGroup avec son incrément associé.
879
- # Tout groupe sauvé avec le même incrément sera écrasé.
880
- # Si le nombre associé au groupe est plus grand que désiré, tous les groupes en surplus seront sauvés dans dans le dicionnaire myIncGroup
881
- # mais supprimé du dictionnaire de paramètre courant.
882
- # S'il n'y a pas assez de groupe dans les paramètres courant, on les ajoute avec les valeurs sauvées, sinon avec des valeurs par défaut.
883
- # Also check the max and min values
884
- def Update_IncGroup(self, withGUI=False):
885
1547
 
1548
+ def _update_IncGroup(self, withGUI:bool=False):
1549
+ """
1550
+ Mise à jour des groupes inctrémmentables:
1551
+ Les groupes existants dans les paramètres courants seront sauvés dans le dicionnaire myIncGroup avec son incrément associé.
1552
+ Tout groupe sauvé avec le même incrément sera écrasé.
1553
+ Si le nombre associé au groupe est plus grand que désiré, tous les groupes en surplus seront sauvés dans dans le dicionnaire myIncGroup
1554
+ mais supprimé du dictionnaire de paramètre courant.
1555
+ S'il n'y a pas assez de groupe dans les paramètres courant, on les ajoute avec les valeurs sauvées, sinon avec des valeurs par défaut.
1556
+
1557
+ Also check the max and min values
1558
+ """
886
1559
  for curIncGroup in self.myIncGroup:
1560
+
1561
+ # groupe contenant le paramètre du nombre de groupes incrémentables
887
1562
  refGroup = self.myIncGroup[curIncGroup]["Ref group"]
1563
+ # paramètre contenant le nompbre de groupes incrémentables
888
1564
  refParam = self.myIncGroup[curIncGroup]["Ref param"]
1565
+ # nombre de groupes min
889
1566
  iterMin = int(self.myIncGroup[curIncGroup]["Min"])
1567
+ # nombre de groupes max
890
1568
  iterMax = int(self.myIncGroup[curIncGroup]["Max"])
1569
+
1570
+ # nombre de groupes
891
1571
  nbElements = int(self.get_param(refGroup,refParam))
892
- savedDict = {}
1572
+
1573
+ # dictionnaire de sauvegarde
1574
+ # savedDict = {}
893
1575
  savedDict = self.myIncGroup[curIncGroup]["Saved"]
894
1576
  templateDict = self.myIncGroup[curIncGroup]["Dict"]
1577
+
895
1578
  if(nbElements is None):
896
1579
  if self.wx_exists:
897
1580
  wx.MessageBox(_('The reference of the incrementable group does not exist!'), _('Error'), wx.OK|wx.ICON_ERROR)
@@ -900,77 +1583,194 @@ class Wolf_Param(wx.Frame):
900
1583
 
901
1584
  elif(nbElements>iterMax):
902
1585
  nbElements = iterMax
1586
+
903
1587
  # elif(nbElements<iterMin):
904
1588
  # nbElements = iterMax
905
1589
 
906
1590
  for i in range(1,nbElements+1):
1591
+ # nom indicé du groupe incrémpentable
907
1592
  curGroup = curIncGroup.replace("$n$",str(i))
1593
+
908
1594
  if(withGUI):
909
- groupexists = False
1595
+ # If a graphical interface exists, we need to ensure that the encoded data is transferred to memory/object first.
910
1596
  for curParam in templateDict:
911
- groupexists = self.Apply1ParamtoMemory(curGroup, curParam, groupexists=groupexists, isIncrementable=True, genGroup=curIncGroup)
1597
+ self._Apply1ParamtoMemory(curGroup, curParam, isIncrementable=True, genGroup=curIncGroup)
912
1598
 
913
1599
  if(curGroup in self.myparams):
914
- savedDict[curGroup] = {}
915
- savedDict[curGroup] = self.myparams[curGroup]
1600
+ # We keep a copy in the 'Saved' dictionary
1601
+ savedDict[curGroup] = deepcopy(self.myparams[curGroup]) # deepcopy is necessary because it is a dict od dict
1602
+
916
1603
  elif(curGroup in savedDict):
917
- self.myparams[curGroup] = {}
918
- self.myparams[curGroup] = savedDict[curGroup]
1604
+ # Priority to the saved dictionary, rather than the default dictionary
1605
+ self.myparams[curGroup] = deepcopy(savedDict[curGroup]) # deepcopy is necessary because it is a dict od dict
1606
+
919
1607
  else:
920
- self.myparams[curGroup] = {}
921
- self.myparams[curGroup] = templateDict.copy()
1608
+ # nothing found --> we create a new group from the default dictionary
1609
+ self.myparams[curGroup] = deepcopy(templateDict) # deepcopy is necessary because it is a dict od dict
922
1610
 
923
1611
  for i in range(nbElements+1,iterMax+1):
1612
+ # all potential groups with an index greater than the desired number of elements
924
1613
  curGroup = curIncGroup.replace("$n$",str(i))
1614
+
925
1615
  if(curGroup in self.myparams):
926
- savedDict[curGroup] = {}
927
- savedDict[curGroup] = self.myparams[curGroup].copy()
928
- self.myparams[curGroup] = {}
1616
+ savedDict[curGroup] = deepcopy(self.myparams[curGroup]) # deepcopy is necessary because it is a dict od dict, copy is not enough
929
1617
  del self.myparams[curGroup]
930
1618
  else:
931
1619
  break
932
1620
 
933
1621
 
934
- # Mise à jour des paramètres inctrémmentables:
935
- # Les paramètres existants dans les paramètres courants seront sauvés dans le dicionnaire myIncParam avec son incrément associé.
936
- # Tout groupe sauvé avec le même incrément sera écrasé.
937
- # Si le nombre associé au groupe est plus grand que désiré, tous les groupe en surplus seront sauvé dans dans le dicionnaire myIncParam
938
- # mais supprimé du dictionnaire de paramètre courant.
939
- # S'il n'y a pas assez de groupe dans les paramètres courant, on les ajoute avec les valeurs sauvées, sinon avec des valeurs par défaut.
940
- # Also check the max and min values
941
- def Update_IncParam(self, withGUI=False):
1622
+ def add_IncParam(self,
1623
+ group:str,
1624
+ name:str,
1625
+ value:Union[float, int, str],
1626
+ comment:str,
1627
+ type:Type_Param,
1628
+ min:int = 0,
1629
+ max:int = 0,
1630
+ refParam:str = None,
1631
+ refGroup:str = None,
1632
+ added_json:str = None):
1633
+ """
1634
+ Ajout d'un paramètre incrémentable
1635
+
1636
+ @param group: nom du groupe
1637
+ @param name: nom du paramètre
1638
+ @param value: valeur du paramètre
1639
+ @param comment: commentaire du paramètre
1640
+ @param type: type du paramètre
1641
+ @param min: valeur minimale
1642
+ @param max: valeur maximale
1643
+ @param refParam: nom du paramètre contenant le nombre de paramètres - doit être dans le même groupe
1644
+
1645
+ """
1646
+
1647
+ if added_json is not None:
1648
+ if isinstance(added_json, str):
1649
+ added_json = json.loads(added_json.replace('%json',''))
1650
+ elif isinstance(added_json, dict):
1651
+ pass
1652
+
1653
+ if group not in self.myIncParam:
1654
+ self.myIncParam[group] = {}
1655
+
1656
+ name, iterInfo = self._Extract_IncrInfo(name)
1657
+
1658
+ if iterInfo is not None:
1659
+ if len(iterInfo) == 3:
1660
+ refParam, min, max = iterInfo
1661
+ refGroup = None
1662
+ elif len(iterInfo) == 4:
1663
+ refGroup, refParam, min, max = iterInfo
1664
+ else:
1665
+ logging.error("ERROR : wrong number of arguments in the incrementable parameter name {}!".format(name))
1666
+
1667
+ refGroup = refGroup if refGroup is not None else group
1668
+
1669
+ assert min >=0, "ERROR : min must be >= 0"
1670
+ assert max >0, "ERROR : max must be > 0"
1671
+ assert max >= min, "ERROR : max must be >= min"
1672
+ assert refParam is not None, "ERROR : refParam can not be None"
1673
+ assert refGroup is not None, "ERROR : refGroup can not be None"
1674
+
1675
+ curdict = self.myIncParam[group][name] = {}
1676
+ curdict["Group"] = group
1677
+ curdict["Ref group"] = refGroup
1678
+ curdict["Ref param"] = refParam
1679
+ curdict["Min"] = min
1680
+ curdict["Max"] = max
1681
+ curdict["Dict"] = {}
1682
+ curdict["Dict"][name] = {}
1683
+ curdict["Dict"][name][key_Param.NAME] = name
1684
+ curdict["Dict"][name][key_Param.VALUE] = value
1685
+ curdict["Dict"][name][key_Param.COMMENT] = comment
1686
+ curdict["Dict"][name][key_Param.TYPE] = type
1687
+ if added_json is not None:
1688
+ curdict["Dict"][name][key_Param.ADDED_JSON] = added_json
1689
+ else:
1690
+ curdict["Dict"][name][key_Param.ADDED_JSON] = None
1691
+ curdict["Saved"] = {}
1692
+ curdict["Saved"][group] = {}
1693
+ curdict["Saved"][group][name] = {}
1694
+ curdict["Saved"][group][name][key_Param.NAME] = name
1695
+ curdict["Saved"][group][name][key_Param.VALUE] = value
1696
+ curdict["Saved"][group][name][key_Param.COMMENT] = comment
1697
+ curdict["Saved"][group][name][key_Param.TYPE] = type
1698
+ if added_json is not None:
1699
+ curdict["Saved"][group][name][key_Param.ADDED_JSON] = added_json
1700
+ else:
1701
+ curdict["Saved"][group][name][key_Param.ADDED_JSON] = None
1702
+
1703
+ return 0
1704
+
942
1705
 
1706
+ def _update_IncParam(self, withGUI:bool=False):
1707
+ """
1708
+ Mise à jour des paramètres inctrémmentables:
1709
+ Les paramètres existants dans les paramètres courants seront sauvés dans le dicionnaire myIncParam avec son incrément associé.
1710
+ Tout groupe sauvé avec le même incrément sera écrasé.
1711
+ Si le nombre associé au groupe est plus grand que désiré, tous les groupe en surplus seront sauvé dans dans le dicionnaire myIncParam
1712
+ mais supprimé du dictionnaire de paramètre courant.
1713
+ S'il n'y a pas assez de groupe dans les paramètres courant, on les ajoute avec les valeurs sauvées, sinon avec des valeurs par défaut.
1714
+
1715
+ Also check the max and min values
1716
+ """
943
1717
  for refGroup in self.myIncParam:
944
1718
  for curIncParam in self.myIncParam[refGroup]:
945
1719
  if(refGroup.find("$n$")>-1):
946
1720
  nbMax = int(self.myIncGroup[refGroup]["Max"])
947
- # refGroup = refGroup.replace("$n$","")
1721
+
948
1722
  i=1
949
1723
  while(i<nbMax+1):
950
1724
  curGroup = refGroup.replace("$n$",str(i))
951
1725
  i += 1
952
1726
  if curGroup in self.myparams:
953
- self.Update_OneIncParam(curIncParam, curGroup, genGroup=refGroup,withGUI=withGUI)
1727
+ self._Update_OneIncParam(curIncParam, curGroup, genGroup=refGroup, withGUI=withGUI)
954
1728
  else:
955
1729
  break
956
1730
  else:
957
- # Si tous les autres paramètres restent inchangés, ce groupe n'est pas présent dans le dictionnaire.
958
- # Il faut donc le créer
959
1731
  if not refGroup in self.myparams:
960
1732
  self.myparams[refGroup] = {}
961
- self.Update_OneIncParam(curIncParam, refGroup, genGroup=refGroup, withGUI=withGUI)
1733
+ self._Update_OneIncParam(curIncParam, refGroup, genGroup=refGroup, withGUI=withGUI)
1734
+
1735
+
1736
+ def _Update_OneIncParam(self, curIncParam:str, curGroup:str, genGroup:str, withGUI:bool=False):
1737
+ """
1738
+ Routine interne de mise à jour d'un seul paramétre incrémentable
1739
+
1740
+ @param curIncParam : nom du paramètre incrémentable - contient $n$ à remplacer par le numéro
1741
+ @param curGroup : nom du groupe de référence - ! peut être un groupe icrémentable !
1742
+ @param genGroup : nom du groupe générique dans lequel sont stockés les informations sur le paramètre (key, value, min, max, ...)
1743
+ @param withGUI : True si on est en mode GUI
1744
+
1745
+ """
962
1746
 
1747
+ refGroup = self.myIncParam[genGroup][curIncParam]["Ref group"] # groupe dans lequel est stocké le nombre de paramètres
1748
+ refParam = self.myIncParam[genGroup][curIncParam]["Ref param"] # nom du paramètre contenant le nombre de paramètres
1749
+ iterMin = int(self.myIncParam[genGroup][curIncParam]["Min"]) # nombre minimal de paramètres
1750
+ iterMax = int(self.myIncParam[genGroup][curIncParam]["Max"]) # nombre maximal de paramètres
1751
+
1752
+ # logging.info(curGroup+" / "+refParam)
1753
+
1754
+ if '$n$' in refGroup:
1755
+ # le groupe de référence est incrémentable --> le paramètre est à trouver dans le groupe courant
1756
+
1757
+ part_Group = refGroup.replace("$n$","")
1758
+ if part_Group in curGroup:
1759
+ refGroup = curGroup
1760
+ else:
1761
+ logging.error(_("ERROR : the reference group {} does not exist or is not well defined {}!".format(part_Group, curGroup)))
1762
+
1763
+ nbElements = self[(refGroup, refParam)] #int(self.get_param(refGroup, refParam))
1764
+
1765
+ if nbElements is None:
1766
+ logging.error(_("ERROR : the reference of the incrementable parameter does not exist!"))
1767
+ return
963
1768
 
964
- def Update_OneIncParam(self, curIncParam, refGroup, genGroup, withGUI=False):
965
- refParam = self.myIncParam[genGroup][curIncParam]["Ref param"]
966
- iterMin = int(self.myIncParam[genGroup][curIncParam]["Min"])
967
- iterMax = int(self.myIncParam[genGroup][curIncParam]["Max"])
968
- logging.info(refGroup+" / "+refParam)
969
- nbElements = int(self.get_param(refGroup,refParam))
970
1769
  savedDict = {}
971
1770
  savedDict = self.myIncParam[genGroup][curIncParam]["Saved"]
972
- if(not(refGroup in savedDict)):
973
- savedDict[refGroup] = {}
1771
+ if(not(curGroup in savedDict)):
1772
+ savedDict[curGroup] = {}
1773
+
974
1774
  templateDict = self.myIncParam[genGroup][curIncParam]["Dict"]
975
1775
  if(nbElements is None):
976
1776
  if self.wx_exists:
@@ -982,178 +1782,41 @@ class Wolf_Param(wx.Frame):
982
1782
  # elif(nbElements<iterMin):
983
1783
  # nbElements = iterMax
984
1784
 
985
- for i in range(1,nbElements+1):
1785
+ for i in range(1, nbElements+1):
986
1786
  curParam = curIncParam.replace("$n$",str(i))
987
1787
 
988
1788
  if(withGUI):
989
- groupexists = True
990
- groupexists = self.Apply1ParamtoMemory(refGroup, curParam, groupexists=groupexists,isIncrementable=True,genGroup=genGroup,genParam=curIncParam)
991
-
992
- if(curParam in self.myparams[refGroup]):
993
- savedDict[refGroup][curParam] = {}
994
- savedDict[refGroup][curParam] = self.myparams[refGroup][curParam]
995
- elif(curParam in savedDict[refGroup]):
996
- self.myparams[refGroup][curParam] = {}
997
- self.myparams[refGroup][curParam] = savedDict[refGroup][curParam]
998
- else:
999
- self.myparams[refGroup][curParam] = {}
1000
- self.myparams[refGroup][curParam] = templateDict.copy()
1001
-
1002
- for i in range(nbElements+1,iterMax+1):
1003
- curParam = curIncParam.replace("$n$",str(i))
1004
- if(curParam in self.myparams):
1005
- savedDict[refGroup][curParam] = {}
1006
- savedDict[refGroup][curParam] = self.myparams[refGroup][curParam].copy()
1007
- self.myparams[refGroup][curParam] = {}
1008
- else:
1009
- break
1010
-
1011
-
1012
-
1013
-
1014
- def Extract_IncrInfo(self, nameStr:str):
1015
-
1016
- iterInfo = []
1017
- newName = ""
1018
- posSep1 = nameStr.find("$")
1019
- posSep2 = nameStr[posSep1+1:].find("$")
1020
-
1021
- # Groupe avec indice incrémentable
1022
- if posSep1>-1 or posSep2>-1:
1023
- iterCode = nameStr[posSep1+1:posSep1+posSep2+1]
1024
- posSep1 = iterCode.find("(")
1025
- posSep2 = iterCode[posSep1:].find(")")
1026
- iterCode = iterCode[posSep1+1:posSep2+1]
1027
- newName = nameStr.replace("("+iterCode+")",'')
1028
- # newName = nameStr.replace(':','')
1029
- newName = newName.strip()
1030
- iterInfo = iterCode.split(',')
1031
- else:
1032
- newName = nameStr
1033
- iterInfo = None
1789
+ self._Apply1ParamtoMemory(curGroup, curParam, isIncrementable=True, genGroup=genGroup, genParam=curIncParam)
1034
1790
 
1035
- return newName, iterInfo
1036
-
1037
-
1038
- # @var genGroup : generic name of an incrementable group
1039
- # @var genParam : generic name of an incrementable param
1040
- def Apply1ParamtoMemory(self, group, param_name, groupexists=False, isIncrementable=False, genGroup="", genParam=""):
1791
+ if(curParam in self.myparams[curGroup]):
1792
+ savedDict[curGroup][curParam] = {}
1793
+ savedDict[curGroup][curParam] = self.myparams[curGroup][curParam]
1041
1794
 
1042
- if isIncrementable:
1043
- if(genParam!=""):
1044
- if(genGroup!=""):
1045
- param = self.myIncParam[genGroup][genParam]["Dict"]
1046
- else:
1047
- param = self.myIncParam[group][genParam]["Dict"]
1795
+ elif(curParam in savedDict[curGroup]):
1796
+ self.myparams[curGroup][curParam] = {}
1797
+ self.myparams[curGroup][curParam] = savedDict[curGroup][curParam]
1048
1798
 
1049
- elif(genGroup!=""):
1050
- param = self.myIncGroup[genGroup]["Dict"][param_name]
1051
-
1052
- else:
1053
- param = self.myparams_default[group][param_name]
1799
+ else:
1800
+ self.myparams[curGroup][curParam] = {}
1801
+ self.myparams[curGroup][curParam][key_Param.NAME] = curParam
1802
+ self.myparams[curGroup][curParam][key_Param.VALUE] = templateDict[curIncParam][key_Param.VALUE]
1803
+ self.myparams[curGroup][curParam][key_Param.COMMENT] = templateDict[curIncParam][key_Param.COMMENT]
1804
+ self.myparams[curGroup][curParam][key_Param.TYPE] = templateDict[curIncParam][key_Param.TYPE]
1805
+ if key_Param.ADDED_JSON in templateDict[curIncParam]:
1806
+ self.myparams[curGroup][curParam][key_Param.ADDED_JSON] = templateDict[curIncParam][key_Param.ADDED_JSON]
1807
+
1808
+ # transfert des paramètres en surplus dans le dictionnaire savedDict
1809
+ for i in range(nbElements+1, iterMax+1):
1810
+ curParam = curIncParam.replace("$n$",str(i))
1054
1811
 
1055
- curprop = self.prop.GetPropertyByName(group + param_name)
1056
- curdefprop = self.prop.GetPropertyByName('def' + group + param_name)
1812
+ if(curParam in self.myparams[curGroup]):
1813
+ savedDict[curGroup][curParam] = {}
1814
+ savedDict[curGroup][curParam] = self.myparams[curGroup][curParam].copy()
1815
+ self.myparams[curGroup].pop(curParam)
1057
1816
 
1058
- if curprop is not None :
1059
- toApply = False
1060
- if param['type']=='Integer_or_Float':
1061
- vpar = float(curprop.m_value)
1062
- if isIncrementable:
1063
- toApply = True
1064
- else:
1065
- vpardef = float(curdefprop.m_value)
1066
- if vpar != vpardef :
1067
- toApply = True
1068
- if toApply:
1069
- if not groupexists :
1070
- self.myparams[group]={}
1071
- groupexists = True
1072
- self.myparams[group][param_name] = {}
1073
- self.myparams[group][param_name]['value']=vpar
1074
- elif param['type']=='Integer':
1075
- if isIncrementable:
1076
- toApply = True
1077
- elif int(curprop.m_value) != int(curdefprop.m_value) :
1078
- toApply = True
1079
- if toApply:
1080
- if not groupexists :
1081
- self.myparams[group]={}
1082
- groupexists = True
1083
- self.myparams[group][param_name] = {}
1084
- self.myparams[group][param_name]['value']=int(curprop.m_value)
1085
- elif param['type']=='Logical':
1086
- if isIncrementable:
1087
- toApply = True
1088
- elif curprop.m_value != curdefprop.m_value :
1089
- toApply = True
1090
- if toApply:
1091
- if not groupexists :
1092
- self.myparams[group]={}
1093
- groupexists = True
1094
- self.myparams[group][param_name] = {}
1095
- if curprop.m_value:
1096
- self.myparams[group][param_name]['value']='.true.'
1097
- else:
1098
- self.myparams[group][param_name]['value']='.false.'
1099
- elif param['type']=='Float':
1100
- #try:
1101
- # vpar = float(curprop.m_value.replace('d','e'))
1102
- #except:
1103
- vpar = float(curprop.m_value)
1104
-
1105
- #try:
1106
- # vpardef = float(curdefprop.m_value.replace('d','e'))
1107
- #except:
1108
- if isIncrementable:
1109
- toApply = True
1110
- else:
1111
- vpardef = float(curdefprop.m_value)
1112
- if vpar != vpardef :
1113
- toApply = True
1114
-
1115
- if toApply:
1116
- if not groupexists :
1117
- self.myparams[group]={}
1118
- groupexists = True
1119
- self.myparams[group][param_name] = {}
1120
- self.myparams[group][param_name]['value']=vpar
1121
- elif param['type']=='File':
1122
- if isIncrementable:
1123
- toApply = True
1124
- elif str(curprop.m_value) != str(curdefprop.m_value) :
1125
- toApply = True
1126
- if toApply:
1127
- if not groupexists :
1128
- self.myparams[group]={}
1129
- groupexists = True
1130
- self.myparams[group][param_name] = {}
1131
- self.myparams[group][param_name]['value']=str(curprop.m_value)
1132
- elif param['type']=='Directory':
1133
- if isIncrementable:
1134
- toApply = True
1135
- elif str(curprop.m_value) != str(curdefprop.m_value) :
1136
- toApply = True
1137
- if toApply:
1138
- if not groupexists :
1139
- self.myparams[group]={}
1140
- groupexists = True
1141
- self.myparams[group][param_name] = {}
1142
- self.myparams[group][param_name]['value']=str(curprop.m_value)
1143
1817
  else:
1144
- if isIncrementable:
1145
- toApply = True
1146
- elif str(curprop.m_value) != str(curdefprop.m_value) :
1147
- toApply = True
1148
- if toApply:
1149
- if not groupexists :
1150
- self.myparams[group]={}
1151
- groupexists = True
1152
- self.myparams[group][param_name] = {}
1153
- self.myparams[group][param_name]['value']=str(curprop.m_value)
1154
-
1155
- return groupexists
1156
-
1818
+ # inutile de continuer, on a atteint le dernier paramètre
1819
+ break
1157
1820
 
1158
1821
  if __name__ =="__main__":
1159
1822
  test = Wolf_Param()