1
|
|
|
# Copyright 2014 Diamond Light Source Ltd. |
2
|
|
|
# |
3
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
4
|
|
|
# you may not use this file except in compliance with the License. |
5
|
|
|
# You may obtain a copy of the License at |
6
|
|
|
# |
7
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
8
|
|
|
# |
9
|
|
|
# Unless required by applicable law or agreed to in writing, software |
10
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
11
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12
|
|
|
# See the License for the specific language governing permissions and |
13
|
|
|
# limitations under the License. |
14
|
|
|
|
15
|
|
|
""" |
16
|
|
|
.. module:: content |
17
|
|
|
:platform: Unix |
18
|
|
|
:synopsis: Content class for the configurator |
19
|
|
|
|
20
|
|
|
.. moduleauthor:: Nicola Wadeson <[email protected]> |
21
|
|
|
|
22
|
|
|
""" |
23
|
|
|
import re |
24
|
|
|
import os |
25
|
|
|
import copy |
26
|
|
|
import inspect |
27
|
|
|
|
28
|
|
|
from savu.plugins import utils as pu |
29
|
|
|
from savu.data.plugin_list import PluginList |
30
|
|
|
import scripts.config_generator.parameter_utils as param_u |
31
|
|
|
|
32
|
|
|
from . import mutations |
33
|
|
|
|
34
|
|
|
|
35
|
|
|
class Content(object): |
36
|
|
|
def __init__(self, filename=None, level="basic"): |
37
|
|
|
self.disp_level = level |
38
|
|
|
self.plugin_list = PluginList() |
39
|
|
|
self.plugin_mutations = mutations.plugin_mutations |
40
|
|
|
self.param_mutations = mutations.param_mutations |
41
|
|
|
self.filename = filename |
42
|
|
|
self._finished = False |
43
|
|
|
self.failed = {} |
44
|
|
|
self.expand_dim = None |
45
|
|
|
|
46
|
|
|
def set_finished(self, check="y"): |
47
|
|
|
self._finished = True if check.lower() == "y" else False |
48
|
|
|
|
49
|
|
|
def is_finished(self): |
50
|
|
|
return self._finished |
51
|
|
|
|
52
|
|
|
def fopen(self, infile, update=False, skip=False): |
53
|
|
|
if os.path.exists(infile): |
54
|
|
|
self.plugin_list._populate_plugin_list(infile, active_pass=True) |
55
|
|
|
else: |
56
|
|
|
raise Exception("INPUT ERROR: The file does not exist.") |
57
|
|
|
self.filename = infile |
58
|
|
|
if update: |
59
|
|
|
self.plugin_mutations = self.check_mutations( |
60
|
|
|
self.plugin_mutations |
61
|
|
|
) |
62
|
|
|
self.param_mutations = self.check_mutations(self.param_mutations) |
63
|
|
|
self._apply_plugin_updates(skip) |
64
|
|
|
|
65
|
|
|
def check_mutations(self, mut_dict: dict): |
66
|
|
|
plist_version = self._version_to_float(self.plugin_list.version) |
67
|
|
|
# deleting elements while iterating invalidates the iterator |
68
|
|
|
# which raises a RuntimeError in Python 3. |
69
|
|
|
# Instead a copy of the dict is mutated and returned |
70
|
|
|
mut_dict_copy = mut_dict.copy() |
71
|
|
|
for key, subdict in mut_dict.items(): |
72
|
|
|
if "up_to_version" in subdict.keys(): |
73
|
|
|
up_to_version = self._version_to_float( |
74
|
|
|
subdict["up_to_version"] |
75
|
|
|
) |
76
|
|
|
if plist_version >= up_to_version: |
77
|
|
|
del mut_dict_copy[key] |
78
|
|
|
return mut_dict_copy |
79
|
|
|
|
80
|
|
|
def _version_to_float(self, version): |
81
|
|
|
if version is None: |
82
|
|
|
return 0 |
83
|
|
|
if isinstance(version, bytes): |
84
|
|
|
version = version.decode("ascii") |
85
|
|
|
split_vals = version.split(".") |
86
|
|
|
return float(".".join([split_vals[0], "".join(split_vals[1:])])) |
87
|
|
|
|
88
|
|
|
def display(self, formatter, **kwargs): |
89
|
|
|
# Set current level |
90
|
|
|
if "current_level" not in list(kwargs.keys()): |
91
|
|
|
kwargs["current_level"] = self.disp_level |
92
|
|
|
if ( |
93
|
|
|
"disp_level" in list(kwargs.keys()) |
94
|
|
|
and kwargs["disp_level"] is True |
95
|
|
|
): |
96
|
|
|
# Display level |
97
|
|
|
print(f"Level is set at '{kwargs['current_level']}'") |
98
|
|
|
else: |
99
|
|
|
# Display parameter |
100
|
|
|
kwargs["expand_dim"] = self.expand_dim |
101
|
|
|
print("\n" + formatter._get_string(**kwargs) + "\n") |
102
|
|
|
|
103
|
|
|
def check_file(self, filename): |
104
|
|
|
if not filename: |
105
|
|
|
raise Exception( |
106
|
|
|
"INPUT ERROR: Please specify the output filepath." |
107
|
|
|
) |
108
|
|
|
path = os.path.dirname(filename) |
109
|
|
|
path = path if path else "." |
110
|
|
|
if not os.path.exists(path): |
111
|
|
|
file_error = "INPUT_ERROR: Incorrect filepath." |
112
|
|
|
raise Exception(file_error) |
113
|
|
|
|
114
|
|
|
def save(self, filename, check="y", template=False): |
115
|
|
|
self.check_plugin_list_exists() |
116
|
|
|
# Check if a loader and saver are present. |
117
|
|
|
self.plugin_list._check_loaders() |
118
|
|
|
if check.lower() == "y": |
119
|
|
|
print(f"Saving file {filename}") |
120
|
|
|
if template: |
121
|
|
|
self.plugin_list.add_template(create=True) |
122
|
|
|
self.plugin_list._save_plugin_list(filename) |
123
|
|
|
else: |
124
|
|
|
print("The process list has NOT been saved.") |
125
|
|
|
|
126
|
|
|
def clear(self, check="y"): |
127
|
|
|
if check.lower() == "y": |
128
|
|
|
self.expand_dim = None |
129
|
|
|
self.plugin_list.plugin_list = [] |
130
|
|
|
self.plugin_list.clear_iterate_plugin_group_dicts() |
131
|
|
|
|
132
|
|
|
def check_plugin_list_exists(self): |
133
|
|
|
""" Check if plugin list is populated. """ |
134
|
|
|
pos_list = self.get_positions() |
135
|
|
|
if not pos_list: |
136
|
|
|
print("There are no items to access in your list.") |
137
|
|
|
raise Exception("Please add an item to the process list.") |
138
|
|
|
|
139
|
|
|
def add(self, name, str_pos): |
140
|
|
|
self.check_for_plugin_failure(name) |
141
|
|
|
plugin = pu.plugins[name]() |
142
|
|
|
plugin.get_plugin_tools()._populate_default_parameters() |
143
|
|
|
pos, str_pos = self.convert_pos(str_pos) |
144
|
|
|
self.insert(plugin, pos, str_pos) |
145
|
|
|
|
146
|
|
|
def iterate(self, args): |
147
|
|
|
if args.remove is not None: |
148
|
|
|
self.plugin_list.remove_iterate_plugin_groups(args.remove) |
149
|
|
|
elif args.set is not None: |
150
|
|
|
# create a dict representing a group of plugins to iterate over |
151
|
|
|
start = args.set[0] |
152
|
|
|
end = args.set[1] |
153
|
|
|
iterations = args.set[2] |
154
|
|
|
self.plugin_list.add_iterate_plugin_group(start, end, iterations) |
155
|
|
|
|
156
|
|
|
def refresh(self, str_pos, defaults=False, change=False): |
157
|
|
|
pos = self.find_position(str_pos) |
158
|
|
|
plugin_entry = self.plugin_list.plugin_list[pos] |
159
|
|
|
name = change if change else plugin_entry["name"] |
160
|
|
|
active = plugin_entry["active"] |
161
|
|
|
plugin = pu.plugins[name]() |
162
|
|
|
plugin.get_plugin_tools()._populate_default_parameters() |
163
|
|
|
keep = self.get(pos)["data"] if not defaults else None |
164
|
|
|
self.insert(plugin, pos, str_pos, replace=True) |
165
|
|
|
self.plugin_list.plugin_list[pos]["active"] = active |
166
|
|
|
if keep: |
167
|
|
|
self._update_parameters(plugin, name, keep, str_pos) |
168
|
|
|
|
169
|
|
|
def duplicate(self, dupl_pos, new): |
170
|
|
|
""" Duplicate the plugin at position dupl_pos |
171
|
|
|
and insert it at the new position |
172
|
|
|
|
173
|
|
|
:param dupl_pos: Position of the plugin to duplicate |
174
|
|
|
:param new: New plugin position |
175
|
|
|
""" |
176
|
|
|
pos = self.find_position(dupl_pos) |
177
|
|
|
new_pos, new_pos_str = self.convert_pos(new) |
178
|
|
|
plugin_entry = copy.deepcopy(self.plugin_list.plugin_list[pos]) |
179
|
|
|
plugin_entry["pos"] = new_pos_str |
180
|
|
|
self.plugin_list.plugin_list.insert(new_pos, plugin_entry) |
181
|
|
|
|
182
|
|
|
def check_for_plugin_failure(self, name): |
183
|
|
|
"""Check if the plugin failed to load |
184
|
|
|
|
185
|
|
|
:param name: plugin name |
186
|
|
|
""" |
187
|
|
|
if (name not in list(pu.plugins.keys()) |
188
|
|
|
or self.plugin_in_failed_dict(name)): |
189
|
|
|
if self.plugin_in_failed_dict(name): |
190
|
|
|
msg = f"IMPORT ERROR: {name} is unavailable due to" \ |
191
|
|
|
f" the following error:\n\t{self.failed[name]}" |
192
|
|
|
raise Exception(msg) |
193
|
|
|
raise Exception("INPUT ERROR: Unknown plugin %s" % name) |
194
|
|
|
|
195
|
|
|
def plugin_in_failed_dict(self, name): |
196
|
|
|
"""Check if plugin in failed dictionary |
197
|
|
|
|
198
|
|
|
:param name: plugin name |
199
|
|
|
:return: True if plugin name in the list of failed plugins |
200
|
|
|
""" |
201
|
|
|
failed_plugin_list = list(self.failed.keys()) if self.failed else [] |
202
|
|
|
return True if name in failed_plugin_list else False |
203
|
|
|
|
204
|
|
|
def check_preview_param(self, plugin_pos): |
205
|
|
|
""" Check that the plugin position number is valid and it contains |
206
|
|
|
a preview parameter |
207
|
|
|
|
208
|
|
|
:param plugin_pos: |
209
|
|
|
:return: |
210
|
|
|
""" |
211
|
|
|
pos = self.find_position(plugin_pos) |
212
|
|
|
plugin_entry = self.plugin_list.plugin_list[pos] |
213
|
|
|
parameters = plugin_entry["data"] |
214
|
|
|
if "preview" not in parameters: |
215
|
|
|
raise Exception("You can only use this command with " |
216
|
|
|
"plugins containing the preview parameter") |
217
|
|
|
|
218
|
|
|
def set_preview_display(self, expand_off, expand_dim, dim_view, |
219
|
|
|
plugin_pos): |
220
|
|
|
"""Set the dimensions_to_display value to "off" to prevent the |
221
|
|
|
preview parameter being shown in it's expanded form. |
222
|
|
|
|
223
|
|
|
If dimensions_to_display = "all", then all dimension slices are shown. |
224
|
|
|
If a number is provided to dim_view, that dimension is shown. |
225
|
|
|
|
226
|
|
|
:param expand_off: True if expand display should be turned off |
227
|
|
|
:param expand_dim: The dimension number to display, or "all" |
228
|
|
|
:param dim_view: True if only a certain dimension should be shown |
229
|
|
|
:param plugin_pos: Plugin position |
230
|
|
|
""" |
231
|
|
|
if expand_off is True: |
232
|
|
|
self.expand_dim = None |
233
|
|
|
print(f"Expand diplay has been turned off") |
234
|
|
|
else: |
235
|
|
|
self.check_preview_param(plugin_pos) |
236
|
|
|
dims_to_display = expand_dim if dim_view else "all" |
237
|
|
|
self.expand_dim = dims_to_display |
238
|
|
|
|
239
|
|
|
def _update_parameters(self, plugin, name, keep, str_pos): |
240
|
|
|
union_params = set(keep).intersection(set(plugin.parameters)) |
241
|
|
|
# Names of the parameter names present in both lists |
242
|
|
|
for param in union_params: |
243
|
|
|
valid_mod = self.modify(str_pos, param, keep[param], ref=True) |
244
|
|
|
if not valid_mod: |
245
|
|
|
self._print_default_message(plugin, param) |
246
|
|
|
# add any parameter mutations here |
247
|
|
|
classes = [c.__name__ for c in inspect.getmro(plugin.__class__)] |
248
|
|
|
m_dict = self.param_mutations |
249
|
|
|
keys = [k for k in list(m_dict.keys()) if k in classes] |
250
|
|
|
|
251
|
|
|
changes = False |
252
|
|
|
for k in keys: |
253
|
|
|
for entry in m_dict[k]["params"]: |
254
|
|
|
if entry["old"] in list(keep.keys()): |
255
|
|
|
changes = True |
256
|
|
|
val = keep[entry["old"]] |
257
|
|
|
if "eval" in list(entry.keys()): |
258
|
|
|
val = eval(entry["eval"]) |
259
|
|
|
self.modify(str_pos, entry["new"], val, ref=True) |
260
|
|
|
if changes: |
261
|
|
|
mutations.param_change_str(keep, plugin.parameters, name, keys) |
262
|
|
|
|
263
|
|
|
def _print_default_message(self, plugin, param): |
264
|
|
|
"""Print message to alert user that value has not been changed/updated |
265
|
|
|
|
266
|
|
|
:param plugin: plugin object |
267
|
|
|
:param param: parameter name |
268
|
|
|
""" |
269
|
|
|
pdefs = plugin.get_plugin_tools().get_param_definitions() |
270
|
|
|
default = pdefs[param]['default'] |
271
|
|
|
print(f"This has been replaced by the default {param}: " |
272
|
|
|
f"{default}.") |
273
|
|
|
|
274
|
|
|
def _apply_plugin_updates(self, skip=False): |
275
|
|
|
# Update old process lists that start from 0 |
276
|
|
|
the_list = self.plugin_list.plugin_list |
277
|
|
|
if "pos" in list(the_list[0].keys()) and the_list[0]["pos"] == "0": |
278
|
|
|
self.increment_positions() |
279
|
|
|
|
280
|
|
|
missing = [] |
281
|
|
|
pos = len(the_list) - 1 |
282
|
|
|
notices = mutations.plugin_notices |
283
|
|
|
|
284
|
|
|
for plugin in the_list[::-1]: |
285
|
|
|
# update old process lists to include 'active' flag |
286
|
|
|
if "active" not in list(plugin.keys()): |
287
|
|
|
plugin["active"] = True |
288
|
|
|
|
289
|
|
|
while True: |
290
|
|
|
name = the_list[pos]["name"] |
291
|
|
|
if name in notices.keys(): |
292
|
|
|
print(notices[name]["desc"]) |
293
|
|
|
|
294
|
|
|
# if a plugin is missing from all available plugins |
295
|
|
|
# then look for mutations in the plugin name |
296
|
|
|
search = True if name not in pu.plugins.keys() else False |
297
|
|
|
found = self._mutate_plugins(name, pos, search=search) |
298
|
|
|
if search and not found: |
299
|
|
|
str_pos = self.plugin_list.plugin_list[pos]["pos"] |
300
|
|
|
missing.append([name, str_pos]) |
301
|
|
|
self.remove(pos) |
302
|
|
|
pos -= 1 |
303
|
|
|
if name == the_list[pos]["name"]: |
304
|
|
|
break |
305
|
|
|
pos -= 1 |
306
|
|
|
|
307
|
|
|
for name, pos in missing[::-1]: |
308
|
|
|
if skip: |
309
|
|
|
print(f"Skipping plugin {pos}: {name}") |
310
|
|
|
else: |
311
|
|
|
message = ( |
312
|
|
|
f"\nPLUGIN ERROR: The plugin {name} is " |
313
|
|
|
f"unavailable in this version of Savu. \nThe plugin is " |
314
|
|
|
f"missing from the position {pos} in the process list. " |
315
|
|
|
f"\n Type open -s <process_list> to skip the broken " |
316
|
|
|
f"plugin." |
317
|
|
|
) |
318
|
|
|
raise Exception(f"Incompatible process list. {message}") |
319
|
|
|
|
320
|
|
|
def _mutate_plugins(self, name, pos, search=False): |
321
|
|
|
""" Perform plugin mutations. """ |
322
|
|
|
# check for case changes in plugin name |
323
|
|
|
if search: |
324
|
|
|
for key in pu.plugins.keys(): |
325
|
|
|
if name.lower() == key.lower(): |
326
|
|
|
str_pos = self.plugin_list.plugin_list[pos]["pos"] |
327
|
|
|
self.refresh(str_pos, change=key) |
328
|
|
|
return True |
329
|
|
|
|
330
|
|
|
# check mutations dict |
331
|
|
|
m_dict = self.plugin_mutations |
332
|
|
|
if name in m_dict.keys(): |
333
|
|
|
mutate = m_dict[name] |
334
|
|
|
if "replace" in mutate.keys(): |
335
|
|
|
if mutate["replace"] in list(pu.plugins.keys()): |
336
|
|
|
str_pos = self.plugin_list.plugin_list[pos]["pos"] |
337
|
|
|
self.refresh(str_pos, change=mutate["replace"]) |
338
|
|
|
print(mutate["desc"]) |
339
|
|
|
return True |
340
|
|
|
raise Exception( |
341
|
|
|
f"Replacement plugin {mutate['replace']} " |
342
|
|
|
f"unavailable for {name}" |
343
|
|
|
) |
344
|
|
|
elif "remove" in mutate.keys(): |
345
|
|
|
self.remove(pos) |
346
|
|
|
print(mutate["desc"]) |
347
|
|
|
else: |
348
|
|
|
raise Exception("Unknown mutation type.") |
349
|
|
|
return False |
350
|
|
|
|
351
|
|
|
def move(self, old, new): |
352
|
|
|
old_pos = self.find_position(old) |
353
|
|
|
entry = self.plugin_list.plugin_list[old_pos] |
354
|
|
|
self.remove(old_pos) |
355
|
|
|
new_pos, new = self.convert_pos(new) |
356
|
|
|
name = entry["name"] |
357
|
|
|
self.insert(pu.plugins[name](), new_pos, new) |
358
|
|
|
self.plugin_list.plugin_list[new_pos] = entry |
359
|
|
|
self.plugin_list.plugin_list[new_pos]["pos"] = new |
360
|
|
|
self.check_iterative_loops([old_pos + 1, new_pos + 1], 0) |
361
|
|
|
|
362
|
|
|
def replace(self, old, new_plugin): |
363
|
|
|
self.check_for_plugin_failure(new_plugin) |
364
|
|
|
old_pos = self.find_position(old) |
365
|
|
|
self.remove(old_pos) |
366
|
|
|
pos, str_pos = self.convert_pos(old) |
367
|
|
|
plugin = pu.plugins[new_plugin]() |
368
|
|
|
plugin.get_plugin_tools()._populate_default_parameters() |
369
|
|
|
self.insert(plugin, pos, str_pos) |
370
|
|
|
#self.plugin_list.plugin_list[pos]["pos"] = str_pos |
371
|
|
|
|
372
|
|
|
def modify(self, pos_str, param_name, value, default=False, ref=False, |
373
|
|
|
dim=False): |
374
|
|
|
"""Modify the plugin at pos_str and the parameter at param_name |
375
|
|
|
The new value will be set if it is valid. |
376
|
|
|
|
377
|
|
|
:param pos_str: The plugin position |
378
|
|
|
:param param_name: The parameter position/name |
379
|
|
|
:param value: The new parameter value |
380
|
|
|
:param default: True if value should be reverted to the default |
381
|
|
|
:param ref: boolean Refresh the plugin |
382
|
|
|
:param dim: The dimension to be modified |
383
|
|
|
|
384
|
|
|
returns: A boolean True if the value is a valid input for the |
385
|
|
|
selected parameter |
386
|
|
|
""" |
387
|
|
|
pos = self.find_position(pos_str) |
388
|
|
|
plugin_entry = self.plugin_list.plugin_list[pos] |
389
|
|
|
tools = plugin_entry["tools"] |
390
|
|
|
parameters = plugin_entry["data"] |
391
|
|
|
params = plugin_entry["param"] |
392
|
|
|
param_name, value = self.setup_modify(params, param_name, value, ref) |
393
|
|
|
default_str = ["-d", "--default"] |
394
|
|
|
if default or value in default_str: |
395
|
|
|
value = tools.get_default_value(param_name) |
396
|
|
|
self._change_value(param_name, value, tools, parameters) |
397
|
|
|
valid_modification = True |
398
|
|
|
else: |
399
|
|
|
value = self._catch_parameter_tuning_syntax(value, param_name) |
400
|
|
|
valid_modification = self.modify_main( |
401
|
|
|
param_name, value, tools, parameters, dim, pos_str |
402
|
|
|
) |
403
|
|
|
return valid_modification |
404
|
|
|
|
405
|
|
|
def _catch_parameter_tuning_syntax(self, value, param_name): |
406
|
|
|
"""Check if the new parameter value seems like it is written |
407
|
|
|
in parameter tuning syntax with colons. If it is, then append |
408
|
|
|
a semi colon onto the end. |
409
|
|
|
|
410
|
|
|
:param value: new parameter value |
411
|
|
|
:param param_name: |
412
|
|
|
:return: modified value with semi colon appended |
413
|
|
|
""" |
414
|
|
|
exempt_parameters = ["preview", "indices"] |
415
|
|
|
if self._is_multi_parameter_syntax(value) \ |
416
|
|
|
and param_name not in exempt_parameters: |
417
|
|
|
# Assume parameter tuning syntax is being used |
418
|
|
|
value = f"{value};" |
419
|
|
|
print("Parameter tuning syntax applied") |
420
|
|
|
return value |
421
|
|
|
|
422
|
|
|
def _is_multi_parameter_syntax(self, value): |
423
|
|
|
"""If the value contains two colons, is not a dictionary, |
424
|
|
|
and doesnt already contain a semi colon, then assume that |
425
|
|
|
it is using parameter tuning syntax |
426
|
|
|
|
427
|
|
|
:param value: new parameter value |
428
|
|
|
:return boolean True if parameter tuning syntax is being used |
429
|
|
|
""" |
430
|
|
|
isdict = re.findall(r"[\{\}]+", str(value)) |
431
|
|
|
return (isinstance(value, str) |
432
|
|
|
and value.count(":") >= 2 |
433
|
|
|
and not isdict |
434
|
|
|
and ";" not in value) |
435
|
|
|
|
436
|
|
|
def setup_modify(self, params, param_name, value, ref): |
437
|
|
|
"""Get the parameter keys in the correct order and find |
438
|
|
|
the parameter name string |
439
|
|
|
|
440
|
|
|
:param params: The plugin parameters |
441
|
|
|
:param param_name: The parameter position/name |
442
|
|
|
:param value: The new parameter value |
443
|
|
|
:param ref: boolean Refresh the plugin |
444
|
|
|
|
445
|
|
|
return: param_name str to avoid discrepancy, value |
446
|
|
|
""" |
447
|
|
|
if ref: |
448
|
|
|
# For a refresh, refresh all keys, including those with |
449
|
|
|
# dependencies (which have the display off) |
450
|
|
|
keys = params.keys() |
451
|
|
|
else: |
452
|
|
|
# Select the correct group and order of parameters according to that |
453
|
|
|
# on display to the user. This ensures correct parameter is modified. |
454
|
|
|
keys = pu.set_order_by_visibility(params) |
455
|
|
|
value = self.value(value) |
456
|
|
|
param_name = pu.param_to_str(param_name, keys) |
457
|
|
|
return param_name, value |
458
|
|
|
|
459
|
|
|
def modify_main(self, param_name, value, tools, parameters, dim, pos_str): |
460
|
|
|
"""Check the parameter is within the current parameter list. |
461
|
|
|
Check the new parameter value is valid, modify the parameter |
462
|
|
|
value, update defaults, check if dependent parameters should |
463
|
|
|
be made visible or hidden. |
464
|
|
|
|
465
|
|
|
:param param_name: The parameter position/name |
466
|
|
|
:param value: The new parameter value |
467
|
|
|
:param tools: The plugin tools |
468
|
|
|
:param parameters: The plugin parameters |
469
|
|
|
:param dim: The dimensions |
470
|
|
|
:param pos_str: The plugin position number |
471
|
|
|
|
472
|
|
|
returns: A boolean True if the value is a valid input for the |
473
|
|
|
selected parameter |
474
|
|
|
""" |
475
|
|
|
parameter_valid = False |
476
|
|
|
current_parameter_details = tools.param.get(param_name) |
477
|
|
|
current_plugin_name = tools.plugin_class.name |
478
|
|
|
|
479
|
|
|
# If dimensions are provided then alter preview param |
480
|
|
|
if self.preview_dimension_to_modify(dim, param_name): |
481
|
|
|
# Filter the dimension, dim1 or dim1.start |
482
|
|
|
dim, _slice = self._separate_dimension_and_slice(dim) |
483
|
|
|
value = self.modify_preview( |
484
|
|
|
parameters, param_name, value, dim, _slice |
485
|
|
|
) |
486
|
|
|
|
487
|
|
|
# If found, then the parameter is within the current parameter list |
488
|
|
|
# displayed to the user |
489
|
|
|
if current_parameter_details: |
490
|
|
|
value_check = pu._dumps(value) |
491
|
|
|
parameter_valid, error_str = param_u.is_valid( |
492
|
|
|
param_name, value_check, current_parameter_details |
493
|
|
|
) |
494
|
|
|
if parameter_valid: |
495
|
|
|
self._change_value(param_name, value, tools, parameters) |
496
|
|
|
else: |
497
|
|
|
print(f"ERROR: The value {value} for " |
498
|
|
|
f"{pos_str}.{param_name} is not correct.") |
499
|
|
|
print(error_str) |
500
|
|
|
else: |
501
|
|
|
print("Not in parameter keys.") |
502
|
|
|
return parameter_valid |
503
|
|
|
|
504
|
|
|
def _change_value(self, param_name, value, tools, parameters): |
505
|
|
|
""" Change the parameter "param_name" value inside the parameters list |
506
|
|
|
Update feedback messages for various dependant parameters |
507
|
|
|
|
508
|
|
|
:param param_name: The parameter position/name |
509
|
|
|
:param value: The new parameter value |
510
|
|
|
:param tools: The plugin tools |
511
|
|
|
:param parameters: The plugin parameters |
512
|
|
|
""" |
513
|
|
|
# Save the value |
514
|
|
|
parameters[param_name] = value |
515
|
|
|
tools.warn_dependents(param_name, value) |
516
|
|
|
# Update the list of parameters to hide those dependent on others |
517
|
|
|
tools.check_dependencies(parameters) |
518
|
|
|
|
519
|
|
|
def check_required_args(self, value, required): |
520
|
|
|
"""Check required argument 'value' is present |
521
|
|
|
|
522
|
|
|
:param value: Argument value |
523
|
|
|
:param required: bool, True if the argument is required |
524
|
|
|
""" |
525
|
|
|
if required and (not value): |
526
|
|
|
raise Exception("Please enter a value") |
527
|
|
|
|
528
|
|
|
if (not required) and value: |
529
|
|
|
raise Exception(f"Unrecognised argument: {value}") |
530
|
|
|
|
531
|
|
|
def preview_dimension_to_modify(self, dim, param_name): |
532
|
|
|
"""Check that the dimension string is only present when the parameter |
533
|
|
|
to modify is the preview parameter |
534
|
|
|
|
535
|
|
|
:param dim: Dimension string |
536
|
|
|
:param param_name: The parameter name (of the parameter to be modified) |
537
|
|
|
:return: True if dimension string is provided and the parameter to modify |
538
|
|
|
the preview parameter |
539
|
|
|
""" |
540
|
|
|
if dim: |
541
|
|
|
if param_name == "preview": |
542
|
|
|
return True |
543
|
|
|
else: |
544
|
|
|
raise Exception( |
545
|
|
|
"Please only use the dimension syntax when " |
546
|
|
|
"modifying the preview parameter." |
547
|
|
|
) |
548
|
|
|
return False |
549
|
|
|
|
550
|
|
|
def modify_dimensions(self, pos_str, dim, check="y"): |
551
|
|
|
"""Modify the plugin preview value. Remove or add dimensions |
552
|
|
|
to the preview parameter until the provided dimension number |
553
|
|
|
is reached. |
554
|
|
|
|
555
|
|
|
:param pos_str: The plugin position |
556
|
|
|
:param dim: The new number of dimensions |
557
|
|
|
:return True if preview is modified |
558
|
|
|
""" |
559
|
|
|
pos = self.find_position(pos_str) |
560
|
|
|
plugin_entry = self.plugin_list.plugin_list[pos] |
561
|
|
|
parameters = plugin_entry["data"] |
562
|
|
|
self.check_param_exists(parameters, "preview") |
563
|
|
|
current_prev_list = pu._dumps(parameters["preview"]) |
564
|
|
|
if not isinstance(current_prev_list, list): |
565
|
|
|
# Temporarily cover dict instance for preview |
566
|
|
|
print("This command is only possible for preview " |
567
|
|
|
"values of the type list") |
568
|
|
|
return False |
569
|
|
|
pu.check_valid_dimension(dim, []) |
570
|
|
|
if check.lower() == "y": |
571
|
|
|
while len(current_prev_list) > dim: |
572
|
|
|
current_prev_list.pop() |
573
|
|
|
while len(current_prev_list) < dim: |
574
|
|
|
current_prev_list.append(":") |
575
|
|
|
parameters["preview"] = current_prev_list |
576
|
|
|
return True |
577
|
|
|
return False |
578
|
|
|
|
579
|
|
|
def check_param_exists(self, parameters, pname): |
580
|
|
|
"""Check the parameter is present in the current parameter list |
581
|
|
|
|
582
|
|
|
:param parameters: Dictionary of parameters |
583
|
|
|
:param pname: Parameter name |
584
|
|
|
:return: |
585
|
|
|
""" |
586
|
|
|
if not parameters.get(pname): |
587
|
|
|
raise Exception( |
588
|
|
|
f"The {pname} parameter is not available" f" for this plugin." |
589
|
|
|
) |
590
|
|
|
|
591
|
|
|
|
592
|
|
|
def plugin_to_num(self, plugin_val, pl_index): |
593
|
|
|
"""Check the plugin is within the process list and |
594
|
|
|
return the number in the list. |
595
|
|
|
|
596
|
|
|
:param plugin_val: The dictionary of parameters |
597
|
|
|
:param pl_index: The plugin index (for use when there are multiple |
598
|
|
|
plugins of same name) |
599
|
|
|
:return: A plugin index number of a certain plugin in the process list |
600
|
|
|
""" |
601
|
|
|
if plugin_val.isdigit(): |
602
|
|
|
return plugin_val |
603
|
|
|
pl_names = [pl["name"] for pl in self.plugin_list.plugin_list] |
604
|
|
|
if plugin_val in pl_names: |
605
|
|
|
# Find the plugin number |
606
|
|
|
pl_indexes = [i for i, p in enumerate(pl_names) if p == plugin_val] |
607
|
|
|
# Subtract one to access correct list index. Add one to access |
608
|
|
|
# correct plugin position |
609
|
|
|
return str(pl_indexes[pl_index-1] +1) |
610
|
|
|
else: |
611
|
|
|
raise Exception("This plugin is not present in this process list.") |
612
|
|
|
|
613
|
|
|
|
614
|
|
|
def value(self, value): |
615
|
|
|
if not value.count(";"): |
616
|
|
|
try: |
617
|
|
|
value = eval(value) |
618
|
|
|
except (NameError, SyntaxError): |
619
|
|
|
try: |
620
|
|
|
value = eval(f"'{value}'") |
621
|
|
|
# if there is one quotation mark there will be an error |
622
|
|
|
except EOFError: |
623
|
|
|
raise EOFError( |
624
|
|
|
"There is an end of line error. Please check your" |
625
|
|
|
' input for the character "\'".' |
626
|
|
|
) |
627
|
|
|
except SyntaxError: |
628
|
|
|
raise SyntaxError( |
629
|
|
|
"There is a syntax error. Please check your input." |
630
|
|
|
) |
631
|
|
|
except: |
632
|
|
|
raise Exception("Please check your input.") |
633
|
|
|
return value |
634
|
|
|
|
635
|
|
|
def convert_to_ascii(self, value): |
636
|
|
|
ascii_list = [] |
637
|
|
|
for v in value: |
638
|
|
|
ascii_list.append(v.encode("ascii", "ignore")) |
639
|
|
|
return ascii_list |
640
|
|
|
|
641
|
|
|
def on_and_off(self, str_pos, index): |
642
|
|
|
print(("switching plugin %s %s" % (str_pos, index))) |
643
|
|
|
status = True if index == "ON" else False |
644
|
|
|
pos = self.find_position(str_pos) |
645
|
|
|
self.plugin_list.plugin_list[pos]["active"] = status |
646
|
|
|
|
647
|
|
|
def convert_pos(self, str_pos): |
648
|
|
|
""" Converts the display position (input) to the equivalent numerical |
649
|
|
|
position and updates the display position if required. |
650
|
|
|
|
651
|
|
|
:param str_pos: the plugin display position (input) string. |
652
|
|
|
:returns: the equivalent numerical position of str_pos and and updated\ |
653
|
|
|
str_pos. |
654
|
|
|
:rtype: (pos, str_pos) |
655
|
|
|
""" |
656
|
|
|
pos_list = self.get_split_positions() |
657
|
|
|
num = re.findall(r"\d+", str_pos)[0] |
658
|
|
|
letter = re.findall("[a-z]", str_pos) |
659
|
|
|
entry = [num, letter[0]] if letter else [num] |
660
|
|
|
|
661
|
|
|
# full value already exists in the list |
662
|
|
|
if entry in pos_list: |
663
|
|
|
index = pos_list.index(entry) |
664
|
|
|
return self.inc_positions(index, pos_list, entry, 1) |
665
|
|
|
|
666
|
|
|
# only the number exists in the list |
667
|
|
|
num_list = [pos_list[i][0] for i in range(len(pos_list))] |
668
|
|
|
if entry[0] in num_list: |
669
|
|
|
start = num_list.index(entry[0]) |
670
|
|
|
if len(entry) == 2: |
671
|
|
|
if len(pos_list[start]) == 2: |
672
|
|
|
idx = int([i for i in range(len(num_list)) if |
673
|
|
|
(num_list[i] == entry[0])][-1]) + 1 |
674
|
|
|
entry = [entry[0], str(chr(ord(pos_list[idx - 1][1]) + 1))] |
675
|
|
|
return idx, ''.join(entry) |
676
|
|
|
if entry[1] == 'a': |
677
|
|
|
self.plugin_list.plugin_list[start]['pos'] = entry[0] + 'b' |
678
|
|
|
return start, ''.join(entry) |
679
|
|
|
else: |
680
|
|
|
self.plugin_list.plugin_list[start]['pos'] = entry[0] + 'a' |
681
|
|
|
return start + 1, entry[0] + 'b' |
682
|
|
|
return self.inc_positions(start, pos_list, entry, 1) |
683
|
|
|
|
684
|
|
|
# number not in list |
685
|
|
|
entry[0] = str(int(num_list[-1]) + 1 if num_list else 1) |
686
|
|
|
if len(entry) == 2: |
687
|
|
|
entry[1] = "a" |
688
|
|
|
return len(self.plugin_list.plugin_list), "".join(entry) |
689
|
|
|
|
690
|
|
|
def increment_positions(self): |
691
|
|
|
"""Update old process lists that start plugin numbering from 0 to |
692
|
|
|
start from 1.""" |
693
|
|
|
for plugin in self.plugin_list.plugin_list: |
694
|
|
|
str_pos = plugin["pos"] |
695
|
|
|
num = str(int(re.findall(r"\d+", str_pos)[0]) + 1) |
696
|
|
|
letter = re.findall("[a-z]", str_pos) |
697
|
|
|
plugin["pos"] = "".join([num, letter[0]] if letter else [num]) |
698
|
|
|
|
699
|
|
|
def get_positions(self): |
700
|
|
|
""" Get a list of all current plugin entry positions. """ |
701
|
|
|
elems = self.plugin_list.plugin_list |
702
|
|
|
pos_list = [] |
703
|
|
|
for e in elems: |
704
|
|
|
pos_list.append(e["pos"]) |
705
|
|
|
return pos_list |
706
|
|
|
|
707
|
|
|
def get_param_arg_str(self, pos_str, subelem): |
708
|
|
|
"""Get the name of the parameter so that the display lists the |
709
|
|
|
correct item when the parameter order has been updated |
710
|
|
|
|
711
|
|
|
:param pos_str: The plugin position |
712
|
|
|
:param subelem: The parameter |
713
|
|
|
:return: The plugin.parameter_name argument |
714
|
|
|
""" |
715
|
|
|
pos = self.find_position(pos_str) |
716
|
|
|
current_parameter_list = self.plugin_list.plugin_list[pos]["param"] |
717
|
|
|
current_parameter_list_ordered = pu.set_order_by_visibility( |
718
|
|
|
current_parameter_list |
719
|
|
|
) |
720
|
|
|
param_name = pu.param_to_str(subelem, current_parameter_list_ordered) |
721
|
|
|
param_argument = pos_str + "." + param_name |
722
|
|
|
return param_argument |
723
|
|
|
|
724
|
|
|
def get_split_positions(self): |
725
|
|
|
""" Separate numbers and letters in positions. """ |
726
|
|
|
positions = self.get_positions() |
727
|
|
|
split_pos = [] |
728
|
|
|
for i in range(len(positions)): |
729
|
|
|
num = re.findall(r"\d+", positions[i])[0] |
730
|
|
|
letter = re.findall("[a-z]", positions[i]) |
731
|
|
|
split_pos.append([num, letter[0]] if letter else [num]) |
732
|
|
|
return split_pos |
733
|
|
|
|
734
|
|
|
def find_position(self, pos): |
735
|
|
|
""" Find the numerical index of a position (a string). """ |
736
|
|
|
pos_list = self.get_positions() |
737
|
|
|
if not pos_list: |
738
|
|
|
print("There are no items to access in your list.") |
739
|
|
|
raise Exception("Please add an item to the process list.") |
740
|
|
|
else: |
741
|
|
|
if pos not in pos_list: |
742
|
|
|
raise ValueError("Incorrect plugin position.") |
743
|
|
|
return pos_list.index(pos) |
744
|
|
|
|
745
|
|
|
def inc_positions(self, start, pos_list, entry, inc): |
746
|
|
|
if len(entry) == 1: |
747
|
|
|
self.inc_numbers(start, pos_list, inc) |
748
|
|
|
else: |
749
|
|
|
idx = [ |
750
|
|
|
i |
751
|
|
|
for i in range(start, len(pos_list)) |
752
|
|
|
if pos_list[i][0] == entry[0] |
753
|
|
|
] |
754
|
|
|
self.inc_letters(idx, pos_list, inc) |
755
|
|
|
return start, "".join(entry) |
756
|
|
|
|
757
|
|
|
def inc_numbers(self, start, pos_list, inc): |
758
|
|
|
for i in range(start, len(pos_list)): |
759
|
|
|
pos_list[i][0] = str(int(pos_list[i][0]) + inc) |
760
|
|
|
self.plugin_list.plugin_list[i]["pos"] = "".join(pos_list[i]) |
761
|
|
|
|
762
|
|
|
def inc_letters(self, idx, pos_list, inc): |
763
|
|
|
for i in idx: |
764
|
|
|
pos_list[i][1] = str(chr(ord(pos_list[i][1]) + inc)) |
765
|
|
|
self.plugin_list.plugin_list[i]["pos"] = "".join(pos_list[i]) |
766
|
|
|
|
767
|
|
|
def split_plugin_string(self, start, stop, subelem_view=False): |
768
|
|
|
"""Find the start and stop number for the plugin range selected. |
769
|
|
|
|
770
|
|
|
:param start: Plugin starting index (including a subelem value |
771
|
|
|
if permitted) |
772
|
|
|
:param stop: Plugin stopping index |
773
|
|
|
:param subelem_view: False if subelem value not permitted |
774
|
|
|
:return: range_dict containing start stop (and possible subelem) |
775
|
|
|
""" |
776
|
|
|
range_dict = {} |
777
|
|
|
if start: |
778
|
|
|
if subelem_view and "." in start: |
779
|
|
|
start, stop, subelem = self._split_subelem(start) |
780
|
|
|
range_dict["subelem"] = subelem |
781
|
|
|
else: |
782
|
|
|
start, stop = self._get_start_stop(start, stop) |
783
|
|
|
range_dict["start"] = start |
784
|
|
|
range_dict["stop"] = stop |
785
|
|
|
return range_dict |
786
|
|
|
|
787
|
|
|
def _get_start_stop(self, start, stop): |
788
|
|
|
"""Find the start and stop number for the plugin range selected """ |
789
|
|
|
start = self.find_position(start) |
790
|
|
|
stop = self.find_position(stop) + 1 if stop else start + 1 |
791
|
|
|
return start, stop |
792
|
|
|
|
793
|
|
|
def _split_subelem(self, start, config_disp=True): |
794
|
|
|
"""Separate the start string containing the plugin number, |
795
|
|
|
parameter(subelement), dimension and command |
796
|
|
|
|
797
|
|
|
:param start: The plugin to start at |
798
|
|
|
:param config_disp: True if command and dimension arguments |
799
|
|
|
are not permitted |
800
|
|
|
:return: start plugin, range_dict containing a subelem |
801
|
|
|
if a parameter is specified |
802
|
|
|
""" |
803
|
|
|
start, subelem, dim, command = self.separate_plugin_subelem( |
804
|
|
|
start, config_disp |
805
|
|
|
) |
806
|
|
|
start, stop = self._get_start_stop(start, "") |
807
|
|
|
return start, stop, subelem |
808
|
|
|
|
809
|
|
|
def _check_command_valid(self, plugin_param, config_disp): |
810
|
|
|
"""Check the plugin_param string length |
811
|
|
|
|
812
|
|
|
:param plugin_param: The string containing plugin number, parameter, |
813
|
|
|
and command |
814
|
|
|
:param config_disp: bool, True if command and dimension arguments are |
815
|
|
|
not permitted |
816
|
|
|
""" |
817
|
|
|
if config_disp: |
818
|
|
|
if not 1 < len(plugin_param) < 3: |
819
|
|
|
raise ValueError( |
820
|
|
|
"Use either 'plugin_pos.param_name' or" |
821
|
|
|
" 'plugin_pos.param_no'" |
822
|
|
|
) |
823
|
|
|
else: |
824
|
|
|
# The modify command is being used |
825
|
|
|
if len(plugin_param) <= 1: |
826
|
|
|
raise ValueError( |
827
|
|
|
"Please enter the plugin parameter to modify" |
828
|
|
|
". Either 'plugin_pos.param_name' or" |
829
|
|
|
" 'plugin_pos.param_no'" |
830
|
|
|
) |
831
|
|
|
if not 1 < len(plugin_param) < 5: |
832
|
|
|
raise ValueError( |
833
|
|
|
"Enter 'plugin_pos.param_no.dimension'. " |
834
|
|
|
"Following the dimension, use start/stop/step" |
835
|
|
|
" eg. '1.1.dim1.start' " |
836
|
|
|
) |
837
|
|
|
|
838
|
|
|
def separate_plugin_subelem(self, plugin_param, config_disp): |
839
|
|
|
"""Separate the plugin number,parameter (subelement) number |
840
|
|
|
and additional command if present. |
841
|
|
|
|
842
|
|
|
:param plugin_param: A string supplied by the user input which |
843
|
|
|
contains the plugin element to edit/display. eg "1.1.dim.command" |
844
|
|
|
:param config_disp: bool, True if command and dimension arguments are |
845
|
|
|
not permitted |
846
|
|
|
|
847
|
|
|
:returns plugin: The number of the plugin element |
848
|
|
|
subelem: The number of the parameter |
849
|
|
|
dim: The dimension |
850
|
|
|
command: The supplied command, eg expand or a dimension |
851
|
|
|
string |
852
|
|
|
""" |
853
|
|
|
plugin_param = plugin_param.split(".") |
854
|
|
|
plugin = plugin_param[0] |
855
|
|
|
# change str plugin name to a number |
856
|
|
|
start = self.find_position(plugin) |
857
|
|
|
self._check_command_valid(plugin_param, config_disp) |
858
|
|
|
subelem = plugin_param[1] |
859
|
|
|
if len(plugin_param) > 2: |
860
|
|
|
dim = self.dim_str_to_int(plugin_param[2]) |
861
|
|
|
command = str(dim) |
862
|
|
|
if len(plugin_param) == 4: |
863
|
|
|
self._check_command_str(plugin_param[3]) |
864
|
|
|
command += "." + plugin_param[3] |
865
|
|
|
else: |
866
|
|
|
dim, command = "", "" |
867
|
|
|
return plugin, subelem, dim, command |
868
|
|
|
|
869
|
|
|
def _check_command_str(self, command_str): |
870
|
|
|
"""Check the additional 1.1.dim.command for slice or 'expand' |
871
|
|
|
keywords |
872
|
|
|
""" |
873
|
|
|
command_list = ["expand", "start", "stop", "step", "chunk"] |
874
|
|
|
if command_str not in command_list: |
875
|
|
|
raise ValueError( |
876
|
|
|
"Following the dimension, use start/stop/step eg. " |
877
|
|
|
"'1.1.dim1.start' " |
878
|
|
|
) |
879
|
|
|
return command_str |
880
|
|
|
|
881
|
|
|
def insert(self, plugin, pos, str_pos, replace=False): |
882
|
|
|
plugin_dict = self.create_plugin_dict(plugin) |
883
|
|
|
plugin_dict["pos"] = str_pos |
884
|
|
|
if replace: |
885
|
|
|
self.plugin_list.plugin_list[pos] = plugin_dict |
886
|
|
|
else: |
887
|
|
|
self.plugin_list.plugin_list.insert(pos, plugin_dict) |
888
|
|
|
|
889
|
|
|
def create_plugin_dict(self, plugin): |
890
|
|
|
tools = plugin.get_plugin_tools() |
891
|
|
|
plugin_dict = {} |
892
|
|
|
plugin_dict["name"] = plugin.name |
893
|
|
|
plugin_dict["id"] = plugin.__module__ |
894
|
|
|
plugin_dict["data"] = plugin.parameters |
895
|
|
|
plugin_dict["active"] = True |
896
|
|
|
plugin_dict["tools"] = tools |
897
|
|
|
plugin_dict["param"] = tools.get_param_definitions() |
898
|
|
|
plugin_dict["doc"] = tools.docstring_info |
899
|
|
|
return plugin_dict |
900
|
|
|
|
901
|
|
|
def get(self, pos): |
902
|
|
|
return self.plugin_list.plugin_list[pos] |
903
|
|
|
|
904
|
|
|
def _separate_dimension_and_slice(self, command_str): |
905
|
|
|
"""Check the start stop step command |
906
|
|
|
|
907
|
|
|
param command_str: a string '1.1' containing the dimension |
908
|
|
|
and slice number seperated by a full stop |
909
|
|
|
|
910
|
|
|
:returns dim, slice |
911
|
|
|
""" |
912
|
|
|
if isinstance(command_str, str) and "." in command_str: |
913
|
|
|
# If the slice value is included |
914
|
|
|
slice_dict = {"start": 0, "stop": 1, "step": 2, "chunk": 3} |
915
|
|
|
_slice = slice_dict[command_str.split(".")[1]] |
916
|
|
|
dim = int(command_str.split(".")[0]) |
917
|
|
|
else: |
918
|
|
|
dim = int(command_str) |
919
|
|
|
_slice = "" |
920
|
|
|
return dim, _slice |
921
|
|
|
|
922
|
|
|
def dim_str_to_int(self, dim_str): |
923
|
|
|
"""Check the additional 1.1.dim keyword |
924
|
|
|
|
925
|
|
|
:param dim: A string 'dim1' specifying the dimension |
926
|
|
|
|
927
|
|
|
:return: dim - An integer dimension value |
928
|
|
|
""" |
929
|
|
|
number = "".join(l for l in dim_str if l.isdigit()) |
930
|
|
|
letters = "".join(l for l in dim_str if l.isalpha()) |
931
|
|
|
|
932
|
|
|
if letters == "dim" and number.strip(): |
933
|
|
|
dim = int(number) |
934
|
|
|
else: |
935
|
|
|
raise ValueError( |
936
|
|
|
"Following the second decimal place, please " |
937
|
|
|
"specify a dimension 1.1.dim1 or 1.preview.dim1" |
938
|
|
|
) |
939
|
|
|
return dim |
940
|
|
|
|
941
|
|
|
def modify_preview(self, parameters, param_name, value, dim, _slice): |
942
|
|
|
""" Check the entered value is valid and edit preview""" |
943
|
|
|
slice_list = [0, 1, 2, 3] |
944
|
|
|
type_check_value = pu._dumps(value) |
945
|
|
|
current_preview_value = pu._dumps(parameters[param_name]) |
946
|
|
|
pu.check_valid_dimension(dim, current_preview_value) |
947
|
|
|
if _slice in slice_list: |
948
|
|
|
# Modify this dimension and slice only |
949
|
|
|
if param_u._preview_dimension_singular(type_check_value): |
950
|
|
|
value = self._modify_preview_dimension_slice( |
951
|
|
|
value, current_preview_value, dim, _slice |
952
|
|
|
) |
953
|
|
|
else: |
954
|
|
|
raise ValueError( |
955
|
|
|
"Invalid preview dimension slice value. Please " |
956
|
|
|
"enter a float, an integer or a string including " |
957
|
|
|
"only start, mid and end keywords." |
958
|
|
|
) |
959
|
|
|
else: |
960
|
|
|
# If the entered value is a valid dimension value |
961
|
|
|
if param_u._preview_dimension(type_check_value): |
962
|
|
|
# Modify the whole dimension |
963
|
|
|
value = self._modify_preview_dimension( |
964
|
|
|
value, current_preview_value, dim |
965
|
|
|
) |
966
|
|
|
else: |
967
|
|
|
raise ValueError( |
968
|
|
|
"Invalid preview dimension value. Please " |
969
|
|
|
"enter a float, an integer or slice notation." |
970
|
|
|
) |
971
|
|
|
return value |
972
|
|
|
|
973
|
|
|
def _modify_preview_dimension_slice(self, value, current_val, dim, _slice): |
974
|
|
|
"""Modify the preview dimension slice value at the dimension (dim) |
975
|
|
|
provided |
976
|
|
|
|
977
|
|
|
:param value: The new value |
978
|
|
|
:param current_value: The current preview parameter value |
979
|
|
|
:param dim: The dimension to modify |
980
|
|
|
:param _slice: The slice value to modify |
981
|
|
|
:return: The modified value |
982
|
|
|
""" |
983
|
|
|
if not current_val: |
984
|
|
|
current_val = self._set_empty_list( |
985
|
|
|
dim, self._set_empty_dimension_slice(value, _slice) |
986
|
|
|
) |
987
|
|
|
else: |
988
|
|
|
current_val[dim - 1] = self._modified_slice_notation( |
989
|
|
|
current_val[dim - 1], value, _slice |
990
|
|
|
) |
991
|
|
|
return current_val |
992
|
|
|
|
993
|
|
|
def _modified_slice_notation(self, old_value, value, _slice): |
994
|
|
|
"""Change the current value at the provided slice |
995
|
|
|
|
996
|
|
|
:param old_value: Previous slice notation |
997
|
|
|
:param value: New value to set |
998
|
|
|
:param _slice: Slice to modify |
999
|
|
|
:return: Changed value (str/int) |
1000
|
|
|
""" |
1001
|
|
|
old_value = self._set_incomplete_slices(str(old_value), _slice) |
1002
|
|
|
if pu.is_slice_notation(old_value): |
1003
|
|
|
start_stop_split = old_value.split(":") |
1004
|
|
|
return self._get_modified_slice(start_stop_split, value, _slice) |
1005
|
|
|
elif _slice == 0: |
1006
|
|
|
# If there is no slice notation, only allow first slice to |
1007
|
|
|
# be modified |
1008
|
|
|
return value |
1009
|
|
|
else: |
1010
|
|
|
raise Exception( |
1011
|
|
|
"There is no existing slice notation to modify." |
1012
|
|
|
) |
1013
|
|
|
|
1014
|
|
|
def _get_modified_slice(self, start_stop_split, value, _slice): |
1015
|
|
|
if all(v == "" for v in start_stop_split): |
1016
|
|
|
return self._set_empty_dimension_slice(value, _slice) |
1017
|
|
|
else: |
1018
|
|
|
start_stop_split[_slice] = str(value) |
1019
|
|
|
start_stop_split = ":".join(start_stop_split) |
1020
|
|
|
return start_stop_split |
1021
|
|
|
|
1022
|
|
|
def _modify_preview_dimension(self, value, current_preview, dim): |
1023
|
|
|
"""Modify the preview list value at the dimension provided (dim) |
1024
|
|
|
|
1025
|
|
|
:param value: The new value |
1026
|
|
|
:param current_value: The current preview parameter list value |
1027
|
|
|
:param dim: The dimension to modify |
1028
|
|
|
:return: The modified value |
1029
|
|
|
""" |
1030
|
|
|
if not current_preview: |
1031
|
|
|
return self._set_empty_list(dim, value) |
1032
|
|
|
current_preview[dim - 1] = value |
1033
|
|
|
# Save the altered preview value |
1034
|
|
|
return current_preview |
1035
|
|
|
|
1036
|
|
|
def _set_empty_dimension_slice(self, value, _slice): |
1037
|
|
|
"""Set the empty dimension and insert colons to indicate |
1038
|
|
|
the correct slice notation. |
1039
|
|
|
|
1040
|
|
|
:param value: New value to set |
1041
|
|
|
:param _slice: start/stop/step/chunk value |
1042
|
|
|
:return: String for the new value |
1043
|
|
|
""" |
1044
|
|
|
return _slice * ":" + str(value) |
1045
|
|
|
|
1046
|
|
|
def _set_incomplete_slices(self, old_value, _slice): |
1047
|
|
|
"""Append default slice values.to the current string value, in order |
1048
|
|
|
to allow later slice values to be set |
1049
|
|
|
|
1050
|
|
|
:param old_value: Current string value with slice notation |
1051
|
|
|
:param _slice: slice notation index to be edited |
1052
|
|
|
:return: String with default slice notation entries set |
1053
|
|
|
""" |
1054
|
|
|
while old_value.count(":") < _slice: |
1055
|
|
|
old_value += ":" |
1056
|
|
|
return old_value |
1057
|
|
|
|
1058
|
|
|
def _set_empty_list(self, dim, value): |
1059
|
|
|
"""If the dimension is 1 then allow the whole empty list |
1060
|
|
|
to be set. |
1061
|
|
|
|
1062
|
|
|
:param dim: Dimension to be altered |
1063
|
|
|
:param value: New value |
1064
|
|
|
:return: List containing new value |
1065
|
|
|
""" |
1066
|
|
|
if dim == 1: |
1067
|
|
|
return [value] |
1068
|
|
|
else: |
1069
|
|
|
raise ValueError("You have not set earlier dimensions") |
1070
|
|
|
|
1071
|
|
|
def level(self, level): |
1072
|
|
|
""" Set the visibility level of parameters """ |
1073
|
|
|
if level: |
1074
|
|
|
self.disp_level = level |
1075
|
|
|
print(f"Level set to '{level}'") |
1076
|
|
|
else: |
1077
|
|
|
print(f"Level is set at '{self.disp_level}'") |
1078
|
|
|
|
1079
|
|
|
def remove(self, pos): |
1080
|
|
|
if pos >= self.size: |
1081
|
|
|
raise Exception( |
1082
|
|
|
"Cannot remove plugin %s as it does not exist." |
1083
|
|
|
% self.plugin_list.plugin_list[pos]["name"] |
1084
|
|
|
) |
1085
|
|
|
pos_str = self.plugin_list.plugin_list[pos]["pos"] |
1086
|
|
|
self.plugin_list.plugin_list.pop(pos) |
1087
|
|
|
pos_list = self.get_split_positions() |
1088
|
|
|
self.inc_positions(pos, pos_list, pos_str, -1) |
1089
|
|
|
|
1090
|
|
|
def check_iterative_loops(self, positions, direction): |
1091
|
|
|
""" |
1092
|
|
|
When a plugin is added, removed, or moved, check if any iterative loops |
1093
|
|
|
should be removed or shifted |
1094
|
|
|
""" |
1095
|
|
|
def moved_plugin(old_pos, new_pos): |
1096
|
|
|
is_in_loop = self.plugin_list.check_pos_in_iterative_loop(old_pos) |
1097
|
|
|
if is_in_loop and old_pos != new_pos: |
1098
|
|
|
self.plugin_list.remove_associated_iterate_group_dict( |
1099
|
|
|
old_pos, -1) |
1100
|
|
|
|
1101
|
|
|
if_will_be_in_loop = \ |
1102
|
|
|
self.plugin_list.check_pos_in_iterative_loop(new_pos) |
1103
|
|
|
if if_will_be_in_loop and old_pos != new_pos: |
1104
|
|
|
self.plugin_list.remove_associated_iterate_group_dict( |
1105
|
|
|
new_pos, -1) |
1106
|
|
|
|
1107
|
|
|
# shift any relevant loops |
1108
|
|
|
if new_pos < old_pos: |
1109
|
|
|
self.plugin_list.shift_range_iterative_loops( |
1110
|
|
|
[new_pos, old_pos], 1) |
1111
|
|
|
elif new_pos > old_pos: |
1112
|
|
|
self.plugin_list.shift_range_iterative_loops( |
1113
|
|
|
[old_pos, new_pos], -1) |
1114
|
|
|
|
1115
|
|
|
def added_removed_plugin(pos, direction): |
1116
|
|
|
is_in_loop = self.plugin_list.check_pos_in_iterative_loop(pos) |
1117
|
|
|
if is_in_loop: |
1118
|
|
|
# delete the associated loop |
1119
|
|
|
self.plugin_list.remove_associated_iterate_group_dict(pos, |
1120
|
|
|
direction) |
1121
|
|
|
|
1122
|
|
|
# check if there are any iterative loops in the process list |
1123
|
|
|
do_loops_exist = len(self.plugin_list.iterate_plugin_groups) > 0 |
1124
|
|
|
if do_loops_exist: |
1125
|
|
|
if direction == -1: |
1126
|
|
|
# shift the start+end of all loops after the plugin down by |
1127
|
|
|
# 1 |
1128
|
|
|
self.plugin_list.shift_subsequent_iterative_loops(pos, -1) |
1129
|
|
|
elif direction == 1: |
1130
|
|
|
# shift the start+end of all loops after the plugin up by 1 |
1131
|
|
|
self.plugin_list.shift_subsequent_iterative_loops(pos, 1) |
1132
|
|
|
|
1133
|
|
|
if direction == 0: |
1134
|
|
|
# a plugin has been moved |
1135
|
|
|
moved_plugin(positions[0], positions[1]) |
1136
|
|
|
else: |
1137
|
|
|
# a plugin has been added or removed |
1138
|
|
|
added_removed_plugin(positions[0], direction) |
1139
|
|
|
|
1140
|
|
|
@property |
1141
|
|
|
def size(self): |
1142
|
|
|
return len(self.plugin_list.plugin_list) |
1143
|
|
|
|
1144
|
|
|
def display_iterative_loops(self): |
1145
|
|
|
self.plugin_list.print_iterative_loops() |