fmu-manipulation-toolbox 1.8__py3-none-any.whl → 1.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- 'V1.8'
1
+ 'V1.8.1'
@@ -7,7 +7,7 @@ import uuid
7
7
  import xml.parsers.expat
8
8
  import zipfile
9
9
 
10
- from .fmu_container import FMUContainer, FMUContainerError
10
+ from .fmu_container import FMUContainer
11
11
 
12
12
  logger = logging.getLogger("fmu_manipulation_toolbox")
13
13
 
@@ -31,18 +31,26 @@ class Connection:
31
31
 
32
32
 
33
33
  class AssemblyNode:
34
- def __init__(self, name: str, step_size: float = None, mt=False, profiling=False,
34
+ def __init__(self, name: Optional[str], step_size: float = None, mt=False, profiling=False,
35
35
  auto_link=True, auto_input=True, auto_output=True):
36
36
  self.name = name
37
- self.step_size = step_size
37
+ if step_size:
38
+ try:
39
+ self.step_size = float(step_size)
40
+ except ValueError:
41
+ logger.warning(f"Step size '{step_size}' is incorrect format.")
42
+ self.step_size = None
43
+ else:
44
+ self.step_size = None
38
45
  self.mt = mt
39
46
  self.profiling = profiling
40
47
  self.auto_link = auto_link
41
48
  self.auto_input = auto_input
42
49
  self.auto_output = auto_output
43
- self.children: List[AssemblyNode] = []
44
50
 
45
- self.fmu_names_list: Set[str] = set()
51
+ self.parent: Optional[AssemblyNode] = None
52
+ self.children: List[AssemblyNode] = [] # sub-containers
53
+ self.fmu_names_list: Set[str] = set() # FMUs contained at this level
46
54
  self.input_ports: Dict[Port, str] = {}
47
55
  self.output_ports: Dict[Port, str] = {}
48
56
  self.start_values: Dict[Port, str] = {}
@@ -53,9 +61,14 @@ class AssemblyNode:
53
61
  if sub_node.name is None:
54
62
  sub_node.name = str(uuid.uuid4())+".fmu"
55
63
 
64
+ if sub_node.parent is not None:
65
+ raise AssemblyError(f"Internal Error: AssemblyNode {sub_node.name} is already parented.")
66
+
67
+ sub_node.parent = self
56
68
  self.fmu_names_list.add(sub_node.name)
57
69
  self.children.append(sub_node)
58
70
 
71
+
59
72
  def add_fmu(self, fmu_name: str):
60
73
  self.fmu_names_list.add(fmu_name)
61
74
 
@@ -135,9 +148,10 @@ class Assembly:
135
148
  self.transient_dirnames: Set[Path] = set()
136
149
 
137
150
  if not fmu_directory.is_dir():
138
- raise FMUContainerError(f"FMU directory is not valid: '{fmu_directory}'")
151
+ raise AssemblyError(f"FMU directory is not valid: '{fmu_directory}'")
139
152
 
140
- self.description_pathname = fmu_directory / self.filename # For inclusion in FMU
153
+ self.input_pathname = fmu_directory / self.filename
154
+ self.description_pathname = self.input_pathname # For inclusion in FMU
141
155
  self.root = None
142
156
  self.read()
143
157
 
@@ -169,7 +183,7 @@ class Assembly:
169
183
  elif self.filename.suffix == ".csv":
170
184
  self.read_csv()
171
185
  else:
172
- raise FMUContainerError(f"Not supported file format '{self.filename}")
186
+ raise AssemblyError(f"Not supported file format '{self.filename}")
173
187
 
174
188
  def write(self, filename: str):
175
189
  if filename.endswith(".csv"):
@@ -185,9 +199,9 @@ class Assembly:
185
199
  mt=self.default_mt, profiling=self.default_profiling,
186
200
  auto_input=self.default_auto_input, auto_output=self.default_auto_output)
187
201
 
188
- with open(self.description_pathname) as file:
202
+ with open(self.input_pathname) as file:
189
203
  reader = csv.reader(file, delimiter=';')
190
- self.check_csv_headers(reader)
204
+ self._check_csv_headers(reader)
191
205
  for i, row in enumerate(reader):
192
206
  if not row or row[0][0] == '#': # skip blank line of comment
193
207
  continue
@@ -198,36 +212,19 @@ class Assembly:
198
212
  logger.error(f"Line #{i+2}: expecting 5 columns. Line skipped.")
199
213
  continue
200
214
 
201
- rule = rule.upper()
202
- if rule in ("LINK", "INPUT", "OUTPUT", "DROP", "FMU", "START"):
203
- try:
204
- self._read_csv_rule(self.root, rule,
205
- from_fmu_filename, from_port_name, to_fmu_filename, to_port_name)
206
- except AssemblyError as e:
207
- logger.error(f"Line #{i+2}: {e}. Line skipped.")
208
- continue
209
- else:
210
- logger.error(f"Line #{i+2}: unexpected rule '{rule}'. Line skipped.")
211
-
212
- def write_csv(self, filename: Union[str, Path]):
213
- if self.root.children:
214
- raise AssemblyError("This assembly is not flat. Cannot export to CSV file.")
215
+ try:
216
+ self._read_csv_rule(self.root, rule.upper(),
217
+ from_fmu_filename, from_port_name, to_fmu_filename, to_port_name)
218
+ except AssemblyError as e:
219
+ logger.error(f"Line #{i+2}: {e}. Line skipped.")
220
+ continue
215
221
 
