fmu-manipulation-toolbox 1.8.1__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.
- fmu_manipulation_toolbox/__version__.py +1 -1
- fmu_manipulation_toolbox/assembly.py +120 -41
- fmu_manipulation_toolbox/cli.py +5 -2
- fmu_manipulation_toolbox/fmu_container.py +53 -15
- fmu_manipulation_toolbox/fmu_operations.py +2 -3
- fmu_manipulation_toolbox/gui.py +330 -79
- 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/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.1.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/METADATA +8 -3
- {fmu_manipulation_toolbox-1.8.1.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/RECORD +24 -21
- {fmu_manipulation_toolbox-1.8.1.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/WHEEL +1 -1
- {fmu_manipulation_toolbox-1.8.1.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/LICENSE.txt +0 -0
- {fmu_manipulation_toolbox-1.8.1.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.dist-info}/entry_points.txt +0 -0
- {fmu_manipulation_toolbox-1.8.1.dist-info → fmu_manipulation_toolbox-1.8.2.dev2.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 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
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,29 +24,29 @@ 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().
|
|
49
|
+
if event.mimeData().hasUrls():
|
|
45
50
|
event.setDropAction(Qt.DropAction.CopyAction)
|
|
46
51
|
try:
|
|
47
52
|
file_path = event.mimeData().urls()[0].toLocalFile()
|
|
@@ -60,7 +65,7 @@ class DropZoneWidget(QLabel):
|
|
|
60
65
|
default_directory = os.path.expanduser('~')
|
|
61
66
|
|
|
62
67
|
fmu_filename, _ = QFileDialog.getOpenFileName(parent=self, caption='Select FMU to Manipulate',
|
|
63
|
-
|
|
68
|
+
dir=default_directory, filter="FMU files (*.fmu)")
|
|
64
69
|
if fmu_filename:
|
|
65
70
|
self.set_fmu(fmu_filename)
|
|
66
71
|
|
|
@@ -71,7 +76,7 @@ class DropZoneWidget(QLabel):
|
|
|
71
76
|
filename = os.path.join(os.path.dirname(__file__), "resources", "fmu.png")
|
|
72
77
|
|
|
73
78
|
base_image = QImage(filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
|
|
74
|
-
|
|
79
|
+
Qt.TransformationMode.SmoothTransformation)
|
|
75
80
|
mask_filename = os.path.join(os.path.dirname(__file__), "resources", "mask.png")
|
|
76
81
|
mask_image = QImage(mask_filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
|
|
77
82
|
Qt.TransformationMode.SmoothTransformation)
|
|
@@ -87,8 +92,7 @@ class DropZoneWidget(QLabel):
|
|
|
87
92
|
|
|
88
93
|
self.setPixmap(pixmap)
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
def set_fmu(self, filename):
|
|
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,17 +140,10 @@ 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
|
|
|
@@ -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,30 +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()
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class AssemblPropertiesWidget(QWidget):
|
|
330
|
+
def __init__(self, parent=None):
|
|
331
|
+
super().__init__(parent)
|
|
332
|
+
|
|
333
|
+
self.layout = QGridLayout()
|
|
334
|
+
self.layout.setVerticalSpacing(4)
|
|
335
|
+
self.layout.setHorizontalSpacing(4)
|
|
336
|
+
self.layout.setContentsMargins(10, 10, 10, 10)
|
|
337
|
+
self.setLayout(self.layout)
|
|
214
338
|
|
|
215
|
-
|
|
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)
|
|
216
378
|
|
|
217
|
-
# set the grid layout
|
|
218
379
|
self.layout = QGridLayout()
|
|
219
380
|
self.layout.setVerticalSpacing(4)
|
|
220
381
|
self.layout.setHorizontalSpacing(4)
|
|
221
382
|
self.layout.setContentsMargins(10, 10, 10, 10)
|
|
222
383
|
self.setLayout(self.layout)
|
|
223
384
|
|
|
385
|
+
|
|
386
|
+
class MainWindow(WindowWithLayout):
|
|
387
|
+
def __init__(self):
|
|
388
|
+
super().__init__('FMU Manipulation Toolbox')
|
|
389
|
+
|
|
224
390
|
self.dropped_fmu = DropZoneWidget()
|
|
225
391
|
self.dropped_fmu.clicked.connect(self.update_fmu)
|
|
392
|
+
|
|
226
393
|
self.layout.addWidget(self.dropped_fmu, 0, 0, 4, 1)
|
|
227
394
|
|
|
228
|
-
font = QFont('Verdana')
|
|
229
|
-
font.setPointSize(14)
|
|
230
|
-
font.setBold(True)
|
|
231
395
|
self.fmu_title = QLabel()
|
|
232
|
-
self.fmu_title.
|
|
396
|
+
self.fmu_title.setProperty("class", "title")
|
|
233
397
|
self.layout.addWidget(self.fmu_title, 0, 1, 1, 4)
|
|
234
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
|
+
|
|
235
406
|
help_widget = HelpWidget()
|
|
236
407
|
self.layout.addWidget(help_widget, 0, 5, 1, 1)
|
|
237
408
|
|
|
@@ -267,8 +438,8 @@ class FMUManipulationToolboxlMainWindow(QWidget):
|
|
|
267
438
|
self.add_operation(operation[0], operation[1], operation[2], operation[3], line, col, **operation[4])
|
|
268
439
|
|
|
269
440
|
line += 1
|
|
270
|
-
self.apply_filter_label = QLabel("Apply
|
|
271
|
-
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)
|
|
272
443
|
self.set_tooltip(self.apply_filter_label, 'gui-apply-only')
|
|
273
444
|
|
|
274
445
|
causality = ["parameter", "calculatedParameter", "input", "output", "local", "independent"]
|
|
@@ -308,6 +479,19 @@ class FMUManipulationToolboxlMainWindow(QWidget):
|
|
|
308
479
|
# show the window
|
|
309
480
|
self.show()
|
|
310
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
|
+
|
|
311
495
|
def set_tooltip(self, widget, usage):
|
|
312
496
|
widget.setToolTip("\n".join(textwrap.wrap(self.help.usage(usage))))
|
|
313
497
|
|
|
@@ -385,7 +569,7 @@ class FMUManipulationToolboxlMainWindow(QWidget):
|
|
|
385
569
|
self.layout.addWidget(button, x, y)
|
|
386
570
|
|
|
387
571
|
def prompt_string(self, message):
|
|
388
|
-
text, ok = QInputDialog().getText(self, "Enter value", f"{message}:", QLineEdit.Normal, "")
|
|
572
|
+
text, ok = QInputDialog().getText(self, "Enter value", f"{message}:", QLineEdit.EchoMode.Normal, "")
|
|
389
573
|
|
|
390
574
|
if ok and text:
|
|
391
575
|
return text
|
|
@@ -436,6 +620,101 @@ class FMUManipulationToolboxlMainWindow(QWidget):
|
|
|
436
620
|
scroll_bar.setValue(scroll_bar.maximum())
|
|
437
621
|
|
|
438
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
|
+
|
|
439
718
|
class Application(QApplication):
|
|
440
719
|
"""
|
|
441
720
|
Analyse and modify your FMUs.
|
|
@@ -446,55 +725,23 @@ Communicating with the FMU-developer and adapting the way the FMU is generated,
|
|
|
446
725
|
"""
|
|
447
726
|
def __init__(self, *args, **kwargs):
|
|
448
727
|
super().__init__(*args, **kwargs)
|
|
728
|
+
self.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.RoundPreferFloor)
|
|
449
729
|
|
|
450
730
|
QDir.addSearchPath('images', os.path.join(os.path.dirname(__file__), "resources"))
|
|
451
|
-
|
|
452
|
-
font.setPointSize(10)
|
|
453
|
-
self.setFont(font)
|
|
454
|
-
css_dark = """
|
|
455
|
-
QWidget {background: #4b4e51; color: #b5bab9}
|
|
456
|
-
QPushButton { min-height: 30px; padding: 1px 1px 0.2em 0.2em; border: 1px solid #282830; border-radius: 5px;}
|
|
457
|
-
QComboBox { min-height: 30px; padding: 1px 1px 0.2em 0.2em; border: 1px solid #282830; border-radius: 5px;}
|
|
458
|
-
QPushButton:pressed { border: 2px solid #282830; }
|
|
459
|
-
QPushButton.info {background-color: #4e6749; color: #dddddd;}
|
|
460
|
-
QPushButton.info:hover {background-color: #5f7850; color: #dddddd;}
|
|
461
|
-
QPushButton.info:hover {background-color: #5f7850; color: #dddddd;}
|
|
462
|
-
QPushButton.modify {background-color: #98763f; color: #dddddd;}
|
|
463
|
-
QPushButton.modify:hover {background-color: #a9874f; color: #dddddd;}
|
|
464
|
-
QPushButton.removal {background-color: #692e2e; color: #dddddd;}
|
|
465
|
-
QPushButton.removal:hover{background-color: #7a3f3f; color: #dddddd;}
|
|
466
|
-
QPushButton.save {background-color: #564967; color: #dddddd;}
|
|
467
|
-
QPushButton.save:hover {background-color: #675a78; color: #dddddd;}
|
|
468
|
-
QPushButton.quit {background-color: #4571a4; color: #dddddd;}
|
|
469
|
-
QPushButton.quit:hover {background-color: #5682b5; color: #dddddd;}
|
|
470
|
-
QToolTip {color: black}
|
|
471
|
-
QLabel.dropped_fmu {background-color: #b5bab9}
|
|
472
|
-
QLabel.dropped_fmu:hover {background-color: #c6cbca}
|
|
473
|
-
QTextBrowser {background-color: #282830; color: #b5bab9;}
|
|
474
|
-
QMenu {font-size: 18px;}
|
|
475
|
-
QMenu::item {padding: 2px 250px 2px 20px; border: 1px solid transparent;}
|
|
476
|
-
QMenu::item::indicator {width: 32px; height: 32px; }
|
|
477
|
-
QMenu::indicator:checked {image: url(images:checkbox-checked.png);}
|
|
478
|
-
QMenu::indicator:checked:hover {image: url(images:checkbox-checked-hover.png);}
|
|
479
|
-
QMenu::indicator:checked:disabled {image: url(images:checkbox-checked-disabled.png);}
|
|
480
|
-
QMenu::indicator:unchecked {IconWidth: 50px; image: url(images:checkbox-unchecked.png); }
|
|
481
|
-
QMenu::indicator:unchecked:hover {width: 35px; image: url(images:checkbox-unchecked-hover.png); }
|
|
482
|
-
QMenu::indicator:unchecked:disabled {width: 35px; image: url(images:checkbox-unchecked-disabled.png); }
|
|
483
|
-
"""
|
|
484
|
-
|
|
485
|
-
self.setStyleSheet(css_dark)
|
|
731
|
+
self.setStyleSheet(gui_style_dark)
|
|
486
732
|
|
|
487
733
|
if os.name == 'nt':
|
|
734
|
+
import ctypes
|
|
488
735
|
self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'icon-round.png')))
|
|
489
736
|
|
|
490
737
|
# https://stackoverflow.com/questions/1551605/how-to-set-applications-taskbar-icon-in-windows-7/1552105#1552105
|
|
491
|
-
|
|
738
|
+
|
|
492
739
|
application_id = 'FMU_Manipulation_Toolbox' # arbitrary string
|
|
493
740
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(application_id)
|
|
494
741
|
else:
|
|
495
742
|
self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'icon.png')))
|
|
496
743
|
|
|
497
|
-
self.window =
|
|
744
|
+
self.window = MainWindow()
|
|
498
745
|
|
|
499
746
|
|
|
500
747
|
def main():
|
|
@@ -504,3 +751,7 @@ def main():
|
|
|
504
751
|
print(application.__doc__)
|
|
505
752
|
|
|
506
753
|
sys.exit(application.exec())
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
if __name__ == "__main__":
|
|
757
|
+
main()
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# if os.name == 'nt':
|
|
2
|
+
# font = QFont('Consolas')
|
|
3
|
+
# font.setPointSize(11)
|
|
4
|
+
# else:
|
|
5
|
+
# font = QFont('Courier New')
|
|
6
|
+
# font.setPointSize(12)
|
|
7
|
+
# self.setFont(font)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
gui_style_dark = """
|
|
11
|
+
QWidget {
|
|
12
|
+
font: 10pt "Verdana";
|
|
13
|
+
background: #4b4e51;
|
|
14
|
+
color: #b5bab9;
|
|
15
|
+
}
|
|
16
|
+
QPushButton, QComboBox {
|
|
17
|
+
min-height: 30px;
|
|
18
|
+
padding: 1px 1px 0.2em 0.2em;
|
|
19
|
+
border: 1px solid #282830;
|
|
20
|
+
border-radius: 5px;
|
|
21
|
+
color: #dddddd;
|
|
22
|
+
}
|
|
23
|
+
QPushButton:pressed {
|
|
24
|
+
border: 2px solid #282830;
|
|
25
|
+
}
|
|
26
|
+
QPushButton.info {
|
|
27
|
+
background-color: #4e6749;
|
|
28
|
+
}
|
|
29
|
+
QPushButton.info:hover {
|
|
30
|
+
background-color: #5f7850;
|
|
31
|
+
}
|
|
32
|
+
QPushButton.modify {
|
|
33
|
+
background-color: #98763f;
|
|
34
|
+
}
|
|
35
|
+
QPushButton.modify:hover {
|
|
36
|
+
background-color: #a9874f;
|
|
37
|
+
}
|
|
38
|
+
QPushButton.removal {
|
|
39
|
+
background-color: #692e2e;
|
|
40
|
+
}
|
|
41
|
+
QPushButton.removal:hover {
|
|
42
|
+
background-color: #7a3f3f;
|
|
43
|
+
}
|
|
44
|
+
QPushButton.save {
|
|
45
|
+
background-color: #564967;
|
|
46
|
+
}
|
|
47
|
+
QPushButton.save:hover {
|
|
48
|
+
background-color: #675a78;
|
|
49
|
+
}
|
|
50
|
+
QPushButton.quit {
|
|
51
|
+
background-color: #4571a4;
|
|
52
|
+
}
|
|
53
|
+
QPushButton.quit:hover {
|
|
54
|
+
background-color: #5682b5;
|
|
55
|
+
}
|
|
56
|
+
QPushButton::disabled {
|
|
57
|
+
background-color: gray;
|
|
58
|
+
}
|
|
59
|
+
QToolTip {
|
|
60
|
+
color: black
|
|
61
|
+
}
|
|
62
|
+
QLabel.dropped_fmu {
|
|
63
|
+
background-color: #b5bab9
|
|
64
|
+
}
|
|
65
|
+
QLabel.title {
|
|
66
|
+
font: 14pt bold "Verdana";
|
|
67
|
+
}
|
|
68
|
+
QLabel.dropped_fmu:hover {
|
|
69
|
+
background-color: #c6cbca
|
|
70
|
+
}
|
|
71
|
+
QTextBrowser, QTreeView {
|
|
72
|
+
font: 11pt "Consolas";
|
|
73
|
+
background-color: #282830;
|
|
74
|
+
color: #b5bab9;
|
|
75
|
+
}
|
|
76
|
+
QMenu::item {
|
|
77
|
+
padding: 2px 250px 2px 20px;
|
|
78
|
+
border: 1px solid transparent;
|
|
79
|
+
}
|
|
80
|
+
QMenu::item::indicator, QCheckBox::item::indicator {
|
|
81
|
+
width: 32px;
|
|
82
|
+
height: 32px;
|
|
83
|
+
}
|
|
84
|
+
QMenu::indicator:checked, QCheckBox::indicator:checked {
|
|
85
|
+
image: url(images:checkbox-checked.png);
|
|
86
|
+
}
|
|
87
|
+
QMenu::indicator:checked:hover, QCheckBox::indicator:checked:hover {
|
|
88
|
+
image: url(images:checkbox-checked-hover.png);
|
|
89
|
+
}
|
|
90
|
+
QMenu::indicator:checked:disabled, QCheckBox::indicator:checked:disabled {
|
|
91
|
+
image: url(images:checkbox-checked-disabled.png);
|
|
92
|
+
}
|
|
93
|
+
QMenu::indicator:unchecked, QCheckBox::indicator:unchecked {
|
|
94
|
+
image: url(images:checkbox-unchecked.png);
|
|
95
|
+
}
|
|
96
|
+
QMenu::indicator:unchecked:hover, QCheckBox::indicator:unchecked:hover {
|
|
97
|
+
image: url(images:checkbox-unchecked-hover.png);
|
|
98
|
+
}
|
|
99
|
+
QMenu::indicator:unchecked:disabled, QCheckBox::indicator:unchecked:disabled {
|
|
100
|
+
image: url(images:checkbox-unchecked-disabled.png);
|
|
101
|
+
}
|
|
102
|
+
QCheckBox::item {
|
|
103
|
+
padding: 2px 250px 2px 20px;
|
|
104
|
+
border: 1px solid transparent;
|
|
105
|
+
}
|
|
106
|
+
QTabBar::tab {
|
|
107
|
+
min-height: 30px;
|
|
108
|
+
padding: 1px 1px 0.2em 0.2em;
|
|
109
|
+
color: #dddddd;
|
|
110
|
+
margin: 2px;
|
|
111
|
+
margin-bottom: 0px;
|
|
112
|
+
border: 1px solid #282830;
|
|
113
|
+
border-top-left-radius: 5px;
|
|
114
|
+
border-top-right-radius: 5px;
|
|
115
|
+
}
|
|
116
|
+
QTabBar::tab:selected, QTabBar::tab:hover {
|
|
117
|
+
background-color: #5f7850;
|
|
118
|
+
margin-bottom:-1px;
|
|
119
|
+
}
|
|
120
|
+
QTabBar {
|
|
121
|
+
border-bottom: 1px solid #282830;
|
|
122
|
+
}
|
|
123
|
+
QTabBar::tab:top:last, QTabBar::tab:bottom:last {
|
|
124
|
+
margin-right: 0;
|
|
125
|
+
}
|
|
126
|
+
QTabBar::tab:top:first, QTabBar::tab:bottom:first {
|
|
127
|
+
margin-left: 0;
|
|
128
|
+
}
|
|
129
|
+
"""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|