fmu-manipulation-toolbox 1.8__py3-none-any.whl → 1.8.2__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.
- fmu_manipulation_toolbox/__version__.py +1 -1
- fmu_manipulation_toolbox/assembly.py +241 -127
- fmu_manipulation_toolbox/cli.py +16 -7
- fmu_manipulation_toolbox/fmu_container.py +69 -30
- fmu_manipulation_toolbox/fmu_operations.py +2 -3
- fmu_manipulation_toolbox/gui.py +356 -103
- fmu_manipulation_toolbox/gui_style.py +129 -0
- fmu_manipulation_toolbox/resources/container.png +0 -0
- fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
- fmu_manipulation_toolbox/resources/fmu.png +0 -0
- fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
- fmu_manipulation_toolbox/resources/icon_fmu.png +0 -0
- fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
- fmu_manipulation_toolbox/resources/mask.png +0 -0
- fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
- fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
- fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
- fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
- fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dist-info}/METADATA +8 -3
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dist-info}/RECORD +25 -21
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dist-info}/WHEEL +1 -1
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dist-info}/LICENSE.txt +0 -0
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dist-info}/entry_points.txt +0 -0
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.2.dist-info}/top_level.txt +0 -0
fmu_manipulation_toolbox/gui.py
CHANGED
|
@@ -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 =
|
|
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().
|
|
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().
|
|
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().
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
self.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
painter
|
|
83
|
-
painter.
|
|
84
|
-
painter.
|
|
85
|
-
painter.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
self.
|
|
89
|
-
|
|
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 =
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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,
|
|
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
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
268
|
-
self.layout.addWidget(self.apply_filter_label, line,
|
|
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 !
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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()
|