sim window save/load
This commit is contained in:
@@ -124,7 +124,11 @@ Keep the root of `src/bedit` minimal. New UI modules should go into the relevant
|
|||||||
callbacks; the simulation service retains the latest progress for polling.
|
callbacks; the simulation service retains the latest progress for polling.
|
||||||
- The application owns one reusable `SimulationWindow`. Starting a run clears its
|
- The application owns one reusable `SimulationWindow`. Starting a run clears its
|
||||||
progress, log, and future result views. Extend graph presentation through its
|
progress, log, and future result views. Extend graph presentation through its
|
||||||
Designer-owned `resultsLayout` and the `clear_results()`/`load_results()` hooks.
|
Designer-owned `resultsLayout` and the
|
||||||
|
`clear_result_views()`/`load_result_views()` hooks.
|
||||||
|
- Standalone simulation-result JSON is modeled in `core/simulation/results.py`.
|
||||||
|
Its versioned schema retains model status, messages, metadata, and plottable
|
||||||
|
traces so the simulation window can open results without an active document.
|
||||||
- The optional OpenModelica executable is persisted as
|
- The optional OpenModelica executable is persisted as
|
||||||
`simulation/openModelicaPath`. The GUI passes it into `Simulation`; core must
|
`simulation/openModelicaPath`. The GUI passes it into `Simulation`; core must
|
||||||
not read `QSettings`. An empty path uses OMPython/PATH discovery, while an
|
not read `QSettings`. An empty path uses OMPython/PATH discovery, while an
|
||||||
|
|||||||
27
BEdit/m_Test-results.json
Normal file
27
BEdit/m_Test-results.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"format": "bedit-simulation-results",
|
||||||
|
"version": 1,
|
||||||
|
"modelName": "m_Test",
|
||||||
|
"status": {
|
||||||
|
"phase": "Simulation finished",
|
||||||
|
"currentStepSize": 0.0,
|
||||||
|
"time": 10.0,
|
||||||
|
"progress": 10000
|
||||||
|
},
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"stream": "LOG_SUCCESS",
|
||||||
|
"type": "info",
|
||||||
|
"text": "The initialization finished successfully without homotopy method."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stream": "LOG_SUCCESS",
|
||||||
|
"type": "info",
|
||||||
|
"text": "The simulation finished successfully."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"traces": [],
|
||||||
|
"metadata": {
|
||||||
|
"processResult": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
from bedit.core.simulation.service import Simulation
|
from bedit.core.simulation.service import Simulation
|
||||||
from bedit.core.simulation.openmodelica import OpenModelicaInterface
|
from bedit.core.simulation.openmodelica import OpenModelicaInterface
|
||||||
|
from bedit.core.simulation.results import SimulationResults, SimulationTrace
|
||||||
|
|
||||||
__all__ = ["OpenModelicaInterface", "Simulation"]
|
__all__ = [
|
||||||
|
"OpenModelicaInterface",
|
||||||
|
"Simulation",
|
||||||
|
"SimulationResults",
|
||||||
|
"SimulationTrace",
|
||||||
|
]
|
||||||
|
|||||||
78
BEdit/src/bedit/core/simulation/results.py
Normal file
78
BEdit/src/bedit/core/simulation/results.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import json
|
||||||
|
from dataclasses import asdict, dataclass, field
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
RESULTS_FORMAT = "bedit-simulation-results"
|
||||||
|
RESULTS_VERSION = 1
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SimulationTrace:
|
||||||
|
"""One plottable series; samples can be filled by a future result importer."""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
x_values: list[float] = field(default_factory=list)
|
||||||
|
y_values: list[float] = field(default_factory=list)
|
||||||
|
x_label: str = "time"
|
||||||
|
y_label: str = ""
|
||||||
|
unit: str = ""
|
||||||
|
properties: dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SimulationResults:
|
||||||
|
"""Serializable state displayed by the standalone simulation window."""
|
||||||
|
|
||||||
|
model_name: str = ""
|
||||||
|
status: dict[str, Any] = field(default_factory=dict)
|
||||||
|
messages: list[dict[str, str]] = field(default_factory=list)
|
||||||
|
traces: list[SimulationTrace] = field(default_factory=list)
|
||||||
|
metadata: dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"format": RESULTS_FORMAT,
|
||||||
|
"version": RESULTS_VERSION,
|
||||||
|
"modelName": self.model_name,
|
||||||
|
"status": dict(self.status),
|
||||||
|
"messages": [dict(message) for message in self.messages],
|
||||||
|
"traces": [asdict(trace) for trace in self.traces],
|
||||||
|
"metadata": dict(self.metadata),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict[str, Any]) -> "SimulationResults":
|
||||||
|
if data.get("format") != RESULTS_FORMAT:
|
||||||
|
raise ValueError("Not a BEdit simulation-results file")
|
||||||
|
if data.get("version") != RESULTS_VERSION:
|
||||||
|
raise ValueError(f"Unsupported simulation-results version: {data.get('version')!r}")
|
||||||
|
try:
|
||||||
|
traces = [SimulationTrace(**trace) for trace in data.get("traces", [])]
|
||||||
|
return cls(
|
||||||
|
model_name=str(data.get("modelName", "")),
|
||||||
|
status=dict(data.get("status", {})),
|
||||||
|
messages=[dict(message) for message in data.get("messages", [])],
|
||||||
|
traces=traces,
|
||||||
|
metadata=dict(data.get("metadata", {})),
|
||||||
|
)
|
||||||
|
except (TypeError, ValueError) as error:
|
||||||
|
raise ValueError("Malformed simulation-results data") from error
|
||||||
|
|
||||||
|
|
||||||
|
def save_simulation_results(path: str | Path, results: SimulationResults) -> None:
|
||||||
|
Path(path).write_text(
|
||||||
|
json.dumps(results.to_dict(), indent=2, ensure_ascii=False) + "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_simulation_results(path: str | Path) -> SimulationResults:
|
||||||
|
try:
|
||||||
|
data = json.loads(Path(path).read_text(encoding="utf-8"))
|
||||||
|
except (OSError, json.JSONDecodeError) as error:
|
||||||
|
raise ValueError(f"Could not read simulation results: {error}") from error
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise ValueError("Simulation-results root must be an object")
|
||||||
|
return SimulationResults.from_dict(data)
|
||||||
BIN
BEdit/src/bedit/data/libraries/default.beb
Normal file
BIN
BEdit/src/bedit/data/libraries/default.beb
Normal file
Binary file not shown.
@@ -51,6 +51,9 @@ class Ui_MainWindow(object):
|
|||||||
icon3 = QIcon()
|
icon3 = QIcon()
|
||||||
icon3.addFile(u":/icons/icons/media-playback-start.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
icon3.addFile(u":/icons/icons/media-playback-start.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||||
self.actionRunSimulation.setIcon(icon3)
|
self.actionRunSimulation.setIcon(icon3)
|
||||||
|
self.actionSimulationWindow = QAction(MainWindow)
|
||||||
|
self.actionSimulationWindow.setObjectName(u"actionSimulationWindow")
|
||||||
|
self.actionSimulationWindow.setIcon(icon1)
|
||||||
self.actionNew = QAction(MainWindow)
|
self.actionNew = QAction(MainWindow)
|
||||||
self.actionNew.setObjectName(u"actionNew")
|
self.actionNew.setObjectName(u"actionNew")
|
||||||
icon4 = QIcon()
|
icon4 = QIcon()
|
||||||
@@ -436,6 +439,7 @@ class Ui_MainWindow(object):
|
|||||||
self.menuSimulation.addAction(self.actionSimulationSettings)
|
self.menuSimulation.addAction(self.actionSimulationSettings)
|
||||||
self.menuSimulation.addAction(self.actionGraphParameters)
|
self.menuSimulation.addAction(self.actionGraphParameters)
|
||||||
self.menuSimulation.addAction(self.actionCompose)
|
self.menuSimulation.addAction(self.actionCompose)
|
||||||
|
self.menuSimulation.addAction(self.actionSimulationWindow)
|
||||||
self.menuSimulation.addAction(self.actionRunSimulation)
|
self.menuSimulation.addAction(self.actionRunSimulation)
|
||||||
self.fileToolbar.addAction(self.actionNew)
|
self.fileToolbar.addAction(self.actionNew)
|
||||||
self.fileToolbar.addAction(self.actionOpen)
|
self.fileToolbar.addAction(self.actionOpen)
|
||||||
@@ -452,6 +456,7 @@ class Ui_MainWindow(object):
|
|||||||
self.simulationToolbar.addAction(self.actionSimulationSettings)
|
self.simulationToolbar.addAction(self.actionSimulationSettings)
|
||||||
self.simulationToolbar.addAction(self.actionGraphParameters)
|
self.simulationToolbar.addAction(self.actionGraphParameters)
|
||||||
self.simulationToolbar.addAction(self.actionCompose)
|
self.simulationToolbar.addAction(self.actionCompose)
|
||||||
|
self.simulationToolbar.addAction(self.actionSimulationWindow)
|
||||||
self.simulationToolbar.addAction(self.actionRunSimulation)
|
self.simulationToolbar.addAction(self.actionRunSimulation)
|
||||||
|
|
||||||
self.retranslateUi(MainWindow)
|
self.retranslateUi(MainWindow)
|
||||||
@@ -486,6 +491,10 @@ class Ui_MainWindow(object):
|
|||||||
#if QT_CONFIG(shortcut)
|
#if QT_CONFIG(shortcut)
|
||||||
self.actionRunSimulation.setShortcut(QCoreApplication.translate("MainWindow", u"F6", None))
|
self.actionRunSimulation.setShortcut(QCoreApplication.translate("MainWindow", u"F6", None))
|
||||||
#endif // QT_CONFIG(shortcut)
|
#endif // QT_CONFIG(shortcut)
|
||||||
|
self.actionSimulationWindow.setText(QCoreApplication.translate("MainWindow", u"Simulation Window", None))
|
||||||
|
#if QT_CONFIG(statustip)
|
||||||
|
self.actionSimulationWindow.setStatusTip(QCoreApplication.translate("MainWindow", u"Show the simulation results window", None))
|
||||||
|
#endif // QT_CONFIG(statustip)
|
||||||
self.actionNew.setText(QCoreApplication.translate("MainWindow", u"&New", None))
|
self.actionNew.setText(QCoreApplication.translate("MainWindow", u"&New", None))
|
||||||
#if QT_CONFIG(statustip)
|
#if QT_CONFIG(statustip)
|
||||||
self.actionNew.setStatusTip(QCoreApplication.translate("MainWindow", u"Create a new document", None))
|
self.actionNew.setStatusTip(QCoreApplication.translate("MainWindow", u"Create a new document", None))
|
||||||
|
|||||||
@@ -11,84 +11,169 @@
|
|||||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
QMetaObject, QObject, QPoint, QRect,
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
QSize, QTime, QUrl, Qt)
|
QSize, QTime, QUrl, Qt)
|
||||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
|
||||||
QFont, QFontDatabase, QGradient, QIcon,
|
QCursor, QFont, QFontDatabase, QGradient,
|
||||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
QIcon, QImage, QKeySequence, QLinearGradient,
|
||||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
QPainter, QPalette, QPixmap, QRadialGradient,
|
||||||
from PySide6.QtWidgets import (QApplication, QLabel, QListWidget, QListWidgetItem,
|
QTransform)
|
||||||
QMainWindow, QProgressBar, QSizePolicy, QTabWidget,
|
from PySide6.QtWidgets import (QApplication, QDockWidget, QLabel, QListWidget,
|
||||||
QVBoxLayout, QWidget)
|
QListWidgetItem, QMainWindow, QMenu, QMenuBar,
|
||||||
|
QProgressBar, QSizePolicy, QToolBar, QVBoxLayout,
|
||||||
|
QWidget)
|
||||||
|
from . import resources_rc
|
||||||
|
|
||||||
class Ui_SimulationWindow(object):
|
class Ui_SimulationWindow(object):
|
||||||
def setupUi(self, SimulationWindow):
|
def setupUi(self, SimulationWindow):
|
||||||
if not SimulationWindow.objectName():
|
if not SimulationWindow.objectName():
|
||||||
SimulationWindow.setObjectName(u"SimulationWindow")
|
SimulationWindow.setObjectName(u"SimulationWindow")
|
||||||
SimulationWindow.resize(720, 480)
|
SimulationWindow.resize(900, 650)
|
||||||
|
self.actionOpen = QAction(SimulationWindow)
|
||||||
|
self.actionOpen.setObjectName(u"actionOpen")
|
||||||
|
icon = QIcon()
|
||||||
|
icon.addFile(u":/icons/icons/document-open.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||||
|
self.actionOpen.setIcon(icon)
|
||||||
|
self.actionSave = QAction(SimulationWindow)
|
||||||
|
self.actionSave.setObjectName(u"actionSave")
|
||||||
|
icon1 = QIcon()
|
||||||
|
icon1.addFile(u":/icons/icons/document-save.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||||
|
self.actionSave.setIcon(icon1)
|
||||||
|
self.actionClear = QAction(SimulationWindow)
|
||||||
|
self.actionClear.setObjectName(u"actionClear")
|
||||||
|
icon2 = QIcon()
|
||||||
|
icon2.addFile(u":/icons/icons/document-new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||||
|
self.actionClear.setIcon(icon2)
|
||||||
|
self.actionExit = QAction(SimulationWindow)
|
||||||
|
self.actionExit.setObjectName(u"actionExit")
|
||||||
|
self.actionAbout = QAction(SimulationWindow)
|
||||||
|
self.actionAbout.setObjectName(u"actionAbout")
|
||||||
|
self.actionAboutQt = QAction(SimulationWindow)
|
||||||
|
self.actionAboutQt.setObjectName(u"actionAboutQt")
|
||||||
self.centralWidget = QWidget(SimulationWindow)
|
self.centralWidget = QWidget(SimulationWindow)
|
||||||
self.centralWidget.setObjectName(u"centralWidget")
|
self.centralWidget.setObjectName(u"centralWidget")
|
||||||
self.windowLayout = QVBoxLayout(self.centralWidget)
|
self.centralWidget.setMaximumSize(QSize(0, 0))
|
||||||
self.windowLayout.setObjectName(u"windowLayout")
|
SimulationWindow.setCentralWidget(self.centralWidget)
|
||||||
self.statusLabel = QLabel(self.centralWidget)
|
self.menuBar = QMenuBar(SimulationWindow)
|
||||||
self.statusLabel.setObjectName(u"statusLabel")
|
self.menuBar.setObjectName(u"menuBar")
|
||||||
|
self.menuFile = QMenu(self.menuBar)
|
||||||
self.windowLayout.addWidget(self.statusLabel)
|
self.menuFile.setObjectName(u"menuFile")
|
||||||
|
self.menuView = QMenu(self.menuBar)
|
||||||
self.progressBar = QProgressBar(self.centralWidget)
|
self.menuView.setObjectName(u"menuView")
|
||||||
self.progressBar.setObjectName(u"progressBar")
|
self.menuPanels = QMenu(self.menuView)
|
||||||
self.progressBar.setMaximum(10000)
|
self.menuPanels.setObjectName(u"menuPanels")
|
||||||
self.progressBar.setValue(0)
|
self.menuToolbars = QMenu(self.menuView)
|
||||||
|
self.menuToolbars.setObjectName(u"menuToolbars")
|
||||||
self.windowLayout.addWidget(self.progressBar)
|
self.menuHelp = QMenu(self.menuBar)
|
||||||
|
self.menuHelp.setObjectName(u"menuHelp")
|
||||||
self.timeLabel = QLabel(self.centralWidget)
|
SimulationWindow.setMenuBar(self.menuBar)
|
||||||
self.timeLabel.setObjectName(u"timeLabel")
|
self.fileToolbar = QToolBar(SimulationWindow)
|
||||||
|
self.fileToolbar.setObjectName(u"fileToolbar")
|
||||||
self.windowLayout.addWidget(self.timeLabel)
|
self.fileToolbar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
|
||||||
|
SimulationWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.fileToolbar)
|
||||||
self.resultsTabs = QTabWidget(self.centralWidget)
|
self.resultsDock = QDockWidget(SimulationWindow)
|
||||||
self.resultsTabs.setObjectName(u"resultsTabs")
|
self.resultsDock.setObjectName(u"resultsDock")
|
||||||
self.logTab = QWidget()
|
self.resultsDockContents = QWidget()
|
||||||
self.logTab.setObjectName(u"logTab")
|
self.resultsDockContents.setObjectName(u"resultsDockContents")
|
||||||
self.logLayout = QVBoxLayout(self.logTab)
|
self.resultsLayout = QVBoxLayout(self.resultsDockContents)
|
||||||
self.logLayout.setObjectName(u"logLayout")
|
|
||||||
self.messageList = QListWidget(self.logTab)
|
|
||||||
self.messageList.setObjectName(u"messageList")
|
|
||||||
self.messageList.setAlternatingRowColors(True)
|
|
||||||
|
|
||||||
self.logLayout.addWidget(self.messageList)
|
|
||||||
|
|
||||||
self.resultsTabs.addTab(self.logTab, "")
|
|
||||||
self.resultsTab = QWidget()
|
|
||||||
self.resultsTab.setObjectName(u"resultsTab")
|
|
||||||
self.resultsLayout = QVBoxLayout(self.resultsTab)
|
|
||||||
self.resultsLayout.setObjectName(u"resultsLayout")
|
self.resultsLayout.setObjectName(u"resultsLayout")
|
||||||
self.resultsPlaceholder = QLabel(self.resultsTab)
|
self.resultsPlaceholder = QLabel(self.resultsDockContents)
|
||||||
self.resultsPlaceholder.setObjectName(u"resultsPlaceholder")
|
self.resultsPlaceholder.setObjectName(u"resultsPlaceholder")
|
||||||
self.resultsPlaceholder.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.resultsPlaceholder.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
self.resultsLayout.addWidget(self.resultsPlaceholder)
|
self.resultsLayout.addWidget(self.resultsPlaceholder)
|
||||||
|
|
||||||
self.resultsTabs.addTab(self.resultsTab, "")
|
self.resultsDock.setWidget(self.resultsDockContents)
|
||||||
|
SimulationWindow.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.resultsDock)
|
||||||
|
self.statusDock = QDockWidget(SimulationWindow)
|
||||||
|
self.statusDock.setObjectName(u"statusDock")
|
||||||
|
self.statusDockContents = QWidget()
|
||||||
|
self.statusDockContents.setObjectName(u"statusDockContents")
|
||||||
|
self.statusLayout = QVBoxLayout(self.statusDockContents)
|
||||||
|
self.statusLayout.setObjectName(u"statusLayout")
|
||||||
|
self.statusLabel = QLabel(self.statusDockContents)
|
||||||
|
self.statusLabel.setObjectName(u"statusLabel")
|
||||||
|
|
||||||
self.windowLayout.addWidget(self.resultsTabs)
|
self.statusLayout.addWidget(self.statusLabel)
|
||||||
|
|
||||||
SimulationWindow.setCentralWidget(self.centralWidget)
|
self.progressBar = QProgressBar(self.statusDockContents)
|
||||||
|
self.progressBar.setObjectName(u"progressBar")
|
||||||
|
self.progressBar.setMaximum(10000)
|
||||||
|
self.progressBar.setValue(0)
|
||||||
|
|
||||||
|
self.statusLayout.addWidget(self.progressBar)
|
||||||
|
|
||||||
|
self.timeLabel = QLabel(self.statusDockContents)
|
||||||
|
self.timeLabel.setObjectName(u"timeLabel")
|
||||||
|
|
||||||
|
self.statusLayout.addWidget(self.timeLabel)
|
||||||
|
|
||||||
|
self.statusDock.setWidget(self.statusDockContents)
|
||||||
|
SimulationWindow.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.statusDock)
|
||||||
|
self.logDock = QDockWidget(SimulationWindow)
|
||||||
|
self.logDock.setObjectName(u"logDock")
|
||||||
|
self.logDockContents = QWidget()
|
||||||
|
self.logDockContents.setObjectName(u"logDockContents")
|
||||||
|
self.logLayout = QVBoxLayout(self.logDockContents)
|
||||||
|
self.logLayout.setObjectName(u"logLayout")
|
||||||
|
self.messageList = QListWidget(self.logDockContents)
|
||||||
|
self.messageList.setObjectName(u"messageList")
|
||||||
|
self.messageList.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
self.logLayout.addWidget(self.messageList)
|
||||||
|
|
||||||
|
self.logDock.setWidget(self.logDockContents)
|
||||||
|
SimulationWindow.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.logDock)
|
||||||
|
|
||||||
|
self.menuBar.addAction(self.menuFile.menuAction())
|
||||||
|
self.menuBar.addAction(self.menuView.menuAction())
|
||||||
|
self.menuBar.addAction(self.menuHelp.menuAction())
|
||||||
|
self.menuFile.addAction(self.actionOpen)
|
||||||
|
self.menuFile.addAction(self.actionSave)
|
||||||
|
self.menuFile.addSeparator()
|
||||||
|
self.menuFile.addAction(self.actionClear)
|
||||||
|
self.menuFile.addSeparator()
|
||||||
|
self.menuFile.addAction(self.actionExit)
|
||||||
|
self.menuView.addAction(self.menuPanels.menuAction())
|
||||||
|
self.menuView.addAction(self.menuToolbars.menuAction())
|
||||||
|
self.menuHelp.addAction(self.actionAbout)
|
||||||
|
self.menuHelp.addAction(self.actionAboutQt)
|
||||||
|
self.fileToolbar.addAction(self.actionOpen)
|
||||||
|
self.fileToolbar.addAction(self.actionSave)
|
||||||
|
self.fileToolbar.addAction(self.actionClear)
|
||||||
|
|
||||||
self.retranslateUi(SimulationWindow)
|
self.retranslateUi(SimulationWindow)
|
||||||
|
|
||||||
self.resultsTabs.setCurrentIndex(0)
|
|
||||||
|
|
||||||
|
|
||||||
QMetaObject.connectSlotsByName(SimulationWindow)
|
QMetaObject.connectSlotsByName(SimulationWindow)
|
||||||
# setupUi
|
# setupUi
|
||||||
|
|
||||||
def retranslateUi(self, SimulationWindow):
|
def retranslateUi(self, SimulationWindow):
|
||||||
SimulationWindow.setWindowTitle(QCoreApplication.translate("SimulationWindow", u"Simulation", None))
|
SimulationWindow.setWindowTitle(QCoreApplication.translate("SimulationWindow", u"Simulation", None))
|
||||||
|
self.actionOpen.setText(QCoreApplication.translate("SimulationWindow", u"&Open\u2026", None))
|
||||||
|
#if QT_CONFIG(shortcut)
|
||||||
|
self.actionOpen.setShortcut(QCoreApplication.translate("SimulationWindow", u"Ctrl+O", None))
|
||||||
|
#endif // QT_CONFIG(shortcut)
|
||||||
|
self.actionSave.setText(QCoreApplication.translate("SimulationWindow", u"&Save\u2026", None))
|
||||||
|
#if QT_CONFIG(shortcut)
|
||||||
|
self.actionSave.setShortcut(QCoreApplication.translate("SimulationWindow", u"Ctrl+S", None))
|
||||||
|
#endif // QT_CONFIG(shortcut)
|
||||||
|
self.actionClear.setText(QCoreApplication.translate("SimulationWindow", u"&Clear", None))
|
||||||
|
self.actionExit.setText(QCoreApplication.translate("SimulationWindow", u"E&xit", None))
|
||||||
|
#if QT_CONFIG(shortcut)
|
||||||
|
self.actionExit.setShortcut(QCoreApplication.translate("SimulationWindow", u"Ctrl+W", None))
|
||||||
|
#endif // QT_CONFIG(shortcut)
|
||||||
|
self.actionAbout.setText(QCoreApplication.translate("SimulationWindow", u"&About Simulation Window", None))
|
||||||
|
self.actionAboutQt.setText(QCoreApplication.translate("SimulationWindow", u"About &Qt", None))
|
||||||
|
self.menuFile.setTitle(QCoreApplication.translate("SimulationWindow", u"&File", None))
|
||||||
|
self.menuView.setTitle(QCoreApplication.translate("SimulationWindow", u"&View", None))
|
||||||
|
self.menuPanels.setTitle(QCoreApplication.translate("SimulationWindow", u"&Panels", None))
|
||||||
|
self.menuToolbars.setTitle(QCoreApplication.translate("SimulationWindow", u"&Toolbars", None))
|
||||||
|
self.menuHelp.setTitle(QCoreApplication.translate("SimulationWindow", u"&Help", None))
|
||||||
|
self.fileToolbar.setWindowTitle(QCoreApplication.translate("SimulationWindow", u"File", None))
|
||||||
|
self.resultsDock.setWindowTitle(QCoreApplication.translate("SimulationWindow", u"Results", None))
|
||||||
|
self.resultsPlaceholder.setText(QCoreApplication.translate("SimulationWindow", u"Simulation graphs and result controls can be added here.", None))
|
||||||
|
self.statusDock.setWindowTitle(QCoreApplication.translate("SimulationWindow", u"Status", None))
|
||||||
self.statusLabel.setText(QCoreApplication.translate("SimulationWindow", u"No simulation has been run yet.", None))
|
self.statusLabel.setText(QCoreApplication.translate("SimulationWindow", u"No simulation has been run yet.", None))
|
||||||
self.progressBar.setFormat(QCoreApplication.translate("SimulationWindow", u"%p%", None))
|
self.progressBar.setFormat(QCoreApplication.translate("SimulationWindow", u"%p%", None))
|
||||||
self.timeLabel.setText(QCoreApplication.translate("SimulationWindow", u"Time: 0 s", None))
|
self.timeLabel.setText(QCoreApplication.translate("SimulationWindow", u"Time: 0 s", None))
|
||||||
self.resultsTabs.setTabText(self.resultsTabs.indexOf(self.logTab), QCoreApplication.translate("SimulationWindow", u"Simulation Log", None))
|
self.logDock.setWindowTitle(QCoreApplication.translate("SimulationWindow", u"Log", None))
|
||||||
self.resultsPlaceholder.setText(QCoreApplication.translate("SimulationWindow", u"Simulation graphs and result controls can be added here.", None))
|
|
||||||
self.resultsTabs.setTabText(self.resultsTabs.indexOf(self.resultsTab), QCoreApplication.translate("SimulationWindow", u"Results", None))
|
|
||||||
# retranslateUi
|
# retranslateUi
|
||||||
|
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ class MainWindow(QMainWindow):
|
|||||||
)
|
)
|
||||||
self.ui.actionGraphParameters.triggered.connect(self.show_graph_parameters)
|
self.ui.actionGraphParameters.triggered.connect(self.show_graph_parameters)
|
||||||
self.ui.actionCompose.triggered.connect(self.compose_active_graph)
|
self.ui.actionCompose.triggered.connect(self.compose_active_graph)
|
||||||
|
self.ui.actionSimulationWindow.triggered.connect(self.show_simulation_window)
|
||||||
self.ui.actionRunSimulation.triggered.connect(self.run_simulation)
|
self.ui.actionRunSimulation.triggered.connect(self.run_simulation)
|
||||||
self.document_controller.undo_stack.canUndoChanged.connect(self.ui.actionUndo.setEnabled)
|
self.document_controller.undo_stack.canUndoChanged.connect(self.ui.actionUndo.setEnabled)
|
||||||
self.document_controller.undo_stack.canRedoChanged.connect(self.ui.actionRedo.setEnabled)
|
self.document_controller.undo_stack.canRedoChanged.connect(self.ui.actionRedo.setEnabled)
|
||||||
@@ -248,15 +249,20 @@ class MainWindow(QMainWindow):
|
|||||||
self.log.error("Composition failed: %s", error)
|
self.log.error("Composition failed: %s", error)
|
||||||
QMessageBox.warning(self, "Cannot compose", str(error))
|
QMessageBox.warning(self, "Cannot compose", str(error))
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def show_simulation_window(self) -> None:
|
||||||
|
self._simulation_window.show()
|
||||||
|
self._simulation_window.raise_()
|
||||||
|
self._simulation_window.activateWindow()
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def run_simulation(self) -> None:
|
def run_simulation(self) -> None:
|
||||||
window = self._simulation_window
|
window = self._simulation_window
|
||||||
callbacks = window.begin_run()
|
callbacks = window.begin_run()
|
||||||
window.show()
|
self.show_simulation_window()
|
||||||
window.raise_()
|
|
||||||
window.activateWindow()
|
|
||||||
try:
|
try:
|
||||||
self.document_controller.run_simulation(*callbacks)
|
self.document_controller.run_simulation(*callbacks)
|
||||||
|
window.set_model_name(self.simulation.model_name)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
self.log.exception("Simulation run failed")
|
self.log.exception("Simulation run failed")
|
||||||
window.report_start_error(error)
|
window.report_start_error(error)
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from PySide6.QtCore import Signal
|
from PySide6.QtCore import Signal
|
||||||
from PySide6.QtWidgets import QMainWindow
|
from PySide6.QtWidgets import QFileDialog, QMainWindow, QMessageBox
|
||||||
|
|
||||||
from bedit.core.simulation.openmodelica import SimulationMessage, SimulationProgress
|
from bedit.core.simulation.openmodelica import SimulationMessage, SimulationProgress
|
||||||
|
from bedit.core.simulation.results import (
|
||||||
|
SimulationResults,
|
||||||
|
load_simulation_results,
|
||||||
|
save_simulation_results,
|
||||||
|
)
|
||||||
from bedit.gui.generated.ui_simulation_window import Ui_SimulationWindow
|
from bedit.gui.generated.ui_simulation_window import Ui_SimulationWindow
|
||||||
|
|
||||||
|
|
||||||
class SimulationWindow(QMainWindow):
|
class SimulationWindow(QMainWindow):
|
||||||
"""Persistent, reusable view of simulation progress and results."""
|
"""Persistent viewer for live and previously saved simulation results."""
|
||||||
|
|
||||||
progressReceived = Signal(object)
|
progressReceived = Signal(object)
|
||||||
messageReceived = Signal(object)
|
messageReceived = Signal(object)
|
||||||
@@ -19,22 +27,37 @@ class SimulationWindow(QMainWindow):
|
|||||||
self.ui.setupUi(self)
|
self.ui.setupUi(self)
|
||||||
self._running = False
|
self._running = False
|
||||||
self._run_generation = 0
|
self._run_generation = 0
|
||||||
|
self._file_path: Path | None = None
|
||||||
|
self.results = SimulationResults()
|
||||||
|
self._connect_actions()
|
||||||
|
self._populate_view_menu()
|
||||||
self.progressReceived.connect(self._show_progress)
|
self.progressReceived.connect(self._show_progress)
|
||||||
self.messageReceived.connect(self._show_message)
|
self.messageReceived.connect(self._show_message)
|
||||||
self.simulationFinished.connect(self._show_finished)
|
self.simulationFinished.connect(self._show_finished)
|
||||||
self.simulationFailed.connect(self._show_error)
|
self.simulationFailed.connect(self._show_error)
|
||||||
|
|
||||||
def begin_run(self) -> tuple:
|
def _connect_actions(self) -> None:
|
||||||
"""Reset the view and return callbacks bound to this particular run."""
|
self.ui.actionOpen.triggered.connect(self.open_results)
|
||||||
|
self.ui.actionSave.triggered.connect(self.save_results)
|
||||||
|
self.ui.actionClear.triggered.connect(self.clear)
|
||||||
|
self.ui.actionExit.triggered.connect(self.close)
|
||||||
|
self.ui.actionAbout.triggered.connect(self.show_about)
|
||||||
|
self.ui.actionAboutQt.triggered.connect(
|
||||||
|
lambda: QMessageBox.aboutQt(self, "About Qt Framework")
|
||||||
|
)
|
||||||
|
|
||||||
self._run_generation += 1
|
def _populate_view_menu(self) -> None:
|
||||||
|
for panel in (self.ui.statusDock, self.ui.logDock, self.ui.resultsDock):
|
||||||
|
self.ui.menuPanels.addAction(panel.toggleViewAction())
|
||||||
|
self.ui.menuToolbars.addAction(self.ui.fileToolbar.toggleViewAction())
|
||||||
|
|
||||||
|
def begin_run(self, model_name: str = "") -> tuple:
|
||||||
|
"""Reset the viewer and return callbacks bound to this run."""
|
||||||
|
|
||||||
|
self.clear(model_name=model_name)
|
||||||
generation = self._run_generation
|
generation = self._run_generation
|
||||||
self._running = True
|
self._running = True
|
||||||
self.ui.statusLabel.setText("Preparing simulation…")
|
self.ui.statusLabel.setText("Preparing simulation…")
|
||||||
self.ui.progressBar.setValue(0)
|
|
||||||
self.ui.timeLabel.setText("Time: 0 s")
|
|
||||||
self.ui.messageList.clear()
|
|
||||||
self.clear_results()
|
|
||||||
return (
|
return (
|
||||||
lambda progress: self._report_progress(generation, progress),
|
lambda progress: self._report_progress(generation, progress),
|
||||||
lambda message: self._report_message(generation, message),
|
lambda message: self._report_message(generation, message),
|
||||||
@@ -42,17 +65,110 @@ class SimulationWindow(QMainWindow):
|
|||||||
lambda error: self._report_error(generation, error),
|
lambda error: self._report_error(generation, error),
|
||||||
)
|
)
|
||||||
|
|
||||||
def clear_results(self) -> None:
|
def set_model_name(self, model_name: str | None) -> None:
|
||||||
"""Clear future plots and result models before a new run.
|
if model_name:
|
||||||
|
self.results.model_name = model_name
|
||||||
|
self._update_title()
|
||||||
|
|
||||||
Add graph widgets and their clearing logic here when simulation result
|
def clear(self, checked: bool = False, *, model_name: str = "") -> None:
|
||||||
loading and plotting are implemented. The Designer-owned `resultsLayout`
|
"""Discard the displayed run and prepare an empty results document."""
|
||||||
is the intended container for those widgets.
|
|
||||||
|
del checked
|
||||||
|
self._run_generation += 1
|
||||||
|
self._running = False
|
||||||
|
self._file_path = None
|
||||||
|
self.results = SimulationResults(model_name=model_name)
|
||||||
|
self.ui.statusLabel.setText("No simulation has been run yet.")
|
||||||
|
self.ui.progressBar.setValue(0)
|
||||||
|
self.ui.timeLabel.setText("Time: 0 s")
|
||||||
|
self.ui.messageList.clear()
|
||||||
|
self.clear_result_views()
|
||||||
|
self._update_title()
|
||||||
|
|
||||||
|
def clear_result_views(self) -> None:
|
||||||
|
"""Clear custom plots before a run or loaded document is displayed.
|
||||||
|
|
||||||
|
Future graph widgets should be placed in the Designer-owned
|
||||||
|
``resultsLayout`` and reset here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _report_progress(
|
self.ui.resultsPlaceholder.setText(
|
||||||
self, generation: int, progress: SimulationProgress
|
"Simulation graphs and result controls can be added here."
|
||||||
) -> None:
|
)
|
||||||
|
|
||||||
|
def load_result_views(self) -> None:
|
||||||
|
"""Populate custom plots from ``self.results.traces``.
|
||||||
|
|
||||||
|
This is the intended integration point for a future plotting widget.
|
||||||
|
"""
|
||||||
|
|
||||||
|
trace_count = len(self.results.traces)
|
||||||
|
if trace_count:
|
||||||
|
self.ui.resultsPlaceholder.setText(
|
||||||
|
f"{trace_count} trace(s) loaded; add graph rendering here."
|
||||||
|
)
|
||||||
|
|
||||||
|
def open_results(self) -> None:
|
||||||
|
file_name, _selected_filter = QFileDialog.getOpenFileName(
|
||||||
|
self,
|
||||||
|
"Open Simulation Results",
|
||||||
|
"",
|
||||||
|
"BEdit Simulation Results (*.json);;JSON Files (*.json)",
|
||||||
|
)
|
||||||
|
if not file_name:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
results = load_simulation_results(file_name)
|
||||||
|
except ValueError as error:
|
||||||
|
QMessageBox.warning(self, "Cannot Open Simulation Results", str(error))
|
||||||
|
return
|
||||||
|
self._run_generation += 1
|
||||||
|
self._running = False
|
||||||
|
self._file_path = Path(file_name)
|
||||||
|
self.results = results
|
||||||
|
self._display_results()
|
||||||
|
|
||||||
|
def save_results(self) -> None:
|
||||||
|
file_path = self._file_path
|
||||||
|
if file_path is None:
|
||||||
|
default_name = f"{_safe_file_stem(self.results.model_name)}-results.json"
|
||||||
|
file_name, _selected_filter = QFileDialog.getSaveFileName(
|
||||||
|
self,
|
||||||
|
"Save Simulation Results",
|
||||||
|
default_name,
|
||||||
|
"BEdit Simulation Results (*.json);;JSON Files (*.json)",
|
||||||
|
)
|
||||||
|
if not file_name:
|
||||||
|
return
|
||||||
|
file_path = Path(file_name)
|
||||||
|
if file_path.suffix.lower() != ".json":
|
||||||
|
file_path = file_path.with_suffix(".json")
|
||||||
|
try:
|
||||||
|
save_simulation_results(file_path, self.results)
|
||||||
|
except OSError as error:
|
||||||
|
QMessageBox.warning(self, "Cannot Save Simulation Results", str(error))
|
||||||
|
return
|
||||||
|
self._file_path = file_path
|
||||||
|
self._update_title()
|
||||||
|
|
||||||
|
def _display_results(self) -> None:
|
||||||
|
status = self.results.status
|
||||||
|
self.ui.statusLabel.setText(str(status.get("phase", "Loaded results")))
|
||||||
|
self.ui.progressBar.setValue(int(status.get("progress", 0)))
|
||||||
|
self.ui.timeLabel.setText(f"Time: {float(status.get('time', 0)):g} s")
|
||||||
|
self.ui.messageList.clear()
|
||||||
|
for message in self.results.messages:
|
||||||
|
prefix = message.get("stream") or message.get("type") or "OpenModelica"
|
||||||
|
self.ui.messageList.addItem(f"{prefix}: {message.get('text', '')}")
|
||||||
|
self.clear_result_views()
|
||||||
|
self.load_result_views()
|
||||||
|
self._update_title()
|
||||||
|
|
||||||
|
def _update_title(self) -> None:
|
||||||
|
name = self.results.model_name or "Simulation"
|
||||||
|
self.setWindowTitle(f"{name} — Simulation")
|
||||||
|
|
||||||
|
def _report_progress(self, generation: int, progress: SimulationProgress) -> None:
|
||||||
if generation == self._run_generation:
|
if generation == self._run_generation:
|
||||||
self.progressReceived.emit(progress)
|
self.progressReceived.emit(progress)
|
||||||
|
|
||||||
@@ -69,34 +185,57 @@ class SimulationWindow(QMainWindow):
|
|||||||
self.simulationFailed.emit(str(error))
|
self.simulationFailed.emit(str(error))
|
||||||
|
|
||||||
def report_start_error(self, error: Exception) -> None:
|
def report_start_error(self, error: Exception) -> None:
|
||||||
"""Report an error raised before asynchronous callbacks were installed."""
|
|
||||||
|
|
||||||
self.simulationFailed.emit(str(error))
|
self.simulationFailed.emit(str(error))
|
||||||
|
|
||||||
def _show_progress(self, progress: SimulationProgress) -> None:
|
def _show_progress(self, progress: SimulationProgress) -> None:
|
||||||
|
self.results.status = {
|
||||||
|
"phase": progress.phase,
|
||||||
|
"currentStepSize": progress.current_step_size,
|
||||||
|
"time": progress.time,
|
||||||
|
"progress": progress.progress,
|
||||||
|
}
|
||||||
self.ui.statusLabel.setText(progress.phase or "Running")
|
self.ui.statusLabel.setText(progress.phase or "Running")
|
||||||
self.ui.timeLabel.setText(f"Time: {progress.time:g} s")
|
self.ui.timeLabel.setText(f"Time: {progress.time:g} s")
|
||||||
self.ui.progressBar.setValue(max(0, min(10000, progress.progress)))
|
self.ui.progressBar.setValue(max(0, min(10000, progress.progress)))
|
||||||
|
|
||||||
def _show_message(self, message: SimulationMessage) -> None:
|
def _show_message(self, message: SimulationMessage) -> None:
|
||||||
|
self.results.messages.append(
|
||||||
|
{"stream": message.stream, "type": message.type, "text": message.text}
|
||||||
|
)
|
||||||
prefix = message.stream or message.type or "OpenModelica"
|
prefix = message.stream or message.type or "OpenModelica"
|
||||||
self.ui.messageList.addItem(f"{prefix}: {message.text}")
|
self.ui.messageList.addItem(f"{prefix}: {message.text}")
|
||||||
self.ui.messageList.scrollToBottom()
|
self.ui.messageList.scrollToBottom()
|
||||||
|
|
||||||
def _show_finished(self, _result) -> None:
|
def _show_finished(self, result) -> None:
|
||||||
self._running = False
|
self._running = False
|
||||||
|
self.results.status.update(phase="Simulation finished", progress=10000)
|
||||||
|
self.results.metadata["processResult"] = result
|
||||||
self.ui.progressBar.setValue(10000)
|
self.ui.progressBar.setValue(10000)
|
||||||
self.ui.statusLabel.setText("Simulation finished")
|
self.ui.statusLabel.setText("Simulation finished")
|
||||||
self.load_results()
|
self.load_result_views()
|
||||||
|
|
||||||
def _show_error(self, message: str) -> None:
|
def _show_error(self, message: str) -> None:
|
||||||
self._running = False
|
self._running = False
|
||||||
|
self.results.status["phase"] = "Simulation failed"
|
||||||
|
self.results.messages.append(
|
||||||
|
{"stream": "BEdit", "type": "error", "text": message}
|
||||||
|
)
|
||||||
self.ui.statusLabel.setText("Simulation failed")
|
self.ui.statusLabel.setText("Simulation failed")
|
||||||
self.ui.messageList.addItem(f"Error: {message}")
|
self.ui.messageList.addItem(f"Error: {message}")
|
||||||
|
|
||||||
def load_results(self) -> None:
|
def show_about(self) -> None:
|
||||||
"""Populate future plots and result controls after a successful run."""
|
QMessageBox.about(
|
||||||
|
self,
|
||||||
|
"About BEdit Simulation",
|
||||||
|
"<h3>BEdit Simulation</h3>"
|
||||||
|
"<p>View live progress and open or save simulation results.</p>",
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_running(self) -> bool:
|
def is_running(self) -> bool:
|
||||||
return self._running
|
return self._running
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_file_stem(model_name: str) -> str:
|
||||||
|
stem = re.sub(r"[^A-Za-z0-9_.-]+", "_", model_name).strip("._")
|
||||||
|
return stem or "simulation"
|
||||||
|
|||||||
@@ -552,6 +552,7 @@
|
|||||||
<addaction name="actionSimulationSettings"/>
|
<addaction name="actionSimulationSettings"/>
|
||||||
<addaction name="actionGraphParameters"/>
|
<addaction name="actionGraphParameters"/>
|
||||||
<addaction name="actionCompose"/>
|
<addaction name="actionCompose"/>
|
||||||
|
<addaction name="actionSimulationWindow"/>
|
||||||
<addaction name="actionRunSimulation"/>
|
<addaction name="actionRunSimulation"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="menuFile"/>
|
<addaction name="menuFile"/>
|
||||||
@@ -645,6 +646,7 @@
|
|||||||
<addaction name="actionSimulationSettings"/>
|
<addaction name="actionSimulationSettings"/>
|
||||||
<addaction name="actionGraphParameters"/>
|
<addaction name="actionGraphParameters"/>
|
||||||
<addaction name="actionCompose"/>
|
<addaction name="actionCompose"/>
|
||||||
|
<addaction name="actionSimulationWindow"/>
|
||||||
<addaction name="actionRunSimulation"/>
|
<addaction name="actionRunSimulation"/>
|
||||||
</widget>
|
</widget>
|
||||||
<action name="actionSimulationSettings">
|
<action name="actionSimulationSettings">
|
||||||
@@ -701,6 +703,18 @@
|
|||||||
<string>F6</string>
|
<string>F6</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionSimulationWindow">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../resources/resources.qrc">
|
||||||
|
<normaloff>:/icons/icons/configure.png</normaloff>:/icons/icons/configure.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Simulation Window</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip">
|
||||||
|
<string>Show the simulation results window</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionNew">
|
<action name="actionNew">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../resources/resources.qrc">
|
<iconset resource="../resources/resources.qrc">
|
||||||
|
|||||||
@@ -2,90 +2,90 @@
|
|||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>SimulationWindow</class>
|
<class>SimulationWindow</class>
|
||||||
<widget class="QMainWindow" name="SimulationWindow">
|
<widget class="QMainWindow" name="SimulationWindow">
|
||||||
<property name="geometry">
|
<property name="geometry"><rect><x>0</x><y>0</y><width>900</width><height>650</height></rect></property>
|
||||||
<rect>
|
<property name="windowTitle"><string>Simulation</string></property>
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>1031</width>
|
|
||||||
<height>881</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Simulation</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="centralWidget">
|
<widget class="QWidget" name="centralWidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<property name="maximumSize"><size><width>0</width><height>0</height></size></property>
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="resultsPlaceholder">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>600</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Simulation graphs and result controls can be added here.</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
<widget class="QMenuBar" name="menuBar">
|
||||||
<item>
|
<widget class="QMenu" name="menuFile">
|
||||||
<widget class="QLabel" name="statusLabel">
|
<property name="title"><string>&File</string></property>
|
||||||
<property name="text">
|
<addaction name="actionOpen"/>
|
||||||
<string>No simulation has been run yet.</string>
|
<addaction name="actionSave"/>
|
||||||
</property>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionClear"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionExit"/>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
<widget class="QMenu" name="menuView">
|
||||||
<item>
|
<property name="title"><string>&View</string></property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<widget class="QMenu" name="menuPanels"><property name="title"><string>&Panels</string></property></widget>
|
||||||
<item>
|
<widget class="QMenu" name="menuToolbars"><property name="title"><string>&Toolbars</string></property></widget>
|
||||||
<widget class="QProgressBar" name="progressBar">
|
<addaction name="menuPanels"/>
|
||||||
<property name="maximum">
|
<addaction name="menuToolbars"/>
|
||||||
<number>10000</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="format">
|
|
||||||
<string>%p%</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
<widget class="QMenu" name="menuHelp">
|
||||||
<item>
|
<property name="title"><string>&Help</string></property>
|
||||||
<widget class="QLabel" name="timeLabel">
|
<addaction name="actionAbout"/>
|
||||||
<property name="text">
|
<addaction name="actionAboutQt"/>
|
||||||
<string>Time: 0 s</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
<addaction name="menuFile"/>
|
||||||
|
<addaction name="menuView"/>
|
||||||
|
<addaction name="menuHelp"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QToolBar" name="fileToolbar">
|
||||||
|
<property name="windowTitle"><string>File</string></property>
|
||||||
|
<property name="toolButtonStyle"><enum>Qt::ToolButtonStyle::ToolButtonIconOnly</enum></property>
|
||||||
|
<attribute name="toolBarArea"><enum>TopToolBarArea</enum></attribute>
|
||||||
|
<addaction name="actionOpen"/>
|
||||||
|
<addaction name="actionSave"/>
|
||||||
|
<addaction name="actionClear"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QDockWidget" name="resultsDock">
|
||||||
|
<property name="windowTitle"><string>Results</string></property>
|
||||||
|
<attribute name="dockWidgetArea"><number>2</number></attribute>
|
||||||
|
<widget class="QWidget" name="resultsDockContents">
|
||||||
|
<layout class="QVBoxLayout" name="resultsLayout">
|
||||||
|
<item><widget class="QLabel" name="resultsPlaceholder"><property name="text"><string>Simulation graphs and result controls can be added here.</string></property><property name="alignment"><set>Qt::AlignmentFlag::AlignCenter</set></property></widget></item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</widget>
|
||||||
<item>
|
</widget>
|
||||||
<widget class="QTabWidget" name="resultsTabs">
|
<widget class="QDockWidget" name="statusDock">
|
||||||
<property name="currentIndex">
|
<property name="windowTitle"><string>Status</string></property>
|
||||||
<number>0</number>
|
<attribute name="dockWidgetArea"><number>8</number></attribute>
|
||||||
</property>
|
<widget class="QWidget" name="statusDockContents">
|
||||||
<widget class="QWidget" name="logTab">
|
<layout class="QVBoxLayout" name="statusLayout">
|
||||||
<attribute name="title">
|
<item><widget class="QLabel" name="statusLabel"><property name="text"><string>No simulation has been run yet.</string></property></widget></item>
|
||||||
<string>Simulation Log</string>
|
<item><widget class="QProgressBar" name="progressBar"><property name="maximum"><number>10000</number></property><property name="value"><number>0</number></property><property name="format"><string>%p%</string></property></widget></item>
|
||||||
</attribute>
|
<item><widget class="QLabel" name="timeLabel"><property name="text"><string>Time: 0 s</string></property></widget></item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QDockWidget" name="logDock">
|
||||||
|
<property name="windowTitle"><string>Log</string></property>
|
||||||
|
<attribute name="dockWidgetArea"><number>8</number></attribute>
|
||||||
|
<widget class="QWidget" name="logDockContents">
|
||||||
<layout class="QVBoxLayout" name="logLayout">
|
<layout class="QVBoxLayout" name="logLayout">
|
||||||
<item>
|
<item><widget class="QListWidget" name="messageList"><property name="alternatingRowColors"><bool>true</bool></property></widget></item>
|
||||||
<widget class="QListWidget" name="messageList">
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
<action name="actionOpen">
|
||||||
</layout>
|
<property name="icon"><iconset resource="../resources/resources.qrc"><normaloff>:/icons/icons/document-open.png</normaloff>:/icons/icons/document-open.png</iconset></property>
|
||||||
|
<property name="text"><string>&Open…</string></property><property name="shortcut"><string>Ctrl+O</string></property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSave">
|
||||||
|
<property name="icon"><iconset resource="../resources/resources.qrc"><normaloff>:/icons/icons/document-save.png</normaloff>:/icons/icons/document-save.png</iconset></property>
|
||||||
|
<property name="text"><string>&Save…</string></property><property name="shortcut"><string>Ctrl+S</string></property>
|
||||||
|
</action>
|
||||||
|
<action name="actionClear">
|
||||||
|
<property name="icon"><iconset resource="../resources/resources.qrc"><normaloff>:/icons/icons/document-new.png</normaloff>:/icons/icons/document-new.png</iconset></property>
|
||||||
|
<property name="text"><string>&Clear</string></property>
|
||||||
|
</action>
|
||||||
|
<action name="actionExit"><property name="text"><string>E&xit</string></property><property name="shortcut"><string>Ctrl+W</string></property></action>
|
||||||
|
<action name="actionAbout"><property name="text"><string>&About Simulation Window</string></property></action>
|
||||||
|
<action name="actionAboutQt"><property name="text"><string>About &Qt</string></property></action>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
<resources><include location="../resources/resources.qrc"/></resources>
|
||||||
<resources/>
|
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
"showName": false
|
"showName": false
|
||||||
},
|
},
|
||||||
"library": {
|
"library": {
|
||||||
"showSubtree": false
|
"showSubtree": true
|
||||||
},
|
},
|
||||||
"implementation": {
|
"implementation": {
|
||||||
"kind": "graph",
|
"kind": "graph",
|
||||||
|
|||||||
Reference in New Issue
Block a user