fmu-manipulation-toolbox 1.7.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. fmu_manipulation_toolbox/__init__.py +1 -0
  2. fmu_manipulation_toolbox/__main__.py +25 -0
  3. fmu_manipulation_toolbox/__version__.py +1 -0
  4. fmu_manipulation_toolbox/checker.py +61 -0
  5. fmu_manipulation_toolbox/cli.py +216 -0
  6. fmu_manipulation_toolbox/fmu_container.py +784 -0
  7. fmu_manipulation_toolbox/fmu_operations.py +489 -0
  8. fmu_manipulation_toolbox/gui.py +493 -0
  9. fmu_manipulation_toolbox/help.py +87 -0
  10. fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png +0 -0
  11. fmu_manipulation_toolbox/resources/checkbox-checked-hover.png +0 -0
  12. fmu_manipulation_toolbox/resources/checkbox-checked.png +0 -0
  13. fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png +0 -0
  14. fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png +0 -0
  15. fmu_manipulation_toolbox/resources/checkbox-unchecked.png +0 -0
  16. fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
  17. fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd +58 -0
  18. fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd +78 -0
  19. fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd +345 -0
  20. fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ScalarVariable.xsd +218 -0
  21. fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Type.xsd +89 -0
  22. fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Unit.xsd +116 -0
  23. fmu_manipulation_toolbox/resources/fmi-2.0/fmi2VariableDependency.xsd +92 -0
  24. fmu_manipulation_toolbox/resources/fmu.png +0 -0
  25. fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
  26. fmu_manipulation_toolbox/resources/help.png +0 -0
  27. fmu_manipulation_toolbox/resources/icon.png +0 -0
  28. fmu_manipulation_toolbox/resources/license.txt +34 -0
  29. fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
  30. fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
  31. fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
  32. fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  33. fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
  34. fmu_manipulation_toolbox/resources/model.png +0 -0
  35. fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  36. fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
  37. fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  38. fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  39. fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  40. fmu_manipulation_toolbox/version.py +9 -0
  41. fmu_manipulation_toolbox-1.7.5.dist-info/LICENSE.txt +22 -0
  42. fmu_manipulation_toolbox-1.7.5.dist-info/METADATA +20 -0
  43. fmu_manipulation_toolbox-1.7.5.dist-info/RECORD +46 -0
  44. fmu_manipulation_toolbox-1.7.5.dist-info/WHEEL +5 -0
  45. fmu_manipulation_toolbox-1.7.5.dist-info/entry_points.txt +3 -0
  46. fmu_manipulation_toolbox-1.7.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,493 @@
