diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 663bf51..ffa90e1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,6 +46,7 @@ qt_add_qml_module(appOPC_UA_Browser qml/controls/StyledMenuItem.qml qml/controls/StyledMenuSeparator.qml qml/controls/StyledScrollBar.qml + qml/controls/StyledSpinBox.qml qml/controls/StyledTabButton.qml qml/controls/StyledTextField.qml qml/style/Style.qml @@ -73,6 +74,7 @@ qt_add_qml_module(appOPC_UA_Browser qml/style/ThemeScrollBar.qml qml/style/ThemeSettingsView.qml qml/style/ThemeSideMenu.qml + qml/style/ThemeSpinBox.qml qml/style/ThemeTabButton.qml qml/style/ThemeTextField.qml SOURCES attribute.h attribute.cpp @@ -128,6 +130,7 @@ qt_add_resources(appOPC_UA_Browser "icons" icons/menu.svg icons/plus.svg icons/refresh.svg + icons/minus.svg icons/save.svg icons/settings.svg languages/English.png diff --git a/src/backend.cpp b/src/backend.cpp index 9b0a211..fb3ca00 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -82,6 +82,11 @@ BackEnd::BackEnd(QObject *parent) //! [Application Identity] QSettings settings; + + mMaxEventsPerObject = settings.value(Constants::SettingsKey::MaxEventPerObject, + Constants::Defaults::MaxEventsPerObject) + .toInt(); + settings.beginGroup(Constants::SettingsKey::DashboardsVariables); QStringList keys = settings.childKeys(); settings.endGroup(); @@ -216,6 +221,13 @@ const QVector &BackEnd::companionSpecDevices() const noexce } OpcUaModel *BackEnd::getOpcUaModelForNode(QOpcUaNode *node) +{ + const auto backend = getBackEndForNode(node); + + return backend ? backend->mOpcUaModel : nullptr; +} + +BackEnd *BackEnd::getBackEndForNode(QOpcUaNode *node) { if (!node) return nullptr; @@ -224,7 +236,7 @@ OpcUaModel *BackEnd::getOpcUaModelForNode(QOpcUaNode *node) if (entry == mBackendMapping.constEnd()) return nullptr; - return entry.value() ? entry.value()->mOpcUaModel : nullptr; + return entry.value(); } QOpcUaClient *BackEnd::getOpcUaClient() @@ -1101,6 +1113,24 @@ QFuture BackEnd::findAllSubtypes(const QString &nodeId, return future; } +int BackEnd::maxEventsPerObject() const +{ + return mMaxEventsPerObject; +} + +void BackEnd::setMaxEventsPerObject(int newMaxEventsPerObject) +{ + if (mMaxEventsPerObject == newMaxEventsPerObject) + return; + + mMaxEventsPerObject = newMaxEventsPerObject; + + QSettings settings; + settings.setValue(Constants::SettingsKey::MaxEventPerObject, newMaxEventsPerObject); + + emit maxEventsPerObjectChanged(); +} + CompanionSpecDevice *BackEnd::getCompanionSpecDeviceForNodeId(const QString &nodeId) { const auto entry = std::find_if( diff --git a/src/backend.h b/src/backend.h index 6d30bd4..c5e5b5e 100644 --- a/src/backend.h +++ b/src/backend.h @@ -143,6 +143,9 @@ class BackEnd : public QObject Q_PROPERTY(QVector companionSpecDevices READ companionSpecDevices NOTIFY companionSpecDevicesChanged FINAL) + Q_PROPERTY(int maxEventsPerObject READ maxEventsPerObject WRITE setMaxEventsPerObject NOTIFY + maxEventsPerObjectChanged FINAL) + explicit BackEnd(QObject *parent = nullptr); ~BackEnd(); @@ -178,6 +181,7 @@ class BackEnd : public QObject int instantiateCompanionSpecEventDashboard(const QString &name); static OpcUaModel *getOpcUaModelForNode(QOpcUaNode *node); + static BackEnd *getBackEndForNode(QOpcUaNode *node); QOpcUaClient *getOpcUaClient(); void addDefaultVariableDashboard(const QString &name); @@ -219,6 +223,9 @@ class BackEnd : public QObject Q_INVOKABLE void removeRecentConnection(const QString &name); + int maxEventsPerObject() const; + void setMaxEventsPerObject(int newMaxEventsPerObject); + signals: void recentConnectionsChanged(); void serverListChanged(); @@ -238,6 +245,8 @@ class BackEnd : public QObject void companionSpecDevicesChanged(); + void maxEventsPerObjectChanged(); + private slots: void findServersComplete(const QList &servers, QOpcUa::UaStatusCode statusCode); @@ -314,6 +323,8 @@ private slots: QHash> mCompanionSpecEventDashboards; QStringList mSelectedEventSourceNodes; + + int mMaxEventsPerObject = 0; }; #endif // BACKEND_H diff --git a/src/constants.cpp b/src/constants.cpp index 500fcc1..564361c 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -37,6 +37,7 @@ const QString SettingsKey::EventFilters = QStringLiteral("eventFilters"); const QString SettingsKey::RecentConnections = QStringLiteral("recentConnections"); const QString SettingsKey::Url = QStringLiteral("url"); const QString SettingsKey::Language = QStringLiteral("language"); +const QString SettingsKey::MaxEventPerObject = QStringLiteral("maxEventsPerObject"); const QString CertInfo::CommonName = QStringLiteral("OpcUaBrowser"); const QString CertInfo::CountryName = QStringLiteral("DE"); diff --git a/src/constants.h b/src/constants.h index 32d5a8e..a3dfaec 100644 --- a/src/constants.h +++ b/src/constants.h @@ -48,6 +48,7 @@ class SettingsKey const static QString RecentConnections; const static QString Url; const static QString Language; + const static QString MaxEventPerObject; }; class CertInfo @@ -68,6 +69,12 @@ class NamespaceUri const static QString Woodworking; const static QString Machinery; }; + +class Defaults +{ +public: + const static int MaxEventsPerObject = 25; +}; } // namespace Constants #endif // CONSTANTS_H diff --git a/src/icons/minus.svg b/src/icons/minus.svg new file mode 100644 index 0000000..b1312e3 --- /dev/null +++ b/src/icons/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/minus.svg.license b/src/icons/minus.svg.license new file mode 100644 index 0000000..c6127c5 --- /dev/null +++ b/src/icons/minus.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Google LLC + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/languages/English_en_GB.ts b/src/languages/English_en_GB.ts index fd308f4..a5a9b6c 100644 --- a/src/languages/English_en_GB.ts +++ b/src/languages/English_en_GB.ts @@ -454,5 +454,9 @@ Enter new dashboard name + + Max. events per object + + diff --git a/src/languages/German_de_DE.ts b/src/languages/German_de_DE.ts index 87bc109..1910d44 100644 --- a/src/languages/German_de_DE.ts +++ b/src/languages/German_de_DE.ts @@ -454,5 +454,9 @@ Enter new dashboard name Neuen Dashboardnamen eingeben + + Max. events per object + Max. Events pro Objekt + diff --git a/src/monitoreditem.cpp b/src/monitoreditem.cpp index 4f56948..2bc8940 100644 --- a/src/monitoreditem.cpp +++ b/src/monitoreditem.cpp @@ -9,6 +9,8 @@ #include #include +#include "backend.h" +#include "constants.h" #include "monitoreditem.h" #include "opcuahelper.h" @@ -145,8 +147,13 @@ void MonitoredItem::handleEvent(const QVariantList &eventFields) } mLastEvents.push_front(eventStrings); - if (mLastEvents.length() > 25) - mLastEvents.pop_back(); + + const auto backend = BackEnd::getBackEndForNode(mOpcNode.get()); + const auto maxEvents = + backend ? backend->maxEventsPerObject() : Constants::Defaults::MaxEventsPerObject; + + if (mLastEvents.length() > maxEvents) + mLastEvents.resize(maxEvents); emit lastEventsChanged(); } diff --git a/src/qml/SettingsView.qml b/src/qml/SettingsView.qml index d1edcf7..6310006 100644 --- a/src/qml/SettingsView.qml +++ b/src/qml/SettingsView.qml @@ -507,6 +507,29 @@ Rectangle { } } + // Dashboard settings + Column { + width: parent.width - content.leftPadding - content.rightPadding + spacing: 5 + + Text { + color: view.theme.textColor + font { + pointSize: 14 + bold: true + } + text: qsTranslate("Dashboard", "Dashboard") + } + + StyledSpinBox { + captionText: qsTranslate("Settings", "Max. events per object") + from: 1 + to: 150 + value: BackEnd.maxEventsPerObject + onValueChanged: BackEnd.maxEventsPerObject = value + } + } + // Certificate list view Column { width: parent.width - content.leftPadding - content.rightPadding diff --git a/src/qml/controls/StyledSpinBox.qml b/src/qml/controls/StyledSpinBox.qml new file mode 100644 index 0000000..cae8f23 --- /dev/null +++ b/src/qml/controls/StyledSpinBox.qml @@ -0,0 +1,73 @@ +/** + * SPDX-FileCopyrightText: 2024 basysKom GmbH + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import OPC_UA_Browser + +ColumnLayout { + id: layout + + property ThemeSpinBox theme: Style.spinBox + property alias captionText: caption.text + property alias from: spinBox.from + property alias to: spinBox.to + property alias value: spinBox.value + + Text { + id: caption + + verticalAlignment: Qt.AlignVCenter + color: layout.theme.textColor + font.bold: true + } + + SpinBox { + id: spinBox + height: 30 + + background: Rectangle { + implicitWidth: 100 + color: "transparent" + border.color: "transparent" + border.width: 0 + } + + contentItem: TextInput { + z: 2 + text: spinBox.textFromValue(spinBox.value, spinBox.locale) + + font: spinBox.font + color: theme.textColor + selectionColor: theme.textBackgroundSelected + selectedTextColor: theme.textColor + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + + readOnly: !spinBox.editable + validator: spinBox.validator + inputMethodHints: Qt.ImhFormattedNumbersOnly + } + + up.indicator: IconImage { + x: spinBox.mirrored ? 0 : parent.width - width + sourceSize.width: 30 + sourceSize.height: 30 + source: "qrc:/icons/plus.svg" + color: spinBox.up.pressed || spinBox.to == value ? theme.buttonBackgroundSelected : theme.textColor + } + + down.indicator: IconImage { + x: spinBox.mirrored ? parent.width - width : 0 + sourceSize.width: 30 + sourceSize.height: 30 + source: "qrc:/icons/minus.svg" + color: spinBox.down.pressed || spinBox.from == value ? theme.buttonBackgroundSelected : theme.textColor + } + } +} diff --git a/src/qml/style/Style.qml b/src/qml/style/Style.qml index 09d99b9..2bedd1f 100644 --- a/src/qml/style/Style.qml +++ b/src/qml/style/Style.qml @@ -31,6 +31,7 @@ ThemeDefault { listView: currentTheme.listView button: currentTheme.button comboBox: currentTheme.comboBox + spinBox: currentTheme.spinBox textField: currentTheme.textField tabButton: currentTheme.tabButton iconTabButton: currentTheme.iconTabButton diff --git a/src/qml/style/ThemeBright.qml b/src/qml/style/ThemeBright.qml index c141727..fd33282 100644 --- a/src/qml/style/ThemeBright.qml +++ b/src/qml/style/ThemeBright.qml @@ -99,6 +99,16 @@ ThemeDefault { background: mediumLight } + spinBox { + textColor: anthrazite + + buttonBackground: foreground + buttonBackgroundSelected: mediumLight + + textBackground: foreground + textBackgroundSelected: mediumLight + } + textField { captionTextColor: anthrazite background: foreground diff --git a/src/qml/style/ThemeDefault.qml b/src/qml/style/ThemeDefault.qml index d0af864..172146d 100644 --- a/src/qml/style/ThemeDefault.qml +++ b/src/qml/style/ThemeDefault.qml @@ -28,4 +28,5 @@ StyleDefinitions { property ThemeIconTabButton iconTabButton: ThemeIconTabButton {} property ThemeScrollBar scrollBar: ThemeScrollBar {} property ThemeItemSelector itemSelector: ThemeItemSelector {} + property ThemeSpinBox spinBox: ThemeSpinBox {} } diff --git a/src/qml/style/ThemeSpinBox.qml b/src/qml/style/ThemeSpinBox.qml new file mode 100644 index 0000000..747fdc1 --- /dev/null +++ b/src/qml/style/ThemeSpinBox.qml @@ -0,0 +1,17 @@ +/** + * SPDX-FileCopyrightText: 2024 basysKom GmbH + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import QtQuick + +StyleDefinitions { + property color textColor: foreground + + property color buttonBackground: dark + property color buttonBackgroundSelected: mediumDark + + property color textBackground: dark + property color textBackgroundSelected: mediumDark +}