fmu-manipulation-toolbox 1.8__py3-none-any.whl → 1.8.2.dev2__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 (25) hide show
  1. fmu_manipulation_toolbox/__version__.py +1 -1
  2. fmu_manipulation_toolbox/assembly.py +233 -126
  3. fmu_manipulation_toolbox/cli.py +7 -4
  4. fmu_manipulation_toolbox/fmu_container.py +60 -27
  5. fmu_manipulation_toolbox/fmu_operations.py +2 -3
  6. fmu_manipulation_toolbox/gui.py +356 -103
  7. fmu_manipulation_toolbox/gui_style.py +129 -0
  8. fmu_manipulation_toolbox/resources/container.png +0 -0
  9. fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
  10. fmu_manipulation_toolbox/resources/fmu.png +0 -0
  11. fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
  12. fmu_manipulation_toolbox/resources/icon_fmu.png +0 -0
  13. fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  14. fmu_manipulation_toolbox/resources/mask.png +0 -0
  15. fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  16. fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
  17. fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  18. fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  19. fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  20. {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/METADATA +8 -3
  21. {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/RECORD +25 -21
  22. {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/WHEEL +1 -1
  23. {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/LICENSE.txt +0 -0
  24. {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/entry_points.txt +0 -0
  25. {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,22 @@
1
1
  import os.path
2
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
3
  import textwrap
4
+
5
+ from PySide6.QtCore import Qt, QObject, QUrl, QDir, Signal, QPoint, QModelIndex
6
+ from PySide6.QtWidgets import (QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QPushButton, QFileDialog,
7
+ QTextBrowser, QInputDialog, QMenu, QTreeView, QAbstractItemView, QTabWidget, QTableView,
8
+ QCheckBox)
9
+ from PySide6.QtGui import (QPixmap, QTextCursor, QStandardItem, QIcon, QDesktopServices, QAction,
10
+ QPainter, QColor, QImage, QStandardItemModel)
9
11
  from functools import partial
10
12
  from typing import Optional
11
13
 
14
+ from .gui_style import *
12
15
  from .fmu_operations import *
16
+ from .assembly import Assembly, AssemblyNode
13
17
  from .checker import checker_list
14
18
  from .help import Help
19
+ from .version import __version__ as version
15
20
 
16
21
 
17
22
  class DropZoneWidget(QLabel):
@@ -19,30 +24,30 @@ class DropZoneWidget(QLabel):
19
24
  HEIGHT = 150
20
25
  fmu = None
21
26
  last_directory = None
22
- clicked = pyqtSignal()
27
+ clicked = Signal()
23
28
 
24
- def __init__(self):
25
- super().__init__()
29
+ def __init__(self, parent=None):
30
+ super().__init__(parent)
26
31
  self.setAcceptDrops(True)
27
32
  self.set_image(None)
28
33
  self.setProperty("class", "dropped_fmu")
29
34
  self.setFixedSize(self.WIDTH, self.HEIGHT)
30
35
 
31
36
  def dragEnterEvent(self, event):
32
- if event.mimeData().hasImage:
37
+ if event.mimeData().hasUrls():
33
38
  event.accept()
34
39
  else:
35
40
  event.ignore()
36
41
 
37
42
  def dragMoveEvent(self, event):
38
- if event.mimeData().hasImage:
43
+ if event.mimeData().hasUrls():
39
44
  event.accept()
40
45
  else:
41
46
  event.ignore()
42
47
 
43
48
  def dropEvent(self, event):
44
- if event.mimeData().hasImage:
45
- event.setDropAction(Qt.CopyAction)
49
+ if event.mimeData().hasUrls():
50
+ event.setDropAction(Qt.DropAction.CopyAction)
46
51
  try:
47
52
  file_path = event.mimeData().urls()[0].toLocalFile()
48
53
  except IndexError:
@@ -59,8 +64,8 @@ class DropZoneWidget(QLabel):
59
64
  else:
60
65
  default_directory = os.path.expanduser('~')
61
66
 
62
- fmu_filename, _ = QFileDialog.getOpenFileName(self, 'Select FMU to Manipulate',
63
- default_directory, "FMU files (*.fmu)")
67
+ fmu_filename, _ = QFileDialog.getOpenFileName(parent=self, caption='Select FMU to Manipulate',
68
+ dir=default_directory, filter="FMU files (*.fmu)")
64
69
  if fmu_filename:
65
70
  self.set_fmu(fmu_filename)
66
71
 
@@ -70,25 +75,24 @@ class DropZoneWidget(QLabel):
70
75
  elif not os.path.isfile(filename):
71
76
  filename = os.path.join(os.path.dirname(__file__), "resources", "fmu.png")
72
77
 
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):
78
+ base_image = QImage(filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
79
+ Qt.TransformationMode.SmoothTransformation)
80
+ mask_filename = os.path.join(os.path.dirname(__file__), "resources", "mask.png")
81
+ mask_image = QImage(mask_filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
82
+ Qt.TransformationMode.SmoothTransformation)
83
+ rounded_image = QImage(self.WIDTH, self.HEIGHT, QImage.Format.Format_ARGB32)
84
+ rounded_image.fill(QColor(0, 0, 0, 0))
85
+ painter = QPainter()
86
+ painter.begin(rounded_image)
87
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
88
+ painter.drawImage(QPoint(0, 0), base_image)
89
+ painter.drawImage(QPoint(0, 0), mask_image)
90
+ painter.end()
91
+ pixmap = QPixmap.fromImage(rounded_image)
92
+
93
+ self.setPixmap(pixmap)
94
+
95
+ def set_fmu(self, filename: str):
92
96
  try:
93
97
  self.last_directory = os.path.dirname(filename)
94
98
  self.fmu = FMU(filename)
@@ -104,7 +108,10 @@ class LogWidget(QTextBrowser):
104
108
  class XStream(QObject):
105
109
  _stdout = None
106
110
  _stderr = None
107
- messageWritten = pyqtSignal(str)
111
+ messageWritten = Signal(str)
112
+
113
+ def __init__(self):
114
+ super().__init__()
108
115
 
109
116
  def flush(self):
110
117
  pass
@@ -133,21 +140,14 @@ class LogWidget(QTextBrowser):
133
140
 
134
141
  def __init__(self):
135
142
  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>')
143
+ self.setMinimumWidth(900)
144
+ self.setMinimumHeight(500)
145
+ self.setSearchPaths([os.path.join(os.path.dirname(__file__), "resources")])
146
+ self.insertHtml('<center><img src="fmu_manipulation_toolbox.png"/></center><br/>')
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))
@@ -175,14 +175,14 @@ class FilterWidget(QPushButton):
175
175
  self.nb_items = len(items)
176
176
  self.update_filter_text()
177
177
  if items:
178
- menu = QMenu()
178
+ self.menu = QMenu()
179
179
  for item in items:
180
180
  action = QAction(item, self)
181
181
  action.setCheckable(True)
182
182
  action.setChecked(True)
183
183
  action.triggered.connect(partial(self.toggle_item, action))
184
- menu.addAction(action)
185
- self.setMenu(menu)
184
+ self.menu.addAction(action)
185
+ self.setMenu(self.menu)
186
186
 
187
187
  def toggle_item(self, action: QAction):
188
188
  if not action.isChecked() and len(self.items_selected) == 1:
@@ -208,27 +208,201 @@ class FilterWidget(QPushButton):
208
208
  return sorted(self.items_selected)
209
209
 
210
210
 
211
- class FMUManipulationToolboxlMainWindow(QWidget):
212
- def __init__(self, *args, **kwargs):
213
- super().__init__(*args, **kwargs)
211
+ class AssemblyTreeWidget(QTreeView):
212
+ class AssemblyTreeModel(QStandardItemModel):
213
+
214
+ def __init__(self, assembly: Assembly, parent=None):
215
+ super().__init__(parent)
216
+
217
+ self.lastDroppedItems = []
218
+ self.pendingRemoveRowsAfterDrop = False
219
+ self.setHorizontalHeaderLabels(['col1'])
220
+ self.dnd_target_node: Optional[AssemblyNode] = None
221
+
222
+ self.icon_container = QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'container.png'))
223
+ self.icon_fmu = QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'icon_fmu.png'))
224
+
225
+ if assembly:
226
+ self.add_node(assembly.root, self)
227
+
228
+ def add_node(self, node: AssemblyNode, parent_item):
229
+ # Add Container
230
+ item = QStandardItem(self.icon_container, node.name)
231
+ parent_item.appendRow(item)
232
+ item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsDragEnabled |
233
+ Qt.ItemFlag.ItemIsDropEnabled)
234
+ item.setData(node, role=Qt.ItemDataRole.UserRole + 1)
235
+ item.setData("container", role=Qt.ItemDataRole.UserRole + 2)
236
+
237
+ # Add FMU's
238
+ children_name = node.children.keys()
239
+ for fmu_name in node.fmu_names_list:
240
+ if fmu_name not in children_name:
241
+ fmu_node = QStandardItem(self.icon_fmu, fmu_name)
242
+ fmu_node.setData(node, role=Qt.ItemDataRole.UserRole + 1)
243
+ fmu_node.setData("fmu", role=Qt.ItemDataRole.UserRole + 2)
244
+ fmu_node.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable |
245
+ Qt.ItemFlag.ItemIsDragEnabled)
246
+ item.appendRow(fmu_node)
247
+
248
+ # Add Sub-Containers
249
+ for child in node.children.values():
250
+ self.add_node(child, item)
251
+
252
+ def insertRows(self, row, count, parent=QModelIndex()):
253
+ self.dnd_target_node = parent.data(role=Qt.ItemDataRole.UserRole + 1)
254
+ return super().insertRows(row, count, parent=parent)
255
+
256
+ def removeRows(self, row, count, parent=QModelIndex()):
257
+ if not self.dnd_target_node:
258
+ print("NO DROP NODE!?")
259
+
260
+ source_index = self.itemFromIndex(parent).child(row, 0).data(role=Qt.ItemDataRole.UserRole+1)
261
+ print(f"{source_index} ==> {self.dnd_target_node.name}")
262
+
263
+ self.dnd_target_node = None
264
+ return super().removeRows(row, count, parent)
265
+
266
+ def dropMimeData(self, data, action, row, column, parent: QModelIndex):
267
+ if parent.column() < 0: # Avoid to drop item as a sibling of the root.
268
+ return False
269
+ return super().dropMimeData(data, action, row, column, parent)
270
+
271
+ def __init__(self, parent=None):
272
+ super().__init__(parent)
273
+
274
+ self.model = self.AssemblyTreeModel(None)
275
+ self.setModel(self.model)
276
+
277
+ self.expandAll()
278
+ self.setDropIndicatorShown(True)
279
+ self.setDragDropOverwriteMode(False)
280
+ self.setAcceptDrops(True)
281
+ self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
282
+ self.setRootIsDecorated(True)
283
+ self.setHeaderHidden(True)
284
+
285
+ def load_container(self, filename):
286
+ assembly = Assembly(filename)
287
+ self.model = self.AssemblyTreeModel(assembly)
288
+ self.setModel(self.model)
289
+
290
+ def setTopIndex(self):
291
+ topIndex = self.model.index(0, 0, self.rootIndex())
292
+ print(topIndex.isValid(), topIndex.model())
293
+ if topIndex.isValid():
294
+ self.setCurrentIndex(topIndex)
295
+ if self.layoutCheck:
296
+ self.model.layoutChanged.disconnect(self.setTopIndex)
297
+ else:
298
+ if not self.layoutCheck:
299
+ self.model.layoutChanged.connect(self.setTopIndex)
300
+ self.layoutCheck = True
301
+
302
+
303
+ def dragEnterEvent2(self, event):
304
+ if event.mimeData().hasImage:
305
+ event.accept()
306
+ else:
307
+ event.ignore()
308
+
309
+ def dragMoveEvent2(self, event):
310
+ if event.mimeData().hasImage:
311
+ event.accept()
312
+ else:
313
+ event.ignore()
314
+
315
+ def dropEvent2(self, event):
316
+ if event.mimeData().hasImage:
317
+ event.setDropAction(Qt.DropAction.CopyAction)
318
+ try:
319
+ file_path = event.mimeData().urls()[0].toLocalFile()
320
+ except IndexError:
321
+ print("Please select a regular file.")
322
+ return
323
+ print(f"DROP: {file_path}")
324
+ event.accept()
325
+ else:
326
+ event.ignore()
214
327
 