216
- with open(self.fmu_directory / filename, "wt") as outfile:
217
- outfile.write("rule;from_fmu;from_port;to_fmu;to_port\n")
218
- for fmu in self.root.fmu_names_list:
219
- outfile.write(f"FMU;{fmu};;;\n")
220
- for port, source in self.root.input_ports.items():
221
- outfile.write(f"INPUT;;{source};{port.fmu_name};{port.port_name}\n")
222
- for port, target in self.root.output_ports.items():
223
- outfile.write(f"OUTPUT;{port.fmu_name};{port.port_name};;{target}\n")
224
- for link in self.root.links:
225
- outfile.write(f"LINK;{link.from_port.fmu_name};{link.from_port.port_name};"
226
- f"{link.to_port.fmu_name};{link.to_port.port_name}\n")
227
- for port, value in self.root.start_values.items():
228
- outfile.write(f"START;{port.fmu_name};{port.port_name};{value};\n")
229
- for port in self.root.drop_ports:
230
- outfile.write(f"DROP;{port.fmu_name};{port.port_name};;\n")
222
+ @staticmethod
223
+ def _check_csv_headers(reader):
224
+ headers = next(reader)
225
+ headers_lowered = [h.lower() for h in headers]
226
+ if not headers_lowered == ["rule", "from_fmu", "from_port", "to_fmu", "to_port"]:
227
+ raise AssemblyError("Header (1st line of the file) is not well formatted.")
231
228
 
232
229
  @staticmethod