1
+ import os.path
2
+ import sys
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
8
+ import textwrap
9
+ from functools import partial
10
+ from typing import Optional
11
+
12
+ from .fmu_operations import *
13
+ from .checker import checker_list
14
+ from .help import Help
15
+
16
+
17
+ class DropZoneWidget(QLabel):
18
+ WIDTH = 150
19
+ HEIGHT = 150
20
+ fmu = None
21
+ last_directory = None
22
+ clicked = pyqtSignal()
23
+
24
+ def __init__(self):
25
+ super().__init__()
26
+ self.setAcceptDrops(True)
27
+ self.set_image(None)
28
+ self.setProperty("class", "dropped_fmu")
29
+ self.setFixedSize(self.WIDTH, self.HEIGHT)
30
+
31
+ def dragEnterEvent(self, event):
32
+ if event.mimeData().hasImage:
33
+ event.accept()
34
+ else:
35
+ event.ignore()
36
+
37
+ def dragMoveEvent(self, event):
38
+ if event.mimeData().hasImage:
39
+ event.accept()
40
+ else:
41
+ event.ignore()
42
+
43
+ def dropEvent(self, event):
44
+ if event.mimeData().hasImage:
45
+ event.setDropAction(Qt.CopyAction)
46
+ try:
47
+ file_path = event.mimeData().urls()[0].toLocalFile()
48
+ except IndexError:
49
+ print("Please select a regular file.")
50
+ return
51
+ self.set_fmu(file_path)
52
+ event.accept()
53
+ else:
54
+ event.ignore()
55
+
56
+ def mousePressEvent(self, event):
57
+ if self.last_directory:
58
+ default_directory = self.last_directory
59
+ else:
60
+ default_directory = os.path.expanduser('~')
61
+
62
+ fmu_filename, _ = QFileDialog.getOpenFileName(self, 'Select FMU to Manipulate',
63
+ default_directory, "FMU files (*.fmu)")
64
+ if fmu_filename:
65
+ self.set_fmu(fmu_filename)
66
+
67
+ def set_image(self, filename=None):
68
+ if not filename:
69
+ filename = os.path.join(os.path.dirname(__file__), "resources", "drop_fmu.png")
70
+ elif not os.path.isfile(filename):
71
+ filename = os.path.join(os.path.dirname(__file__), "resources", "fmu.png")
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)
77
+
78
+ def make_pixmap_rounded(self, pixmap):
79
+ rounded = QPixmap(pixmap.size())
80
+ rounded.fill(QColor("transparent"))
81
+
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
+
91
+ def set_fmu(self, filename):
92
+ try:
93
+ self.last_directory = os.path.dirname(filename)
94
+ self.fmu = FMU(filename)
95
+ self.set_image(os.path.join(self.fmu.tmp_directory, "model.png"))
96
+ except Exception as e:
97
+ print(f"ERROR: Cannot load this FMU: {e}")
98
+ self.set_image(None)
99
+ self.fmu = None
100
+ self.clicked.emit()
101
+
102
+
103
+ class LogWidget(QTextBrowser):
104
+ class XStream(QObject):
105
+ _stdout = None
106
+ _stderr = None
107
+ messageWritten = pyqtSignal(str)
108
+
109
+ def flush(self):
110
+ pass
111
+
112
+ @staticmethod
113
+ def fileno():
114
+ return -1
115
+
116
+ def write(self, msg):
117
+ if not self.signalsBlocked():
118
+ self.messageWritten.emit(msg)
119
+
120
+ @staticmethod
121
+ def stdout():
122
+ if not LogWidget.XStream._stdout:
123
+ LogWidget.XStream._stdout = LogWidget.XStream()
124
+ sys.stdout = LogWidget.XStream._stdout
125
+ return LogWidget.XStream._stdout
126
+
127
+ @staticmethod
128
+ def stderr():
129
+ if not LogWidget.XStream._stderr:
130
+ LogWidget.XStream._stderr = LogWidget.XStream()
131
+ sys.stderr = LogWidget.XStream._stderr
132
+ return LogWidget.XStream._stderr
133
+
134
+ def __init__(self):
135
+ super().__init__()
136
+ if os.name == 'nt':
137
+ font = QFont('Consolas')
138
+ font.setPointSize(10)
139
+ else:
140
+ font = QFont('Courier New')
141
+ font.setPointSize(12)
142
+ self.setFont(font)
143
+ self.setMinimumWidth(800)
144
+ self.setMinimumHeight(480)
145
+
146
+ self.insertHtml('<center><img src="fmu_manipulation_toolbox.png"/></center><br>')
147
+ LogWidget.XStream.stdout().messageWritten.connect(self.insertPlainText)
148
+ LogWidget.XStream.stderr().messageWritten.connect(self.insertPlainText)
149
+
150
+ def loadResource(self, type, name):
151
+ image_path = os.path.join(os.path.dirname(__file__), "resources", name.toString())
152
+ return QPixmap(image_path)
153
+
154
+
155
+ class HelpWidget(QLabel):
156
+ HELP_URL = "https://github.com/grouperenault/fmu_manipulation_toolbox/blob/main/README.md"
157
+
158
+ def __init__(self):
159
+ super().__init__()
160
+ self.setProperty("class", "help")
161
+
162
+ filename = os.path.join(os.path.dirname(__file__), "resources", "help.png")
163
+ image = QPixmap(filename)
164
+ self.setPixmap(image)
165
+ self.setAlignment(Qt.AlignRight)
166
+
167
+ def mousePressEvent(self, event):
168
+ QDesktopServices.openUrl(QUrl(self.HELP_URL))
169
+
170
+
171
+ class FilterWidget(QPushButton):
172
+ def __init__(self, items: Optional[list[str]] = (), parent=None):
173
+ super().__init__(parent)
174
+ self.items_selected = set(items)
175
+ self.nb_items = len(items)
176
+ self.update_filter_text()
177
+ if items:
178
+ menu = QMenu()
179
+ for item in items:
180
+ action = QAction(item, self)
181
+ action.setCheckable(True)
182
+ action.setChecked(True)
183
+ action.triggered.connect(partial(self.toggle_item, action))
184
+ menu.addAction(action)
185
+ self.setMenu(menu)
186
+
187
+ def toggle_item(self, action: QAction):
188
+ if not action.isChecked() and len(self.items_selected) == 1:
189
+ action.setChecked(True)
190
+
191
+ if action.isChecked():
192
+ self.items_selected.add(action.text())
193
+ else:
194
+ self.items_selected.remove(action.text())
195
+
196
+ self.update_filter_text()
197
+
198
+ def update_filter_text(self):
199
+ if len(self.items_selected) == self.nb_items:
200
+ self.setText("All causalities")
201
+ else:
202
+ self.setText(", ".join(sorted(self.items_selected)))
203
+
204
+ def get(self):
205
+ if len(self.items_selected) == self.nb_items:
206
+ return []
207
+ else:
208
+ return sorted(self.items_selected)
209
+
210
+
211
+ class FMUManipulationToolboxlMainWindow(QWidget):
212
+ def __init__(self, app, *args, **kwargs):
213
+ super().__init__(*args, **kwargs)
214
+
215
+ self.setWindowTitle('FMU Manipulation Toolbox')
216
+ self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'icon.png')))
217
+
218
+ # set the grid layout
219
+ self.layout = QGridLayout()
220
+ self.setLayout(self.layout)
221
+
222
+ self.dropped_fmu = DropZoneWidget()
223
+ self.dropped_fmu.clicked.connect(self.update_fmu)
224
+ self.layout.addWidget(self.dropped_fmu, 0, 0, 4, 1)
225
+
226
+ font = QFont('Verdana')
227
+ font.setPointSize(14)
228
+ font.setBold(True)
229
+ self.fmu_title = QLabel()
230
+ self.fmu_title.setFont(font)
231
+ self.layout.addWidget(self.fmu_title, 0, 1, 1, 4)
232
+
233
+ help_widget = HelpWidget()
234
+ self.layout.addWidget(help_widget, 0, 5, 1, 1)
235
+
236
+ # Operations
237
+ self.help = Help()
238
+ operations_list = [
239
+ ("Save port names", '-dump-csv', 'save', OperationSaveNamesToCSV, {"prompt_file": "write"}),
240
+ ("Rename ports from CSV", '-rename-from-csv', 'modify', OperationRenameFromCSV, {"prompt_file": "read"}),
241
+ ("Remove Toplevel", '-remove-toplevel', 'modify', OperationStripTopLevel),
242
+ ("Remove Regexp", '-remove-regexp', 'removal', OperationRemoveRegexp, {"prompt": "regexp"}),
243
+ ("Keep only Regexp", '-keep-only-regexp', 'removal', OperationKeepOnlyRegexp, {"prompt": "regexp"}),
244
+ ("Save description.xml", '-extract-descriptor', 'save', None, {"func": self.save_descriptor}),
245
+ ("Trim Until", '-trim-until', 'modify', OperationTrimUntil, {"prompt": "Prefix"}),
246
+ ("Merge Toplevel", '-merge-toplevel', 'modify', OperationMergeTopLevel),
247
+ ("Remove all", '-remove-all', 'removal', OperationRemoveRegexp, {"arg": ".*"}),
248
+ ("Remove sources", '-remove-sources', 'removal', OperationRemoveSources),
249
+ ("Add Win32 remoting", '-add-remoting-win32', 'info', OperationAddRemotingWin32),
250
+ ("Add Win64 remoting", '-add-remoting-win64', 'info', OperationAddRemotingWin64),
251
+ ("Add Win32 frontend", '-add-frontend-win32', 'info', OperationAddFrontendWin32),
252
+ ("Add Win64 frontend", '-add-frontend-win64', 'info', OperationAddFrontendWin64),
253
+ ("Check", '-check', 'info', checker_list),
254
+ ]
255
+
256
+ width = 5
257
+ line = 1
258
+ for i, operation in enumerate(operations_list):
259
+ col = i % width + 1
260
+ line = int(i / width) + 1
261
+
262
+ if len(operation) < 5:
263
+ self.add_operation(operation[0], operation[1], operation[2], operation[3], line, col)
264
+ else:
265
+ self.add_operation(operation[0], operation[1], operation[2], operation[3], line, col, **operation[4])
266
+
267
+ line += 1
268
+ self.apply_filter_label = QLabel("Apply modification only on: ")
269
+ self.layout.addWidget(self.apply_filter_label, line, 1, 1, 2, alignment=Qt.AlignRight)
270
+ self.set_tooltip(self.apply_filter_label, 'gui-apply-only')
271
+
272
+ causality = ["parameter", "calculatedParameter", "input", "output", "local", "independent"]
273
+ self.filter_list = FilterWidget(items=causality)
274
+ self.layout.addWidget(self.filter_list, line, 3, 1, 3)
275
+ self.filter_list.setProperty("class", "quit")
276
+
277
+ # Text
278
+ line += 1
279
+ self.log_widget = LogWidget()
280
+ self.layout.addWidget(self.log_widget, line, 0, 1, width + 1)
281
+
282
+ # buttons
283
+ line += 1
284
+
285
+ reload_button = QPushButton('Reload')
286
+ self.layout.addWidget(reload_button, 4, 0, 1, 1)
287
+ reload_button.clicked.connect(self.reload_fmu)
288
+ reload_button.setProperty("class", "quit")
289
+
290
+ exit_button = QPushButton('Exit')
291
+ self.layout.addWidget(exit_button, line, 0, 1, 2)
292
+ exit_button.clicked.connect(app.exit)
293
+ exit_button.setProperty("class", "quit")
294
+
295
+ save_log_button = QPushButton('Save log as')
296
+ self.layout.addWidget(save_log_button, line, 2, 1, 2)
297
+ save_log_button.clicked.connect(self.save_log)
298
+ save_log_button.setProperty("class", "save")
299
+
300
+ save_fmu_button = QPushButton('Save modified FMU as')
301
+ self.layout.addWidget(save_fmu_button, line, 4, 1, 2)
302
+ save_fmu_button.clicked.connect(self.save_fmu)
303
+ save_fmu_button.setProperty("class", "save")
304
+ self.set_tooltip(save_fmu_button, '-output')
305
+
306
+ # show the window
307
+ self.show()
308
+
309
+ def set_tooltip(self, widget, usage):
310
+ widget.setToolTip("\n".join(textwrap.wrap(self.help.usage(usage))))
311
+
312
+ def reload_fmu(self):
313
+ if self.dropped_fmu.fmu:
314
+ filename = self.dropped_fmu.fmu.fmu_filename
315
+ self.dropped_fmu.fmu = None
316
+ self.dropped_fmu.set_fmu(filename)
317
+
318
+ def save_descriptor(self):
319
+ if self.dropped_fmu.fmu:
320
+ fmu = self.dropped_fmu.fmu
321
+ filename, ok = QFileDialog.getSaveFileName(self, "Select a file",
322
+ os.path.dirname(fmu.fmu_filename),
323
+ "XML files (*.xml)")
324
+ if ok and filename:
325
+ fmu.save_descriptor(filename)
326
+
327
+ def save_fmu(self):
328
+ if self.dropped_fmu.fmu:
329
+ fmu = self.dropped_fmu.fmu
330
+ filename, ok = QFileDialog.getSaveFileName(self, "Select a file",
331
+ os.path.dirname(fmu.fmu_filename),
332
+ "FMU files (*.fmu)")
333
+ if ok and filename:
334
+ fmu.repack(filename)
335
+ print(f"Modified version saved as {filename}.")
336
+
337
+ def save_log(self):
338
+ if self.dropped_fmu.fmu:
339
+ default_dir = os.path.dirname(self.dropped_fmu.fmu.fmu_filename)
340
+ else:
341
+ default_dir = None
342
+ filename, ok = QFileDialog.getSaveFileName(self, "Select a file",
343
+ default_dir,
344
+ "TXT files (*.txt)")
345
+ if ok and filename:
346
+ try:
347
+ with open(filename, "wt") as file:
348
+ file.write(str(self.log_widget.toPlainText()))
349
+ except Exception as e:
350
+ print(f"ERROR: {e}")
351
+
352
+ def add_operation(self, name, usage, severity, operation, x, y, prompt=None, prompt_file=None, arg=None,
353
+ func=None):
354
+ if prompt:
355
+ def operation_handler():
356
+ local_arg = self.prompt_string(prompt)
357
+ if local_arg:
358
+ self.apply_operation(operation(local_arg))
359
+ elif prompt_file:
360
+ def operation_handler():
361
+ local_arg = self.prompt_file(prompt_file)
362
+ if local_arg:
363
+ self.apply_operation(operation(local_arg))
364
+ elif arg:
365
+ def operation_handler():
366
+ self.apply_operation(operation(arg))
367
+ else:
368
+ def operation_handler():
369
+ # Checker can be a list of operations!
370
+ if isinstance(operation, list):
371
+ for op in operation:
372
+ self.apply_operation(op())
373
+ else:
374
+ self.apply_operation(operation())
375
+
376
+ button = QPushButton(name)
377
+ self.set_tooltip(button, usage)
378
+ button.setProperty("class", severity)
379
+ if func:
380
+ button.clicked.connect(func)
381
+ else:
382
+ button.clicked.connect(operation_handler)
383
+ self.layout.addWidget(button, x, y)
384
+
385
+ def prompt_string(self, message):
386
+ text, ok = QInputDialog().getText(self, "Enter value", f"{message}:", QLineEdit.Normal, "")
387
+
388
+ if ok and text:
389
+ return text
390
+ else:
391
+ return None
392
+
393
+ def prompt_file(self, access):
394
+ if self.dropped_fmu.fmu:
395
+ default_dir = os.path.dirname(self.dropped_fmu.fmu.fmu_filename)
396
+
397
+ if access == 'read':
398
+ filename, ok = QFileDialog.getOpenFileName(self, "Select a file",
399
+ default_dir, "CSV files (*.csv)")
400
+ else:
401
+ filename, ok = QFileDialog.getSaveFileName(self, "Select a file",
402
+ default_dir, "CSV files (*.csv)")
403
+
404
+ if ok and filename:
405
+ return filename
406
+ return None
407
+
408
+ def update_fmu(self):
409
+ if self.dropped_fmu.fmu:
410
+ self.fmu_title.setText(os.path.basename(self.dropped_fmu.fmu.fmu_filename))
411
+ self.log_widget.clear()
412
+ self.apply_operation(OperationSummary())
413
+ else:
414
+ self.fmu_title.setText('')
415
+
416
+ def apply_operation(self, operation):
417
+ if self.dropped_fmu.fmu:
418
+ self.log_widget.moveCursor(QTextCursor.End)
419
+ fmu_filename = os.path.basename(self.dropped_fmu.fmu.fmu_filename)
420
+ print('-' * 100)
421
+ self.log_widget.insertHtml(f"<strong>{fmu_filename}: {operation}</strong><br>")
422
+
423
+ apply_on = self.filter_list.get()
424
+ if apply_on:
425
+ self.log_widget.insertHtml(f"<i>Applied only for ports with causality = " +
426
+ ", ".join(apply_on) + "</i><br>")
427
+ print('-' * 100)
428
+ try:
429
+ self.dropped_fmu.fmu.apply_operation(operation, apply_on=apply_on)
430
+ except Exception as e:
431
+ print(f"ERROR: {e}")
432
+
433
+ scroll_bar = self.log_widget.verticalScrollBar()
434
+ scroll_bar.setValue(scroll_bar.maximum())
435
+
436
+
437
+ class Application:
438
+ """
439
+ Analyse and modify your FMUs.
440
+
441
+ Note: modifying the modelDescription.xml can damage your FMU ! Communicating with the FMU-developer and adapting the
442
+ way the FMU is generated, is preferable when possible.
443
+
444
+ """
445
+ def __init__(self):
446
+ QDir.addSearchPath('images', os.path.join(os.path.dirname(__file__), "resources"))
447
+ self.app = QApplication(sys.argv)
448
+ font = QFont("Verdana")
449
+ font.setPointSize(10)
450
+ self.app.setFont(font)
451
+ css_dark = """
452
+ QWidget {background: #4b4e51; color: #b5bab9}
453
+ QPushButton { min-height: 30px; padding: 1px 1px 0.2em 0.2em; border: 1px solid #282830; border-radius: 5px;}
454
+ QComboBox { min-height: 30px; padding: 1px 1px 0.2em 0.2em; border: 1px solid #282830; border-radius: 5px;}
455
+ QPushButton:pressed { border: 2px solid #282830; }
456
+ QPushButton.info {background-color: #4e6749; color: #dddddd;}
457
+ QPushButton.info:hover {background-color: #5f7850; color: #dddddd;}
458
+ QPushButton.info:hover {background-color: #5f7850; color: #dddddd;}
459
+ QPushButton.modify {background-color: #98763f; color: #dddddd;}
460
+ QPushButton.modify:hover {background-color: #a9874f; color: #dddddd;}
461
+ QPushButton.removal {background-color: #692e2e; color: #dddddd;}
462
+ QPushButton.removal:hover{background-color: #7a3f3f; color: #dddddd;}
463
+ QPushButton.save {background-color: #564967; color: #dddddd;}
464
+ QPushButton.save:hover {background-color: #675a78; color: #dddddd;}
465
+ QPushButton.quit {background-color: #4571a4; color: #dddddd;}
466
+ QPushButton.quit:hover {background-color: #5682b5; color: #dddddd;}
467
+ 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;}
470
+ QTextBrowser {background-color: #282830; color: #b5bab9;}
471
+ QMenu {font-size: 18px;}
472
+ QMenu::item {padding: 2px 250px 2px 20px; border: 1px solid transparent;}
473
+ QMenu::item::indicator {width: 32px; height: 32px; }
474
+ QMenu::indicator:checked {image: url(images:checkbox-checked.png);}
475
+ QMenu::indicator:checked:hover {image: url(images:checkbox-checked-hover.png);}
476
+ QMenu::indicator:checked:disabled {image: url(images:checkbox-checked-disabled.png);}
477
+ QMenu::indicator:unchecked {IconWidth: 50px; image: url(images:checkbox-unchecked.png); }
478
+ QMenu::indicator:unchecked:hover {width: 35px; image: url(images:checkbox-unchecked-hover.png); }
479
+ QMenu::indicator:unchecked:disabled {width: 35px; image: url(images:checkbox-unchecked-disabled.png); }
480
+ """
481
+
482
+ self.app.setStyleSheet(css_dark)
483
+ self.window = FMUManipulationToolboxlMainWindow(self.app)
484
+ print(" "*80, f"Version {version}")
485
+ print(self.__doc__)
486
+ sys.exit(self.app.exec())
487
+
488
+ def exit(self):
489
+ self.app.exit()
490
+
491
+
492
+ def main():
493
+ Application()
@@ -0,0 +1,87 @@
1
+ class Help:
2
+ _usage = {
3
+ '-h': "display help.",
4
+ '-input': "this option is mandatory to specify the filename of the FMU to be loaded.",
5
+
6
+ '-output': "this option is used to specify the filename of the FMU to be created after manipulations."
7
+ " If it is not provided, no new fmu will be saved and some manipulations can be lost.",
8
+
9
+ '-remove-toplevel': "rename the ports of the input fmu by striping all characters until the first '.' "
10
+ "(toplevel bus). If no '.' is present, the port won't be renamed. Resulting fmu should be "
11
+ "saved by using -output option. Note: before version 1.2.6, this option was spelled "
12
+ "-remove-toplel.",
13
+
14
+ '-merge-toplevel': "replace first '.' by an '_' on every port name.",
15
+
16
+ '-trim-until': "remove a prefix from port name. Example '-trim-until _' : will rename port names of the"
17
+ " FMU by removing part of the name until the first '_'. Prefix can be longer than a "
18
+ "single character. ",
19
+
20
+ '-remove-regexp': "remove ports that match the regular-expression. Other ports will be kept. Resulting "
21
+ "fmu should be saved by using -output option. This option is available from version 1.1. "
22
+ "See https://en.wikipedia.org/wiki/Regular_expression to have more detail of expected "
23
+ "format.",
24
+
25
+ '-keep-only-regexp': "keep only ports that match the regular-expression. Other ports will be removed. "
26
+ "Resulting fmu should be saved by using -output option. This option is available from "
27
+ "version 1.1. See https://en.wikipedia.org/wiki/Regular_expression to have more detail "
28
+ "of expected format.",
29
+
30
+ '-remove-all': "equivalent to '-remove-regexp .*'. Typical use case is to use it with -only-* options. "
31
+ "Example: in order ro suppress all parameters of FMU: -only-parameters -remove-all",
32
+
33
+ '-dump-csv': "list all names of the ports of the input fmu and store them inside path/to/list.csv. "
34
+ "This file is ';' separated. It contains two columns in order to be easily reused by "
35
+ "-rename-from-csv option.",
36
+
37
+ '-rename-from-csv': "rename the ports of fmu accordingly to path/to/translation.csv. This file is ';' "
38
+ "separated. It contains two columns. First column contains original names. Second column "
39
+ "contains new names. * If a port is not found in the file, it won't be renamed. This is "
40
+ "working with version > 1.2.6. It is safer to keep ALL port in csv. * If the new name is"
41
+ " empty, the port will be removed. This is working starting version 1.1. * If a name in "
42
+ "the file is not present in input FMU, it will be ignored. (no warning will be issued). "
43
+ "Resulting fmu should be saved by using -output option.",
44
+
45
+ '-add-remoting-win32': "this option is windows specific. It will add 'win32' interface to a 'win64' fmu."
46
+ " Please upgrade to version 1.2.1 before using this option. Resulting fmu should be"
47
+ " saved by using -output option.",
48
+
49
+ '-add-remoting-win64': "this option is windows specific. It will add 'win64' interface to a 'win32' fmu."
50
+ " Please upgrade to version 1.2.1 before using this option. Resulting fmu should be"
51
+ " saved by using -output option.",
52
+
53
+ '-add-frontend-win32': "this option is windows specific. It can be used with 'win32' fmu. At simulation time, "
54
+ "the FMU will spawn a dedicated process tu run the model. This option is available from "
55
+ "version 1.4. Resulting fmu should be saved by using -output option.",
56
+
57
+ '-add-frontend-win64': "this option is windows specific. It can be used with 'win64' fmu. At simulation time, "
58
+ "the FMU will spawn a dedicated process tu run the model. This option is available from "
59
+ "version 1.4. Resulting fmu should be saved by using -output option.",
60
+
61
+ '-extract-descriptor': "save the modelDescription.xml into the specified location. If modification options "
62
+ "(like -rename-from-csv or -remove-toplevel are set), the saved file will contain "
63
+ "modification. This option is available from version 1.1.",
64
+
65
+ '-remove-sources': "Remove sources folder from the FMU. This option is available from version 1.3.",
66
+
67
+ '-only-parameters': "apply operation only on ports with causality = 'parameter'. This "
68
+ "option is available from version 1.3.",
69
+
70
+ '-only-inputs': "apply operation only on ports with causality = 'parameter'. This "
71
+ "option is available from version 1.3.",
72
+
73
+ '-only-outputs': "apply operation only on ports with causality = 'output'. This "
74
+ "option is available from version 1.3.",
75
+
76
+ '-summary': "display useful information regarding the FMU.",
77
+
78
+ '-check': "performs some check of FMU and display Errors or Warnings. This is useful to avoid later "
79
+ "issues when using the FMU.",
80
+
81
+ # GUI message
82
+ "gui-apply-only": "Apply operation only on ports with specified causality. If selected, at least one causality "
83
+ "should be selected."
84
+ }
85
+
86
+ def usage(self, option):
87
+ return self._usage[option]
@@ -0,0 +1,58 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
3
+ <xs:annotation>
4
+ <xs:documentation>
5
+ Copyright(c) 2008-2011 MODELISAR consortium,
6
+ 2012-2022 Modelica Association Project "FMI".
7
+ All rights reserved.
8
+
9
+ This schema file is part of the FMI 2.0.4 standard.
10
+
11
+ This file is licensed by the copyright holders under the 2-Clause BSD License
12
+ (https://opensource.org/licenses/BSD-2-Clause):
13
+
14
+ ----------------------------------------------------------------------------
15
+ Redistribution and use in source and binary forms, with or without
16
+ modification, are permitted provided that the following conditions are met:
17
+
18
+ - Redistributions of source code must retain the above copyright notice,
19
+ this list of conditions and the following disclaimer.
20
+
21
+ - Redistributions in binary form must reproduce the above copyright notice,
22
+ this list of conditions and the following disclaimer in the documentation
23
+ and/or other materials provided with the distribution.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
29
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
32
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ ----------------------------------------------------------------------------
37
+ </xs:documentation>
38
+ </xs:annotation>
39
+ <xs:complexType name="fmi2Annotation">
40
+ <xs:sequence maxOccurs="unbounded">
41
+ <xs:element name="Tool">
42
+ <xs:annotation>
43
+ <xs:documentation>Tool specific annotation (ignored by other tools).</xs:documentation>
44
+ </xs:annotation>
45
+ <xs:complexType>
46
+ <xs:sequence>
47
+ <xs:any namespace="##any" processContents="lax" minOccurs="0"/>
48
+ </xs:sequence>
49
+ <xs:attribute name="name" type="xs:normalizedString" use="required">
50
+ <xs:annotation>
51
+ <xs:documentation>Name of tool that can interpret the annotation. "name" must be unique with respect to all other elements of the VendorAnnotation list.</xs:documentation>
52
+ </xs:annotation>
53
+ </xs:attribute>
54
+ </xs:complexType>
55
+ </xs:element>
56
+ </xs:sequence>
57
+ </xs:complexType>
58
+ </xs:schema>