215
- self.setWindowTitle('FMU Manipulation Toolbox')
216
328
 
217
- # set the grid layout
329
+ class AssemblPropertiesWidget(QWidget):
330
+ def __init__(self, parent=None):
331
+ super().__init__(parent)
332
+
218
333
  self.layout = QGridLayout()
334
+ self.layout.setVerticalSpacing(4)
335
+ self.layout.setHorizontalSpacing(4)
336
+ self.layout.setContentsMargins(10, 10, 10, 10)
219
337
  self.setLayout(self.layout)
220
338
 
339
+ mt_check = QCheckBox("Multi-Threaded", self)
340
+ self.layout.addWidget(mt_check, 1, 0)
341
+
342
+ profiling_check = QCheckBox("Profiling", self)
343
+ self.layout.addWidget(profiling_check, 1, 1)
344
+
345
+ auto_inputs_check = QCheckBox("Auto Inputs", self)
346
+ self.layout.addWidget(auto_inputs_check, 0, 0)
347
+
348
+ auto_outputs_check = QCheckBox("Auto Outputs", self)
349
+ self.layout.addWidget(auto_outputs_check, 0, 1)
350
+
351
+ auto_links_check = QCheckBox("Auto Links", self)
352
+ self.layout.addWidget(auto_links_check, 0, 2)
353
+
354
+
355
+ class AssemblyTabWidget(QTabWidget):
356
+ def __init__(self, parent=None):
357
+ super().__init__(parent)
358
+
359
+ table = AssemblPropertiesWidget(parent=self)
360
+ self.addTab(table, "Properties")
361
+ table = QTableView()
362
+ self.addTab(table, "Links")
363
+ table = QTableView()
364
+ self.addTab(table, "Inputs")
365
+ table = QTableView()
366
+ self.addTab(table, "Outputs")
367
+ table = QTableView()
368
+ self.addTab(table, "Start values")
369
+
370
+ self.tabBar().setDocumentMode(True)
371
+ self.tabBar().setExpanding(True)
372
+
373
+
374
+ class WindowWithLayout(QWidget):
375
+ def __init__(self, title: str):
376
+ super().__init__(None) # Do not set parent to have a separated window
377
+ self.setWindowTitle(title)
378
+
379
+ self.layout = QGridLayout()
380
+ self.layout.setVerticalSpacing(4)
381
+ self.layout.setHorizontalSpacing(4)
382
+ self.layout.setContentsMargins(10, 10, 10, 10)
383
+ self.setLayout(self.layout)
384
+
385
+
386
+ class MainWindow(WindowWithLayout):
387
+ def __init__(self):
388
+ super().__init__('FMU Manipulation Toolbox')
389
+
221
390
  self.dropped_fmu = DropZoneWidget()