233
230
  def _read_csv_rule(node: AssemblyNode, rule: str, from_fmu_filename: str, from_port_name: str,
@@ -264,114 +261,145 @@ class Assembly:
264
261
  raise AssemblyError("Missing START ports information.")
265
262
 
266
263
  node.add_start_value(from_fmu_filename, from_port_name, to_fmu_filename)
267
- # no else: check on rule is already done in read_description()
264
+ else:
265
+ raise AssemblyError(f"unexpected rule '{rule}'. Line skipped.")
268
266
 
269
- @staticmethod
270
- def check_csv_headers(reader):
271
- headers = next(reader)
272
- if not headers == ["rule", "from_fmu", "from_port", "to_fmu", "to_port"]:
273
- raise AssemblyError("Header (1st line of the file) is not well formatted.")
267
+ def write_csv(self, filename: Union[str, Path]):
268
+ if self.root.children:
269
+ raise AssemblyError("This assembly is not flat. Cannot export to CSV file.")
270
+
271
+ with open(self.fmu_directory / filename, "wt") as outfile:
272
+ outfile.write("rule;from_fmu;from_port;to_fmu;to_port\n")
273
+ for fmu in self.root.fmu_names_list:
274
+ outfile.write(f"FMU;{fmu};;;\n")
275
+ for port, source in self.root.input_ports.items():
276
+ outfile.write(f"INPUT;;{source};{port.fmu_name};{port.port_name}\n")
277
+ for port, target in self.root.output_ports.items():
278
+ outfile.write(f"OUTPUT;{port.fmu_name};{port.port_name};;{target}\n")
279
+ for link in self.root.links:
280
+ outfile.write(f"LINK;{link.from_port.fmu_name};{link.from_port.port_name};"
281
+ f"{link.to_port.fmu_name};{link.to_port.port_name}\n")
282
+ for port, value in self.root.start_values.items():
283
+ outfile.write(f"START;{port.fmu_name};{port.port_name};{value};\n")
284
+ for port in self.root.drop_ports:
285
+ outfile.write(f"DROP;{port.fmu_name};{port.port_name};;\n")
274
286
 
275
287
  def read_json(self):
276
- with open(self.description_pathname) as file:
288
+ with open(self.input_pathname) as file:
277
289
  try:
278
290
  data = json.load(file)
279
291
  except json.decoder.JSONDecodeError as e:
280
- raise FMUContainerError(f"Cannot read json: {e}")
281
- self.root = self.json_decode_node(data)
292
+ raise AssemblyError(f"Cannot read json: {e}")
293
+ self.root = self._json_decode_node(data)
282
294
  if not self.root.name:
283
295
  self.root.name = str(self.filename.with_suffix(".fmu"))
284
296
 
297
+ def _json_decode_node(self, data:Dict) -> AssemblyNode:
298
+ name = data.get("name", None) # 1
299
+ step_size = data.get("step_size", self.default_step_size) # 7
300
+ auto_link = data.get("auto_link", self.default_auto_link) # 4
301
+ auto_input = data.get("auto_input", self.default_auto_input) # 5
302
+ auto_output = data.get("auto_output", self.default_auto_output) # 6
303
+ mt = data.get("mt", self.default_mt) # 2
304
+ profiling = data.get("profiling", self.default_profiling) # 3
305
+
306
+ node = AssemblyNode(name, step_size=step_size, auto_link=auto_link, mt=mt, profiling=profiling,
307
+ auto_input=auto_input, auto_output=auto_output)
308
+
309
+ for key, value in data.items():
310
+ if key in ('name', 'step_size', 'auto_link', 'auto_input', 'auto_output', 'mt', 'profiling'):
311
+ continue # Already read
312
+
313
+ elif key == "container": # 8
314
+ if not isinstance(value, list):
315
+ raise AssemblyError("JSON: 'container' keyword should define a list.")
316
+ for sub_data in value:
317
+ node.add_sub_node(self._json_decode_node(sub_data))
318
+
319
+ elif key == "fmu": # 9
320
+ if not isinstance(value, list):
321
+ raise AssemblyError("JSON: 'fmu' keyword should define a list.")
322
+ for fmu in value:
323
+ node.add_fmu(fmu)
324
+
325
+ elif key == "input": # 10
326
+ self._json_decode_keyword('input', value, node.add_input)
327
+
328
+ elif key == "output": # 11
329
+ self._json_decode_keyword('output', value, node.add_output)
330
+
331
+ elif key == "link": # 12
332
+ self._json_decode_keyword('link', value, node.add_link)
333
+
334
+ elif key == "start": # 13
335
+ self._json_decode_keyword('start', value, node.add_start_value)
336
+
337
+ elif key == "drop": #14
338
+ self._json_decode_keyword('drop', value, node.add_drop_port)
339
+
340
+ else:
341
+ logger.error(f"JSON: unexpected keyword {key}. Skipped.")
342
+
343
+ return node
344
+
345
+ @staticmethod
346
+ def _json_decode_keyword(keyword: str, value, function):
347
+ if not isinstance(value, list):
348
+ raise AssemblyError(f"JSON: '{keyword}' keyword should define a list.")
349
+ for line in value:
350
+ if not isinstance(line, list):
351
+ raise AssemblyError(f"JSON: unexpected '{keyword}' value: {line}.")
352
+ try:
353
+ function(*line)
354
+ except TypeError:
355
+ raise AssemblyError(f"JSON: '{keyword}' value does not contain right number of fields: {line}.")
356
+
357
+
285
358
  def write_json(self, filename: Union[str, Path]):
286
359
  with open(self.fmu_directory / filename, "wt") as file:
287
- data = self.json_encode_node(self.root)
360
+ data = self._json_encode_node(self.root)
288
361
  json.dump(data, file, indent=2)
289
362
 
290
- def json_encode_node(self, node: AssemblyNode) -> Dict[str, Any]:
363
+ def _json_encode_node(self, node: AssemblyNode) -> Dict[str, Any]:
291
364
  json_node = dict()
292
- json_node["name"] = node.name
293
- json_node["mt"] = node.mt
294
- json_node["profiling"] = node.profiling
295
- json_node["auto_link"] = node.auto_link
365
+ json_node["name"] = node.name # 1
366
+ json_node["mt"] = node.mt # 2
367
+ json_node["profiling"] = node.profiling # 3
368
+ json_node["auto_link"] = node.auto_link # 4
369
+ json_node["auto_input"] = node.auto_input # 5
370
+ json_node["auto_output"] = node.auto_output # 6
371
+
296
372
  if node.step_size:
297
- json_node["step_size"] = node.step_size
373
+ json_node["step_size"] = node.step_size # 7
298
374
 
299
375
  if node.children:
300
- json_node["container"] = [self.json_encode_node(child) for child in node.children]
376
+ json_node["container"] = [self._json_encode_node(child) for child in node.children] # 8
301
377
 
302
378
  if node.fmu_names_list:
303
- json_node["fmu"] = [f"{fmu_name}" for fmu_name in sorted(node.fmu_names_list)]
379
+ json_node["fmu"] = [f"{fmu_name}" for fmu_name in sorted(node.fmu_names_list)] #9
304
380
 
305
381
  if node.input_ports:
306
- json_node["input"] = [[f"{source}", f"{port.fmu_name}", f"{port.port_name}"]
382
+ json_node["input"] = [[f"{source}", f"{port.fmu_name}", f"{port.port_name}"] # 10
307
383
  for port, source in node.input_ports.items()]
308
384
 
309
385
  if node.output_ports:
310
- json_node["output"] = [[f"{port.fmu_name}", f"{port.port_name}", f"{target}"]
386
+ json_node["output"] = [[f"{port.fmu_name}", f"{port.port_name}", f"{target}"] # 11
311
387
  for port, target in node.output_ports.items()]
312
388
 
313
389
  if node.links:
314
- json_node["link"] = [[f"{link.from_port.fmu_name}", f"{link.from_port.port_name}",
390
+ json_node["link"] = [[f"{link.from_port.fmu_name}", f"{link.from_port.port_name}", # 12
315
391
  f"{link.to_port.fmu_name}", f"{link.to_port.port_name}"]
316
392
  for link in node.links]
317
393
 
318
394
  if node.start_values:
319
- json_node["start"] = [[f"{port.fmu_name}", f"{port.port_name}", value]
395
+ json_node["start"] = [[f"{port.fmu_name}", f"{port.port_name}", value] # 13
320
396
  for port, value in node.start_values.items()]
321
397
 
322
398
  if node.drop_ports:
323
- json_node["drop"] = [[f"{port.fmu_name}", f"{port.port_name}"] for port in node.drop_ports]
399
+ json_node["drop"] = [[f"{port.fmu_name}", f"{port.port_name}"] for port in node.drop_ports] # 14
324
400
 
325
401
  return json_node
326
402
 
327
- def json_decode_node(self, data) -> AssemblyNode:
328
- name = data.get("name", None)
329
- step_size = data.get("step_size", self.default_step_size)
330
- auto_link = data.get("auto_link", self.default_auto_link)
331
- auto_input = data.get("auto_input", self.default_auto_input)
332
- auto_output = data.get("auto_output", self.default_auto_output)
333
- mt = data.get("mt", self.default_mt)
334
- profiling = data.get("profiling", self.default_profiling)
335
-
336
- node = AssemblyNode(name, step_size=step_size, auto_link=auto_link, mt=mt, profiling=profiling,
337
- auto_input=auto_input, auto_output=auto_output)
338
-
339
- if "container" in data:
340
- if not isinstance(data["container"], list):
341
- raise FMUContainerError("JSON: 'container' keyword should define a list.")
342
- for sub_data in data["container"]:
343
- node.add_sub_node(self.json_decode_node(sub_data))
344
-
345
- if "fmu" in data:
346
- if not isinstance(data["fmu"], list):
347
- raise FMUContainerError("JSON: 'fmu' keyword should define a list.")
348
- for fmu in data["fmu"]:
349
- node.add_fmu(fmu)
350
-
351
- if "input" in data:
352
- if not isinstance(data["input"], list):
353
- raise FMUContainerError("JSON: 'input' keyword should define a list.")
354
- for line in data["input"]:
355
- node.add_input(line[1], line[2], line[0])
356
-
357
- if "output" in data:
358
- if not isinstance(data["output"], list):
359
- raise FMUContainerError("JSON: 'output' keyword should define a list.")
360
- for line in data["output"]:
361
- node.add_output(line[0], line[1], line[2])
362
-
363
- if "start" in data:
364
- if not isinstance(data["start"], list):
365
- raise FMUContainerError("JSON: 'start' keyword should define a list.")
366
- for line in data["start"]:
367
- node.add_start_value(line[0], line[1], line[2])
368
-
369
- if "drop" in data:
370
- for line in data["drop"]:
371
- node.add_drop_port(line[0], line[1])
372
-
373
- return node
374
-
375
403
  def read_ssp(self):
376
404
  logger.warning("This feature is ALPHA stage.")
377
405
 
@@ -388,11 +416,11 @@ class Assembly:
388
416
  mt=self.default_mt, profiling=self.default_profiling,
389
417
  auto_input=self.default_auto_input, auto_output=self.default_auto_output)
390
418
  self.root = sdd.parse(self.description_pathname)
419
+ self.root.name = str(self.filename.with_suffix(".fmu"))
391
420
 
392
421
  def make_fmu(self, dump_json=False):
393
422
  if dump_json:
394
- dump_file = (self.description_pathname.with_stem(self.description_pathname.stem + "-dump")
395
- .with_suffix(".json"))
423
+ dump_file = Path(self.input_pathname.stem + "-dump").with_suffix(".json")
396
424
  logger.info(f"Dump Json '{dump_file}'")
397
425
  self.write_json(dump_file)
398
426
  self.root.make_fmu(self.fmu_directory, debug=self.debug, description_pathname=self.description_pathname)
@@ -5,7 +5,7 @@ from colorama import Fore, Style, init
5
5
 
6
6
  from .fmu_operations import *
7
7
  from .fmu_container import FMUContainerError
8
- from .assembly import Assembly
8
+ from .assembly import Assembly, AssemblyError
9
9
  from .checker import checker_list
10
10
  from .version import __version__ as version
11
11
  from .help import Help
@@ -211,7 +211,7 @@ def fmucontainer():
211
211
  except FileNotFoundError as e:
212
212
  logger.fatal(f"Cannot read file: {e}")
213
213
  continue
214
- except FMUContainerError as e:
214
+ except (FMUContainerError, AssemblyError) as e:
215
215
  logger.fatal(f"{filename}: {e}")
216
216
  continue
217
217
 
@@ -291,7 +291,7 @@ class FMUContainer:
291
291
 
292
292
  self.start_values[cport] = value
293
293
 
294
- def find_input(self, port_to_connect: FMUPort) -> Union[ContainerPort, None]:
294
+ def find_input(self, port_to_connect: FMUPort) -> Optional[ContainerPort]:
295
295
  for fmu in self.execution_order:
296
296
  for port in fmu.ports.values():
297
297
  if (port.causality == 'input' and port.name == port_to_connect.name
@@ -346,31 +346,26 @@ class FMUContainer:
346
346
 
347
347
  return step_size
348
348
 
349
- def sanity_check(self, step_size: Union[float, None]):
350
- nb_error = 0
349
+ def sanity_check(self, step_size: Optional[float]):
351
350
  for fmu in self.execution_order:
352
351
  if not fmu.step_size:
353
352
  continue
354
353
  ts_ratio = step_size / fmu.step_size
355
354
  if ts_ratio < 1.0:
356
- logger.error(f"Container step_size={step_size}s is lower than FMU '{fmu.name}' "
357
- f"step_size={fmu.step_size}s")
355
+ logger.warning(f"Container step_size={step_size}s is lower than FMU '{fmu.name}' "
356
+ f"step_size={fmu.step_size}s.")
358
357
  if ts_ratio != int(ts_ratio):
359
- logger.error(f"Container step_size={step_size}s should divisible by FMU '{fmu.name}' "
360
- f"step_size={fmu.step_size}s")
358
+ logger.warning(f"Container step_size={step_size}s should divisible by FMU '{fmu.name}' "
359
+ f"step_size={fmu.step_size}s.")
361
360
  for port_name in fmu.ports:
362
361
  cport = ContainerPort(fmu, port_name)
363
362
  if cport not in self.rules:
364
363
  if cport.port.causality == 'input':
365
364
  logger.error(f"{cport} is not connected")
366
- nb_error += 1
367
365
  if cport.port.causality == 'output':
368
366
  logger.warning(f"{cport} is not connected")
369
367
 
370
- if nb_error:
371
- raise FMUContainerError(f"Some ports are not connected.")
372
-
373
- def make_fmu(self, fmu_filename: Union[str, Path], step_size: Union[float, None] = None, debug=False, mt=False,
368
+ def make_fmu(self, fmu_filename: Union[str, Path], step_size: Optional[float] = None, debug=False, mt=False,
374
369
  profiling=False):
375
370
  if isinstance(fmu_filename, str):
376
371
  fmu_filename = Path(fmu_filename)
@@ -1,10 +1,10 @@
1
1
  import os.path
2
2
  import sys
3
3
  from .version import __version__ as version
4
- from PyQt5.QtCore import Qt, QObject, QUrl, pyqtSignal, QDir
5
- from PyQt5.QtWidgets import (QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QPushButton, QFileDialog,
6
- QTextBrowser, QInputDialog, QMenu, QAction)
7
- from PyQt5.QtGui import QPixmap, QImage, QFont, QTextCursor, QIcon, QColor, QPainter, QBrush, QDesktopServices
4
+ from PyQt6.QtCore import Qt, QObject, QUrl, pyqtSignal, QDir, QSize, QPoint
5
+ from PyQt6.QtWidgets import (QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QPushButton, QFileDialog,
6
+ QTextBrowser, QInputDialog, QMenu)
7
+ from PyQt6.QtGui import (QPixmap, QImage, QFont, QTextCursor, QIcon, QDesktopServices, QAction, QPainter, QColor)
8
8
  import textwrap
9
9
  from functools import partial
10
10
  from typing import Optional
@@ -42,7 +42,7 @@ class DropZoneWidget(QLabel):
42
42
 
43
43
  def dropEvent(self, event):
44
44
  if event.mimeData().hasImage:
45
- event.setDropAction(Qt.CopyAction)
45
+ event.setDropAction(Qt.DropAction.CopyAction)
46
46
  try:
47
47
  file_path = event.mimeData().urls()[0].toLocalFile()
48
48
  except IndexError:
@@ -59,8 +59,8 @@ class DropZoneWidget(QLabel):
59
59
  else:
60
60
  default_directory = os.path.expanduser('~')
61
61
 
62
- fmu_filename, _ = QFileDialog.getOpenFileName(self, 'Select FMU to Manipulate',
63
- default_directory, "FMU files (*.fmu)")
62
+ fmu_filename, _ = QFileDialog.getOpenFileName(parent=self, caption='Select FMU to Manipulate',
63
+ directory=default_directory, filter="FMU files (*.fmu)")
64
64
  if fmu_filename:
65
65
  self.set_fmu(fmu_filename)
66
66
 
@@ -70,23 +70,23 @@ class DropZoneWidget(QLabel):
70
70
  elif not os.path.isfile(filename):
71
71
  filename = os.path.join(os.path.dirname(__file__), "resources", "fmu.png")
72
72
 
73
- image = QImage(filename).scaled(self.WIDTH-4, self.HEIGHT-4, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
74
- pixmap = QPixmap.fromImage(image)
75
- rounded = self.make_pixmap_rounded(pixmap)
76
- self.setPixmap(rounded)
73
+ base_image = QImage(filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
74
+ Qt.TransformationMode.SmoothTransformation)
75
+ mask_filename = os.path.join(os.path.dirname(__file__), "resources", "mask.png")
76
+ mask_image = QImage(mask_filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
77
+ Qt.TransformationMode.SmoothTransformation)
78
+ rounded_image = QImage(self.WIDTH, self.HEIGHT, QImage.Format.Format_ARGB32)
79
+ rounded_image.fill(QColor(0, 0, 0, 0))
80
+ painter = QPainter()
81
+ painter.begin(rounded_image)
82
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
83
+ painter.drawImage(QPoint(0, 0), base_image)
84
+ painter.drawImage(QPoint(0, 0), mask_image)
85
+ painter.end()
86
+ pixmap = QPixmap.fromImage(rounded_image)
77
87
 
78
- def make_pixmap_rounded(self, pixmap):
79
- rounded = QPixmap(pixmap.size())
80
- rounded.fill(QColor("transparent"))
88
+ self.setPixmap(pixmap)
81
89
 
82
- painter = QPainter(rounded)
83
- painter.setRenderHint(QPainter.Antialiasing)
84
- painter.setBrush(QBrush(pixmap))
85
- painter.setPen(Qt.NoPen)
86
- painter.drawRoundedRect(pixmap.rect(), 20, 20)
87
- del painter # Mandatory to avoid a crash
88
- self.update() # Mandatory to avoid a crash
89
- return rounded
90
90
 
91
91
  def set_fmu(self, filename):
92
92
  try:
@@ -147,7 +147,7 @@ class LogWidget(QTextBrowser):
147
147
  LogWidget.XStream.stdout().messageWritten.connect(self.insertPlainText)
148
148
  LogWidget.XStream.stderr().messageWritten.connect(self.insertPlainText)
149
149
 
150
- def loadResource(self, type, name):
150
+ def loadResource(self, _, name):
151
151
  image_path = os.path.join(os.path.dirname(__file__), "resources", name.toString())
152
152
  return QPixmap(image_path)
153
153
 
@@ -162,7 +162,7 @@ class HelpWidget(QLabel):
162
162
  filename = os.path.join(os.path.dirname(__file__), "resources", "help.png")
163
163
  image = QPixmap(filename)
164
164
  self.setPixmap(image)
165
- self.setAlignment(Qt.AlignRight)
165
+ self.setAlignment(Qt.AlignmentFlag.AlignRight)
166
166
 
167
167
  def mousePressEvent(self, event):
168
168
  QDesktopServices.openUrl(QUrl(self.HELP_URL))
@@ -216,6 +216,9 @@ class FMUManipulationToolboxlMainWindow(QWidget):
216
216
 
217
217
  # set the grid layout
218
218
  self.layout = QGridLayout()
219
+ self.layout.setVerticalSpacing(4)
220
+ self.layout.setHorizontalSpacing(4)
221
+ self.layout.setContentsMargins(10, 10, 10, 10)
219
222
  self.setLayout(self.layout)
220
223
 
221
224
  self.dropped_fmu = DropZoneWidget()
@@ -265,7 +268,7 @@ class FMUManipulationToolboxlMainWindow(QWidget):
265
268
 
266
269
  line += 1
267
270
  self.apply_filter_label = QLabel("Apply modification only on: ")
268
- self.layout.addWidget(self.apply_filter_label, line, 1, 1, 2, alignment=Qt.AlignRight)
271
+ self.layout.addWidget(self.apply_filter_label, line, 1, 1, 2, alignment=Qt.AlignmentFlag.AlignRight)
269
272
  self.set_tooltip(self.apply_filter_label, 'gui-apply-only')
270
273
 
271
274
  causality = ["parameter", "calculatedParameter", "input", "output", "local", "independent"]
@@ -414,7 +417,7 @@ class FMUManipulationToolboxlMainWindow(QWidget):
414
417
 
415
418
  def apply_operation(self, operation):
416
419
  if self.dropped_fmu.fmu:
417
- self.log_widget.moveCursor(QTextCursor.End)
420
+ self.log_widget.moveCursor(QTextCursor.MoveOperation.End)
418
421
  fmu_filename = os.path.basename(self.dropped_fmu.fmu.fmu_filename)
419
422
  print('-' * 100)
420
423
  self.log_widget.insertHtml(f"<strong>{fmu_filename}: {operation}</strong><br>")
@@ -437,8 +440,8 @@ class Application(QApplication):
437
440
  """
438
441
  Analyse and modify your FMUs.
439
442
 
440
- Note: modifying the modelDescription.xml can damage your FMU ! Communicating with the FMU-developer and adapting the
441
- way the FMU is generated, is preferable when possible.
443
+ Note: modifying the modelDescription.xml can damage your FMU !
444
+ Communicating with the FMU-developer and adapting the way the FMU is generated, is preferable when possible.
442
445
 
443
446
  """
444
447
  def __init__(self, *args, **kwargs):
@@ -465,8 +468,8 @@ QPushButton.save:hover {background-color: #675a78; color: #dddddd;}
465
468
  QPushButton.quit {background-color: #4571a4; color: #dddddd;}
466
469
  QPushButton.quit:hover {background-color: #5682b5; color: #dddddd;}
467
470
  QToolTip {color: black}
468
- QLabel.dropped_fmu {background-color: #b5bab9; border: 2px dashed #282830; border-radius: dashed 20px;}
469
- QLabel.dropped_fmu:hover {background-color: #c6cbca; border: 2px dashed #282830; border-radius: dashed 20px;}
471
+ QLabel.dropped_fmu {background-color: #b5bab9}
472
+ QLabel.dropped_fmu:hover {background-color: #c6cbca}
470
473
  QTextBrowser {background-color: #282830; color: #b5bab9;}
471
474
  QMenu {font-size: 18px;}
472
475
  QMenu::item {padding: 2px 250px 2px 20px; border: 1px solid transparent;}
@@ -481,7 +484,6 @@ QMenu::indicator:unchecked:disabled {width: 35px; image: url(images:checkbox-unc
481
484
 
482
485
  self.setStyleSheet(css_dark)
483
486
 
484
-
485
487
  if os.name == 'nt':
486
488
  self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'icon-round.png')))
487
489
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fmu_manipulation_toolbox
3
- Version: 1.8
3
+ Version: 1.8.1
4
4
  Summary: FMU Manipulation Toobox is a python application which help to modify a Functional Mock-up Units (FMUs) without recompilation or to group them into FMU Containers
5
5
  Home-page: https://github.com/grouperenault/fmu_manipulation_toolbox/
6
6
  Author: Nicolas.LAURENT@Renault.com
@@ -1,12 +1,12 @@
1
1
  fmu_manipulation_toolbox/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
2
  fmu_manipulation_toolbox/__main__.py,sha256=mAzrtkil506DS0F3g3CEbHdtZsZotzntcNhIn_lNJkw,344
3
- fmu_manipulation_toolbox/__version__.py,sha256=wgnOv0C5Ic8_OmpPXk8qES2otG4lRxHybApfWS9AX58,7
4
- fmu_manipulation_toolbox/assembly.py,sha256=3YGVBUMjTNwzU-GxUCT-uER3DkX_r4V44ekfzwTmT8I,19364
3
+ fmu_manipulation_toolbox/__version__.py,sha256=UHHZBWWhJrlslwhXubEi_Un0xr55BiMWPTaN6R2jGLM,9
4
+ fmu_manipulation_toolbox/assembly.py,sha256=i0BDfgAca5WgrIYDv1z1mMxaKTiP-tBlPtZJJqMxHgI,20776
5
5
  fmu_manipulation_toolbox/checker.py,sha256=lE2MpH4BAKCDjUvbr06N56u7ao8hWXaJgMKaLvmhFTQ,2272
6
- fmu_manipulation_toolbox/cli.py,sha256=bIfQAGMyTmsx8HQSvu0Ic_UqDRvvm_Op3RCwHpjfqVI,10070
7
- fmu_manipulation_toolbox/fmu_container.py,sha256=cPJ_f0-KrBpF8Ri9D9RhgGIuT9BiqWpus4nPet4a3PU,26488
6
+ fmu_manipulation_toolbox/cli.py,sha256=yAyfpogj_NgGt0mQRFA9dBy0NvN8vFomKUUrAtOIA5c,10102
7
+ fmu_manipulation_toolbox/fmu_container.py,sha256=rbe71nS_kv2QyhcsaW70YlxR3iA_YO2_VX2ihRVxFQs,26338
8
8
  fmu_manipulation_toolbox/fmu_operations.py,sha256=Z3LVOnDvwzoBrqfibZPAn_Osw6MIuGrXtaboGFqp0DA,15836
9
- fmu_manipulation_toolbox/gui.py,sha256=4gD6D4f1jQm1UlVC0iV4nuFNOT-y-AuJ13_7LBVm944,20269
9
+ fmu_manipulation_toolbox/gui.py,sha256=VKhQpIEEilScBSwTSJkDUwSbtsmB_VMuX0QwhZczTq4,20628
10
10
  fmu_manipulation_toolbox/help.py,sha256=aklKiLrsE0adSzQ5uoEB1sBDmI6s4l231gavu4XxxzA,5856
11
11
  fmu_manipulation_toolbox/version.py,sha256=OhBLkZ1-nhC77kyvffPNAf6m8OZe1bYTnNf_PWs1NvM,392
12
12
  fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png,sha256=FWIuyrXlaNLLePHfXj7j9ca5rT8Hgr14KCe1EqTCZyk,2288
@@ -15,13 +15,14 @@ fmu_manipulation_toolbox/resources/checkbox-checked.png,sha256=gzyFqvRFsZixVh6Zl
15
15
  fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png,sha256=KNdiE8zJ8H-mH81spHL8Ck-V87dj-fPPPrPNZFRv3wA,1783
16
16
  fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png,sha256=7XT54vwzDfSK-i6oJ5BBKGXKz8duRRVtoUzaOlWX0WE,1797
17
17
  fmu_manipulation_toolbox/resources/checkbox-unchecked.png,sha256=w3MG3RwFeTwkVOAFi8ZBs6yNlmtVnXxXY5atNyvLw54,1793
18
- fmu_manipulation_toolbox/resources/drop_fmu.png,sha256=aynTWUw5220BXPZIhNco5AkR-ydCAwibWb5gdhR3cCQ,20077
18
+ fmu_manipulation_toolbox/resources/drop_fmu.png,sha256=xt2YzoQVcvr95Ix-4kw6wT04yf660sUHGHkskHRE_l0,10365
19
19
  fmu_manipulation_toolbox/resources/fmu.png,sha256=7bI_cb3pcqEnwBCCL30v3MXPwOK8OtbhFFouRq9lTj8,12048
20
20
  fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png,sha256=hUhuGg88BU5j7KkqhnU981wSpzn9ftQuOSu8pMYuP-Y,37997
21
21
  fmu_manipulation_toolbox/resources/help.png,sha256=WrIbjmlgIqdo6UWYj6L6gG-BCGWlu4qma8HRgRk8k7o,1822
22
22
  fmu_manipulation_toolbox/resources/icon-round.png,sha256=j7jk-9NVQQZJMNazjpj8WeC1q6ptXwTaiO38kPoDEnE,83754
23
23
  fmu_manipulation_toolbox/resources/icon.png,sha256=Ovui-UDBARqdTlVQwTn5Ud8seSsVh9pdElwLq5s6xKg,69976
24
24
  fmu_manipulation_toolbox/resources/license.txt,sha256=5ODuU8g8pIkK-NMWXu_rjZ6k7gM7b-N2rmg87-2Kmqw,1583
25
+ fmu_manipulation_toolbox/resources/mask.png,sha256=px1U4hQGL0AmZ4BQPknOVREpMpTSejbah3ntkpqAzFA,3008
25
26
  fmu_manipulation_toolbox/resources/model.png,sha256=EAf_HnZJe8zYGZygerG1MMt2U-tMMZlifzXPj4_iORA,208788
26
27
  fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd,sha256=OGfyJtaJntKypX5KDpuZ-nV1oYLZ6HV16pkpKOmYox4,2731
27
28
  fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd,sha256=HwyV7LBse-PQSv4z1xjmtzPU3Hjnv4mluq9YdSBNHMQ,3704
@@ -35,14 +36,14 @@ fmu_manipulation_toolbox/resources/linux32/server_sm,sha256=1TLGqNPyM5UVOrCfzNqW
35
36
  fmu_manipulation_toolbox/resources/linux64/client_sm.so,sha256=EhY1XHo1YcQn6yqZ7wk5okqtZyp0MrcCsGcudqE-aIM,37000
36
37
  fmu_manipulation_toolbox/resources/linux64/container.so,sha256=_uhkJYZa_z1tV0NBATWC8iAGt7lPY11_VA29a_5hXsM,45384
37
38
  fmu_manipulation_toolbox/resources/linux64/server_sm,sha256=ulfoPvmaYe9nInYcVEyj7mD9zDzGk56OUoWx1mPKLiE,22768
38
- fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=Xuo3euqlAL1GTEseSN4I3oZgtyazVAtcPIURN1g5MGs,17920
39
- fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=BQVRmQlsRCNaPfvutK2HACHsOhM7eC-X7xYs2pdX2hc,15872
40
- fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=AEijF7jWf5bvI1V73eU4qOH4HF1ji0OZtnicm3dW5P4,22016
41
- fmu_manipulation_toolbox/resources/win64/container.dll,sha256=6yK5n_2ImVvk3cJJ2Xvmps3OrOUJD_Yf0D_F7oRlK_Y,32768
42
- fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=Idy3JQhIkS3gauQVz5iDBgp9hw8nBSMgNExeoDnhJBA,19456
43
- fmu_manipulation_toolbox-1.8.dist-info/LICENSE.txt,sha256=c_862mzyk6ownO3Gt6cVs0-53IXLi_-ZEQFNDVabESw,1285
44
- fmu_manipulation_toolbox-1.8.dist-info/METADATA,sha256=dfoN8Q5lNqwgCThHbYBSuD9UoKl8rhBsts_L8z76vSM,965
45
- fmu_manipulation_toolbox-1.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
46
- fmu_manipulation_toolbox-1.8.dist-info/entry_points.txt,sha256=jCPLMBdS-eOvmRfDv7n0wHZSbJHccHviW03mz5vwO-Q,124
47
- fmu_manipulation_toolbox-1.8.dist-info/top_level.txt,sha256=9D_h-5BMjSqf9z-XFkbJL_bMppR2XNYW3WNuPkXou0k,25
48
- fmu_manipulation_toolbox-1.8.dist-info/RECORD,,
39
+ fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=usY6k86X-Y0De5-SxCvgJ3yRzPpR4KCNTShYzDeec80,17920
40
+ fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=3570x6uDaKUIYBy9uOmID66ffvy-bxVa8Q2il4m5KZc,15872
41
+ fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=G6bNin2QuwbTiToJNSsSM1Dil899MHnZ5FNm-4ExH5I,22016
42
+ fmu_manipulation_toolbox/resources/win64/container.dll,sha256=RBBo9kBBYhYe_KLPl5MtJJCVuL1Zf1LNT87mT88JCPg,32768
43
+ fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=Pcx1z-2E7_iHmAzUDEFNTBCf02gfzg5pdYdso5GbP5A,19456
44
+ fmu_manipulation_toolbox-1.8.1.dist-info/LICENSE.txt,sha256=c_862mzyk6ownO3Gt6cVs0-53IXLi_-ZEQFNDVabESw,1285
45
+ fmu_manipulation_toolbox-1.8.1.dist-info/METADATA,sha256=24_m-PSpY9JZqU4LxMHDN3G1IdGydt_8aSXvDU7vm-8,967
46
+ fmu_manipulation_toolbox-1.8.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
47
+ fmu_manipulation_toolbox-1.8.1.dist-info/entry_points.txt,sha256=jCPLMBdS-eOvmRfDv7n0wHZSbJHccHviW03mz5vwO-Q,124
48
+ fmu_manipulation_toolbox-1.8.1.dist-info/top_level.txt,sha256=9D_h-5BMjSqf9z-XFkbJL_bMppR2XNYW3WNuPkXou0k,25
49
+ fmu_manipulation_toolbox-1.8.1.dist-info/RECORD,,