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