222
391
  self.dropped_fmu.clicked.connect(self.update_fmu)
392
+
223
393
  self.layout.addWidget(self.dropped_fmu, 0, 0, 4, 1)
224
394
 
225
- font = QFont('Verdana')
226
- font.setPointSize(14)
227
- font.setBold(True)
228
395
  self.fmu_title = QLabel()
229
- self.fmu_title.setFont(font)
396
+ self.fmu_title.setProperty("class", "title")
230
397
  self.layout.addWidget(self.fmu_title, 0, 1, 1, 4)
231
398
 
399
+ self.container_window = None
400
+ #TODO: Container Window
401
+ #container_button = QPushButton("Make a container")
402
+ #container_button.setProperty("class", "quit")
403
+ #container_button.clicked.connect(self.launch_container)
404
+ #self.layout.addWidget(container_button, 4, 1, 1, 1)
405
+
232
406
  help_widget = HelpWidget()
233
407
  self.layout.addWidget(help_widget, 0, 5, 1, 1)
234
408
 
@@ -264,8 +438,8 @@ class FMUManipulationToolboxlMainWindow(QWidget):
264
438
  self.add_operation(operation[0], operation[1], operation[2], operation[3], line, col, **operation[4])
265
439
 
266
440
  line += 1
267
- self.apply_filter_label = QLabel("Apply modification only on: ")
268
- self.layout.addWidget(self.apply_filter_label, line, 1, 1, 2, alignment=Qt.AlignRight)
441
+ self.apply_filter_label = QLabel("Apply only on: ")
442
+ self.layout.addWidget(self.apply_filter_label, line, 2, 1, 1, alignment=Qt.AlignmentFlag.AlignRight)
269
443
  self.set_tooltip(self.apply_filter_label, 'gui-apply-only')
