From 9da757e3b416faa10c88b0acfb1d264371b609fd Mon Sep 17 00:00:00 2001 From: JesusPoderoso Date: Wed, 23 Aug 2023 12:46:03 +0200 Subject: [PATCH] Domain graph view feature Signed-off-by: JesusPoderoso Refs #19308: Update graph view name Signed-off-by: JesusPoderoso Refs #19308: Close last tab would open New tab Signed-off-by: JesusPoderoso Refs #19308: Add and delete tab buttons improvements Signed-off-by: JesusPoderoso Refs #19308: Fix transition Signed-off-by: JesusPoderoso Refs #19308: Cap max tabs to 15 Signed-off-by: JesusPoderoso Refs #19308: Fix view issue when first tab gets closed Signed-off-by: JesusPoderoso Refs #19308: Fix ChartLayout view issue when resizing window Signed-off-by: JesusPoderoso Refs #19308: Fix polish infinite loop Signed-off-by: JesusPoderoso Refs #19308: Avoid tab dragging Signed-off-by: JesusPoderoso Refs #19308: Propagate fullScreen var changes Signed-off-by: JesusPoderoso Refs #19308: Initial domain graph view Signed-off-by: JesusPoderoso Refs #19308: Improve domain view Signed-off-by: JesusPoderoso Refs #19308: Adapt to new JSON and visual improvements Signed-off-by: JesusPoderoso Refs #19308: Add arrows to connections Signed-off-by: JesusPoderoso Refs #19308: Round elements Signed-off-by: JesusPoderoso Refs #19308: Fix entities height Signed-off-by: JesusPoderoso Refs #19308: Update tab name with view content Signed-off-by: JesusPoderoso Refs #19308: Fix connections Signed-off-by: JesusPoderoso Refs #19308: Introduce backend call Signed-off-by: JesusPoderoso Refs #19308: Add domain id selector for graph view Signed-off-by: JesusPoderoso Refs #19308: Establish connection Signed-off-by: JesusPoderoso Refs #19308: Add Refresh button and refresh feature Signed-off-by: JesusPoderoso Refs #19308: Add try-catch in SyncBackendConnection call Signed-off-by: JesusPoderoso Refs #19308: Catch Exception when no graph Signed-off-by: JesusPoderoso Refs #19308: Fix wheel movement when scrollbar disabled Signed-off-by: JesusPoderoso Refs #19308: Fix communication arrows visual bugs Signed-off-by: JesusPoderoso Refs #19308: Fix icons and text spacing in entity boxes Signed-off-by: JesusPoderoso Refs: Remove vulcanexus blue from color palette Signed-off-by: JesusPoderoso Refs #19308: Fix entity order generation to optimize resizing Signed-off-by: JesusPoderoso Refs #19308: Add comments to QML code Signed-off-by: JesusPoderoso Refs #19308: Improve backend calls Signed-off-by: JesusPoderoso Refs #19308: Fix overlapping visual bugs Signed-off-by: JesusPoderoso Refs #19308: Fix resizing with large alias Signed-off-by: JesusPoderoso Refs #19308: Fix single topic (and no entities) data representation Signed-off-by: JesusPoderoso Refs #19308: Fix tab issue with multiple domains Signed-off-by: JesusPoderoso Refs #19308: Display domain info when navigating through different domain views in tabs Signed-off-by: JesusPoderoso Refs #19308: Display entity and topic data when clicked in the graph Signed-off-by: JesusPoderoso Refs #19513: Improve Domain ID Dialog values Signed-off-by: JesusPoderoso Refs #19513: Add metatraffic filter in the graph view Signed-off-by: JesusPoderoso Refs #19513: Add status layout skeleton Signed-off-by: JesusPoderoso Refs #19513: Add right click options Signed-off-by: JesusPoderoso Refs #19513: Include topic filtering Signed-off-by: JesusPoderoso Refs #19513: Improve alias updates Signed-off-by: JesusPoderoso Refs #19533: Implement topic filtering Signed-off-by: JesusPoderoso Refs #19533: Fix alias to set automatically in domain view graph Signed-off-by: JesusPoderoso Refs #19533: Fix refresh tab renaming Signed-off-by: JesusPoderoso Refs #19533: Fix topic view from logical model Signed-off-by: JesusPoderoso Refs #19533: Improve arrow drawing Signed-off-by: JesusPoderoso --- qml.qrc | 1 + qml/ChangeAliasDialog.qml | 2 + qml/EntitiesMenu.qml | 7 +- qml/EntityList.qml | 6 +- qml/LeftPanel.qml | 29 ++++- qml/LogicalView.qml | 5 +- qml/Panels.qml | 79 ++++++++++-- qml/PhysicalView.qml | 6 +- qml/StatusLayout.qml | 249 ++++++++++++++++++++++++++++++++++++++ qml/TopicMenu.qml | 45 +++++++ src/Controller.cpp | 1 + 11 files changed, 408 insertions(+), 22 deletions(-) create mode 100644 qml/StatusLayout.qml create mode 100644 qml/TopicMenu.qml diff --git a/qml.qrc b/qml.qrc index 22715c9e..8f9fd736 100644 --- a/qml.qrc +++ b/qml.qrc @@ -60,6 +60,7 @@ qml/StatisticsChartBox.qml qml/StatisticsChartView.qml qml/StatusPanel.qml + qml/StatusLayout.qml qml/StatusView.qml qml/SummaryView.qml qml/TabLayout.qml diff --git a/qml/ChangeAliasDialog.qml b/qml/ChangeAliasDialog.qml index 3c0507c0..fc16d65e 100644 --- a/qml/ChangeAliasDialog.qml +++ b/qml/ChangeAliasDialog.qml @@ -32,6 +32,7 @@ Dialog { standardButtons: Dialog.Ok | Dialog.Cancel anchors.centerIn: Overlay.overlay + property var domainEntityId: "" property var entityId: "" property var currentAlias: "" property var entityKind: "" @@ -79,6 +80,7 @@ Dialog { } else { controller.set_alias(entityId, newSeriesNameTextField.text, entityKind) } + refreshDomainGraphView(domainEntityId, entityId) } } } diff --git a/qml/EntitiesMenu.qml b/qml/EntitiesMenu.qml index 48b084f6..7f7602aa 100644 --- a/qml/EntitiesMenu.qml +++ b/qml/EntitiesMenu.qml @@ -7,12 +7,17 @@ import Theme 1.0 */ Menu { id: entitiesMenu + property string domainEntityId: "" property string entityId: "" property string currentAlias: "" property string entityKind: "" MenuItem { text: "Change alias" - onTriggered: changeAlias(menu.entityId, menu.currentAlias, menu.entityKind) + onTriggered: changeAlias(menu.domainEntityId, menu.entityId, menu.currentAlias, menu.entityKind) + } + MenuItem { + text: "View Problems" + onTriggered: {}//todo } } diff --git a/qml/EntityList.qml b/qml/EntityList.qml index 0746d97b..61c534d2 100644 --- a/qml/EntityList.qml +++ b/qml/EntityList.qml @@ -90,7 +90,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.participant_click(id) } @@ -173,7 +173,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.endpoint_click(id) } @@ -245,7 +245,7 @@ Rectangle { onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.locator_click(id) } diff --git a/qml/LeftPanel.qml b/qml/LeftPanel.qml index 4f3fe822..f85e84e3 100644 --- a/qml/LeftPanel.qml +++ b/qml/LeftPanel.qml @@ -40,6 +40,8 @@ RowLayout { signal explorerPhysicalChanged(bool status) signal explorerLogicalChanged(bool status) signal explorerEntityInfoChanged(bool status) + signal open_topic_view(string domainEntityId, string domainId, string entityId) + signal refresh_domain_graph_view(string domainEntityId, string entityId) MonitoringPanel { id: monitoringPanel @@ -65,26 +67,49 @@ RowLayout { id: entitiesMenu } + TopicMenu { + id: topicMenu + } + IssuesPanel { id: issuesPanel Layout.fillHeight: true visible: (visiblePanel === panelItem[LeftPanel.LeftSubPanel.Issues]) ? true : false } - function changeAlias(entityId, currentAlias, entityKind) { + function changeAlias(domainEntityId, entityId, currentAlias, entityKind) { + aliasDialog.domainEntityId = domainEntityId aliasDialog.entityId = entityId aliasDialog.currentAlias = currentAlias aliasDialog.entityKind = entityKind aliasDialog.open() } - function openEntitiesMenu(entityId, currentAlias, entityKind) { + function openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) { + entitiesMenu.domainEntityId = domainEntityId entitiesMenu.entityId = entityId entitiesMenu.currentAlias = currentAlias entitiesMenu.entityKind = entityKind entitiesMenu.popup() } + function openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) { + topicMenu.domainEntityId = domainEntityId + topicMenu.domainId = domainId + topicMenu.entityId = entityId + topicMenu.currentAlias = currentAlias + topicMenu.entityKind = entityKind + topicMenu.popup() + } + + function openTopicView(domainEntityId, domainId, entityId) { + leftPanel.open_topic_view(domainEntityId, domainId, entityId) + } + + function refreshDomainGraphView(domainEntityId, entityId) { + leftPanel.refresh_domain_graph_view(domainEntityId, entityId) + } + function expandAll(view, model) { for(var i=0; i < model.rowCount(); i++) { var index = model.index(i, 0) diff --git a/qml/LogicalView.qml b/qml/LogicalView.qml index cbbfa464..999950e4 100644 --- a/qml/LogicalView.qml +++ b/qml/LogicalView.qml @@ -63,6 +63,7 @@ Rectangle { height: domainListColumn.childrenRect.height property var domainId: id + property var domainName: name property int domainIdx: index property var topicList: topicList @@ -96,7 +97,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu(domainId, id, name, kind) } else { controller.domain_click(id) } @@ -166,7 +167,7 @@ Rectangle { onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openTopicMenu(domainId, domainName, id, name, kind) } else { controller.topic_click(id) } diff --git a/qml/Panels.qml b/qml/Panels.qml index 84df2717..d9c093bc 100644 --- a/qml/Panels.qml +++ b/qml/Panels.qml @@ -30,6 +30,9 @@ RowLayout { // If is set to true, the left sidebar was opened, and false if it was closed. property bool prevFullScreenLeftSidebarState: true + // private properties + property int status_layout_height: status_layout_min_height_ + signal openCloseLeftSideBar signal changeChartboxLayout(int chartsPerRow) signal explorerDDSEntitiesChanged(bool status) @@ -37,6 +40,10 @@ RowLayout { signal explorerLogicalChanged(bool status) signal explorerEntityInfoChanged(bool status) + // Read only design properties + readonly property int status_layout_min_height_: 24 + + onOpenCloseLeftSideBar: { if (panels.showLeftSidebar) { iconsVBar.iconClicked(iconsVBar.selected) @@ -90,24 +97,66 @@ RowLayout { onExplorerPhysicalChanged: panels.explorerPhysicalChanged(status) onExplorerLogicalChanged: panels.explorerLogicalChanged(status) onExplorerEntityInfoChanged: panels.explorerEntityInfoChanged(status) + onOpen_topic_view: tabs.open_topic_view(domainEntityId, domainId, entityId) + onRefresh_domain_graph_view: tabs.refresh_domain_graph_view(domainEntityId, entityId) } TabLayout { id: tabs SplitView.fillWidth: true - clip: true - onFullScreenChanged: { - if (fullScreen) { - if (showLeftSidebar) { - openCloseLeftSideBar() - prevFullScreenLeftSidebarState = true - } else { - prevFullScreenLeftSidebarState = false + SplitView { + id: tabsSplitView + anchors.fill: parent + orientation: Qt.Vertical + + TabLayout { + id: tabs + SplitView.fillWidth: true + SplitView.fillHeight: true + SplitView.preferredHeight: parent.height - parent.height / 5 + clip: true + + onFullScreenChanged: { + if (fullScreen) { + if (showLeftSidebar) { + openCloseLeftSideBar() + prevFullScreenLeftSidebarState = true + } else { + prevFullScreenLeftSidebarState = false + } + } else { + if (!showLeftSidebar && prevFullScreenLeftSidebarState) { + openCloseLeftSideBar() + } + } } - } else { - if (!showLeftSidebar && prevFullScreenLeftSidebarState) { - openCloseLeftSideBar() + onOpenEntitiesMenu: { + panels.openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) + } + onOpenTopicMenu: { + panels.openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) + } + } + StatusLayout { + id: statusLayout + SplitView.preferredHeight: status_layout_height + SplitView.minimumHeight: status_layout_min_height_ + clip: true + current_status: StatusLayout.Status.Collapsed + footer_height: status_layout_min_height_ + + onClose_status_layout: { + status_layout_height = status_layout_min_height_ + statusLayout.current_status = StatusLayout.Status.Closed + } + onCollapse_status_layout: { + status_layout_height = tabsSplitView.height / 5 + statusLayout.current_status = StatusLayout.Status.Collapsed + } + onExpand_status_layout: { + status_layout_height = tabsSplitView.height + statusLayout.current_status = StatusLayout.Status.Expanded } } } @@ -145,4 +194,12 @@ RowLayout { function changeExplorerEntityInfo(status) { leftPanel.changeExplorerEntityInfo(status) } + + function openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) { + leftPanel.openEntitiesMenu(domainEntityId, entityId, currentAlias, entityKind) + } + + function openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) { + leftPanel.openTopicMenu(domainEntityId, domainId, entityId, currentAlias, entityKind) + } } diff --git a/qml/PhysicalView.qml b/qml/PhysicalView.qml index 0fa8cb55..e698b445 100644 --- a/qml/PhysicalView.qml +++ b/qml/PhysicalView.qml @@ -90,7 +90,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.host_click(id) } @@ -174,7 +174,7 @@ Rectangle { } onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.user_click(id) } @@ -246,7 +246,7 @@ Rectangle { onClicked: { if(mouse.button & Qt.RightButton) { - openEntitiesMenu(id, name, kind) + openEntitiesMenu("", id, name, kind) } else { controller.process_click(id) } diff --git a/qml/StatusLayout.qml b/qml/StatusLayout.qml new file mode 100644 index 00000000..3125e719 --- /dev/null +++ b/qml/StatusLayout.qml @@ -0,0 +1,249 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eProsima Fast DDS Monitor. If not, see . + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.15 + +import Theme 1.0 + +Item +{ + id: statusLayout + + // Public properties + enum Status {Closed, Expanded, Collapsed} + property int current_status: Closed + required property int footer_height + + // Public signals + signal close_status_layout() + signal expand_status_layout() + signal collapse_status_layout() + + // Private properties + property bool filter_visible_: true + property int expand_btn_rotation_angle: expand_rotation_angle_ + + // Private signals + + // Read only design properties (sizes and colors) + readonly property int tabs_height_: 30 + readonly property int tabs_width_: 100 + readonly property int elements_spacing_: 8 + readonly property int separator_line_: 2 + readonly property string grey_background_: "#eaeaea" + readonly property int expand_rotation_angle_: 90 + readonly property int collapse_rotation_angle_: -90 + + TabView { + id: tab_view + anchors.top: parent.top + anchors.bottom: separator_line.top + width: parent.width + + Tab { + title: "Problems" + Rectangle { + + color: "white" + + Text { + anchors.top: parent.top; anchors.topMargin: elements_spacing_ + anchors.left: parent.left; anchors.leftMargin: elements_spacing_ + text: "There are no issues" + } + } + } + style: TabViewStyle { + frameOverlap: 1 + tab: Rectangle { + color: styleData.selected ? "white" : Theme.lightGrey + implicitWidth: Math.max(text.width + 10, tabs_width_) + implicitHeight: tabs_height_ + radius: 4 + + Rectangle { + width: parent.width + height: parent.height/2 + anchors.bottom: parent.bottom + anchors.left: parent.left + color: parent.color + } + Text { + id: text + anchors.centerIn: parent + text: styleData.title + } + } + tabBar: Rectangle { + anchors.top: parent.top + width: parent.width + height: tabs_height_ + color: grey_background_ + + IconSVG { + id: close_icon + anchors.right: parent.right + anchors.rightMargin: elements_spacing_ *2 + anchors.verticalCenter: parent.verticalCenter + name: "cross" + size: parent.height - elements_spacing_ *3/2 + + MouseArea { + anchors.centerIn: parent + width: parent.width + 2*elements_spacing_ + height: parent.height + 2*elements_spacing_ + + onClicked: { + close_status_layout() + } + } + } + + Rectangle { + id: rotation_rect + anchors.right: close_icon.left + anchors.rightMargin: elements_spacing_ *2 + anchors.verticalCenter: parent.verticalCenter + width: parent.height - elements_spacing_ + height: parent.height - elements_spacing_ + transform: Rotation { + origin.x: rotation_rect.width/2 + origin.y: rotation_rect.height/2 + angle: expand_btn_rotation_angle + } + color: "transparent" + + IconSVG { + id: expand_icon + + name: "lessthan" + size: parent.width + } + + MouseArea { + anchors.centerIn: parent + width: parent.width + 2*elements_spacing_ + height: parent.height + 2*elements_spacing_ + + onClicked: { + if (statusLayout.current_status === StatusLayout.Status.Expanded) + { + expand_btn_rotation_angle = expand_rotation_angle_ + collapse_status_layout() + } + else if (statusLayout.current_status === StatusLayout.Status.Collapsed) + { + expand_btn_rotation_angle = collapse_rotation_angle_ + expand_status_layout() + } + } + } + } + + IconSVG { + id: filter_icon + visible: statusLayout.filter_visible_ + anchors.right: rotation_rect.left + anchors.rightMargin: elements_spacing_ *2 + anchors.verticalCenter: parent.verticalCenter + name: "status" + size: parent.height - elements_spacing_ + } + } + } + } + + Rectangle { + id: separator_line + anchors.bottom: icon_section.top + width: parent.width + height: separator_line_ + + color: Theme.grey + } + + Rectangle { + id: icon_section + anchors.bottom: parent.bottom + height: footer_height + width: parent.width + color: grey_background_ + + IconSVG { + id: error_icon + anchors.left: parent.left + anchors.leftMargin: elements_spacing_ + anchors.verticalCenter: parent.verticalCenter + name: "cross" + size: parent.height - elements_spacing_ -3 + } + Label { + id: error_value + anchors.left: error_icon.right + anchors.leftMargin: elements_spacing_/2 + anchors.verticalCenter: parent.verticalCenter + text: "1" + } + IconSVG { + id: warning_icon + anchors.left: error_value.right + anchors.leftMargin: elements_spacing_ + anchors.verticalCenter: parent.verticalCenter + name: "issues" + size: parent.height - elements_spacing_ + } + Label { + id: warning_value + anchors.left: warning_icon.right + anchors.leftMargin: elements_spacing_/2 + anchors.verticalCenter: parent.verticalCenter + text: "5" + } + IconSVG { + id: info_icon + anchors.left: warning_value.right + anchors.leftMargin: elements_spacing_ + name: "info" + size: parent.height - elements_spacing_ -1 + anchors.verticalCenter: parent.verticalCenter + } + Label { + id: info_value + anchors.left: info_icon.right + anchors.leftMargin: elements_spacing_/2 + anchors.verticalCenter: parent.verticalCenter + text: "19" + } + MouseArea { + anchors.fill: parent + onClicked: { + if (current_status === StatusLayout.Status.Collapsed) + { + close_status_layout() + } + else + { + collapse_status_layout() + } + } + } + } +} diff --git a/qml/TopicMenu.qml b/qml/TopicMenu.qml new file mode 100644 index 00000000..73caf50f --- /dev/null +++ b/qml/TopicMenu.qml @@ -0,0 +1,45 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// This file is part of eProsima Fast DDS Monitor. +// +// eProsima Fast DDS Monitor is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// eProsima Fast DDS Monitor is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eProsima Fast DDS Monitor. If not, see . + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import Theme 1.0 + +/* + Menu containing the possible actions that can be performed on any DDS, physical and logical entity. + */ +Menu { + id: topicMenu + property string domainEntityId: "" + property string domainId: "" + property string entityId: "" + property string currentAlias: "" + property string entityKind: "" + + MenuItem { + text: "Change alias" + onTriggered: changeAlias(menu.domainEntityId, menu.entityId, menu.currentAlias, menu.entityKind) + } + MenuItem { + text: "View problems" + onTriggered: {}//todo + } + MenuItem { + text: "Filter graph view" + onTriggered: openTopicView(menu.domainEntityId, menu.domainId, menu.entityId) + } +} diff --git a/src/Controller.cpp b/src/Controller.cpp index 10fa491c..543dab3f 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include