270
444
 
271
445
  causality = ["parameter", "calculatedParameter", "input", "output", "local", "independent"]
@@ -305,6 +479,19 @@ class FMUManipulationToolboxlMainWindow(QWidget):
305
479
  # show the window
306
480
  self.show()
307
481
 
482
+ def closeEvent(self, event):
483
+ if self.container_window:
484
+ self.container_window.close()
485
+ self.container_window = None
486
+ event.accept()
487
+
488
+ def launch_container(self):
489
+ if not self.container_window:
490
+ self.container_window = ContainerWindow(self)
491
+
492
+ def closing_container(self):
493
+ self.container_window = None
494
+
308
495
  def set_tooltip(self, widget, usage):
309
496
  widget.setToolTip("\n".join(textwrap.wrap(self.help.usage(usage))))
310
497
 
@@ -382,7 +569,7 @@ class FMUManipulationToolboxlMainWindow(QWidget):
382
569
  self.layout.addWidget(button, x, y)
383
570
 
384
571
  def prompt_string(self, message):
385
- text, ok = QInputDialog().getText(self, "Enter value", f"{message}:", QLineEdit.Normal, "")
572
+ text, ok = QInputDialog().getText(self, "Enter value", f"{message}:", QLineEdit.EchoMode.Normal, "")
386
573
 
387
574
  if ok and text:
388
575
  return text
@@ -414,7 +601,7 @@ class FMUManipulationToolboxlMainWindow(QWidget):
414
601
 
415
602
  def apply_operation(self, operation):
416
603
  if self.dropped_fmu.fmu:
417
- self.log_widget.moveCursor(QTextCursor.End)
604
+ self.log_widget.moveCursor(QTextCursor.MoveOperation.End)
418
605
  fmu_filename = os.path.basename(self.dropped_fmu.fmu.fmu_filename)
419
606
  print('-' * 100)
420
607
  self.log_widget.insertHtml(f"<strong>{fmu_filename}: {operation}</strong><br>")
@@ -433,66 +620,128 @@ class FMUManipulationToolboxlMainWindow(QWidget):
433
620
  scroll_bar.setValue(scroll_bar.maximum())
434
621
 
435
622
 
623
+ class ContainerWindow(WindowWithLayout):
624
+ def __init__(self, parent: MainWindow):
625
+ super().__init__('FMU Manipulation Toolbox - Container')
626
+ self.main_window = parent
627
+ self.last_directory = None
628
+
629
+ # ROW 0
630
+ load_button = QPushButton("Load Description")
631
+ load_button.clicked.connect(self.load_container)
632
+ load_button.setProperty("class", "quit")
633
+ self.layout.addWidget(load_button, 0, 0)
634
+
635
+ self.container_label = QLabel()
636
+ self.container_label.setProperty("class", "title")
637
+ self.container_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
638
+ self.layout.addWidget(self.container_label, 0, 1, 1, 2)
639
+
640
+ # ROW 1
641
+ add_fmu_button = QPushButton("Add FMU")
642
+ add_fmu_button.setProperty("class", "modify")
643
+ add_fmu_button.setDisabled(True)
644
+ self.layout.addWidget(add_fmu_button, 1, 1)
645
+
646
+ add_sub_button = QPushButton("Add SubContainer")
647
+ add_sub_button.setProperty("class", "modify")
648
+ add_sub_button.setDisabled(True)
649
+ self.layout.addWidget(add_sub_button, 1, 2)
650
+
651
+ self.assembly_tree = AssemblyTreeWidget(parent=self)
652
+ self.assembly_tree.setMinimumHeight(600)
653
+ self.assembly_tree.setMinimumWidth(200)
654
+ self.layout.addWidget(self.assembly_tree, 1, 0, 3, 1)
655
+
656
+ # ROW 2
657
+ del_fmu_button = QPushButton("Remove FMU")
658
+ del_fmu_button.setProperty("class", "removal")
659
+ del_fmu_button.setDisabled(True)
660
+ self.layout.addWidget(del_fmu_button, 2, 1)
661
+
662
+ del_sub_button = QPushButton("Remove SubContainer")
663
+ del_sub_button.setProperty("class", "removal")
664
+ del_sub_button.setDisabled(True)
665
+ self.layout.addWidget(del_sub_button, 2, 2)
666
+
667
+ # ROW 3
668
+ self.assembly_tab = AssemblyTabWidget(parent=self)
669
+ self.assembly_tab.setMinimumWidth(600)
670
+ self.layout.addWidget(self.assembly_tab, 3, 1, 1, 2)
671
+
672
+ # ROW 4
673
+ close_button = QPushButton("Close")
674
+ close_button.setProperty("class", "quit")
675
+ close_button.clicked.connect(self.close)
676
+ self.layout.addWidget(close_button, 4, 0)
677
+
678
+ save_button = QPushButton("Save Container")
679
+ save_button.setProperty("class", "save")
680
+ self.layout.addWidget(save_button, 4, 2)
681
+
682
+ self.assembly_tree.selectionModel().currentChanged.connect(self.item_selected)
683
+ topIndex = self.assembly_tree.model.index(0, 0, self.assembly_tree.rootIndex())
684
+ self.assembly_tree.setCurrentIndex(topIndex)
685
+
686
+ self.show()
687
+
688
+ def closeEvent(self, event):
689
+ if self.main_window:
690
+ self.main_window.closing_container()
691
+ event.accept()
692
+
693
+ def item_selected(self, current: QModelIndex, previous: QModelIndex):
694
+ if current.isValid():
695
+ node = current.data(role=Qt.ItemDataRole.UserRole + 1)
696
+ node_type = current.data(role=Qt.ItemDataRole.UserRole + 2)
697
+ self.container_label.setText(f"{node.name} ({node_type})")
698
+ else:
699
+ self.container_label.setText("")
700
+
701
+ def load_container(self):
702
+ if self.last_directory:
703
+ default_directory = self.last_directory
704
+ else:
705
+ default_directory = os.path.expanduser('~')
706
+
707
+ filename, _ = QFileDialog.getOpenFileName(parent=self, caption='Select FMU to Manipulate',
708
+ dir=default_directory,
709
+ filter="JSON files (*.json);;SSP files (*.ssp)")
710
+ if filename:
711
+ try:
712
+ self.last_directory = os.path.dirname(filename)
713
+ self.assembly_tree.load_container(filename)
714
+ except Exception as e:
715
+ print(e)
716
+
717
+
436
718
  class Application(QApplication):
437
719
  """
438
720
  Analyse and modify your FMUs.
439
721
 
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.
722
+ Note: modifying the modelDescription.xml can damage your FMU !
723
+ Communicating with the FMU-developer and adapting the way the FMU is generated, is preferable when possible.
442
724
 
443
725
  """
444
726
  def __init__(self, *args, **kwargs):
445
727
  super().__init__(*args, **kwargs)
728
+ self.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.RoundPreferFloor)
446
729
 
447
730
  QDir.addSearchPath('images', os.path.join(os.path.dirname(__file__), "resources"))
448
- font = QFont("Verdana")
449
- font.setPointSize(10)
450
- self.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.setStyleSheet(css_dark)
483
-
731
+ self.setStyleSheet(gui_style_dark)
484
732
 
485
733
  if os.name == 'nt':
734
+ import ctypes
486
735
  self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'icon-round.png')))
487
736
 
488
737
  # https://stackoverflow.com/questions/1551605/how-to-set-applications-taskbar-icon-in-windows-7/1552105#1552105
489
- import ctypes
738
+
490
739
  application_id = 'FMU_Manipulation_Toolbox' # arbitrary string
491
740
  ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(application_id)
492
741
  else:
493
742
  self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'icon.png')))
494
743
 
495
- self.window = FMUManipulationToolboxlMainWindow()
744
+ self.window = MainWindow()
496
745
 
497
746
 
498
747
  def main():
@@ -502,3 +751,7 @@ def main():
502
751
  print(application.__doc__)
503
752
 
504
753
  sys.exit(application.exec())
754
+
755
+
756
+ if __name__ == "__main__":
757
+ main()