From 2f544ae7ceaa7b23ab04dc869966576976de44d2 Mon Sep 17 00:00:00 2001 From: kosta Date: Tue, 7 Feb 2023 18:03:58 +0100 Subject: [PATCH] [bos #32523][EDF] SALOME on Demand GUI. Added render of dependency tree into extensions info dialog as SVG. --- src/LightApp/CMakeLists.txt | 2 + src/LightApp/LightApp_ExtInfoDlg.cxx | 91 ++++++++++++++++++++-------- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/src/LightApp/CMakeLists.txt b/src/LightApp/CMakeLists.txt index 16cc420b6..1dd89a2e8 100644 --- a/src/LightApp/CMakeLists.txt +++ b/src/LightApp/CMakeLists.txt @@ -102,7 +102,9 @@ SET(_link_LIBRARIES ${HDF5_LIBRARIES} CASCatch qtx suit std SalomeStyle SalomePrs CAM LogWindow ObjBrowser Event ${KERNEL_SalomeHDFPersist} ${KERNEL_SALOMELocalTrace} + ${GRAPHVIZ_LIBRARIES} ) + IF(SALOME_USE_SALOMEOBJECT) LIST(APPEND _link_LIBRARIES SalomeObject) ENDIF() diff --git a/src/LightApp/LightApp_ExtInfoDlg.cxx b/src/LightApp/LightApp_ExtInfoDlg.cxx index 491565a93..d81d95ea5 100644 --- a/src/LightApp/LightApp_ExtInfoDlg.cxx +++ b/src/LightApp/LightApp_ExtInfoDlg.cxx @@ -47,6 +47,11 @@ #include #include // to get path to the file +// Render dependency tree +#include +#include +#include + /*!Constructor.*/ LightApp_ExtInfoDlg::LightApp_ExtInfoDlg(QWidget* parent) : QtxDialog(parent, true, true, ButtonFlags::OK) @@ -129,7 +134,7 @@ QWidget* LightApp_ExtInfoDlg::getExtTreeWidget(QWidget* parent) const { MESSAGE("Start to get extensions dependency tree...\n"); - auto extTreeWidget = new QWidget(parent); + auto extTreeWidget = new QSvgWidget(parent); auto app = dynamic_cast(SUIT_Session::session()->activeApplication()); ASSERT(app); if (!app) @@ -137,46 +142,80 @@ QWidget* LightApp_ExtInfoDlg::getExtTreeWidget(QWidget* parent) const return extTreeWidget; } - // TODO: replace this list with diagram - auto gridLayout = new QGridLayout(extTreeWidget); - gridLayout->setSpacing(5); - // Import Python module that manages SALOME extensions PyLockWrapper lck; // acquire GIL PyObjWrapper extensionQuery = PyImport_ImportModule((char*)"SalomeOnDemandTK.extension_query"); auto extRootDir = getenv("SALOME_APPLICATION_DIR"); PyObjWrapper dependencyTree = PyObject_CallMethod(extensionQuery, (char*)"dependency_tree", (char*)"s", extRootDir); ASSERT(dependencyTree); - - if (dependencyTree) + if (!dependencyTree) { - PyObject *extName = nullptr; - PyObject *dependantsList = nullptr; - Py_ssize_t pos = 0; + PyErr_Print(); + return extTreeWidget; + } - // Iterate the tree - while (PyDict_Next(dependencyTree, &pos, &extName, &dependantsList)) - { - auto name = new QLabel(PyUnicode_AsUTF8(extName), extTreeWidget); - gridLayout->addWidget(name, pos, 0); + // Graph to be filled up from python data + char graphName[] = "extTreeGraph"; + Agraph_t* extTreeGraph = agopen(graphName, Agstrictdirected, nullptr); + GVC_t* gvc = gvContext(); - SCRUTE(name->text().toStdString()); + // Python declarations + PyObject *extName = nullptr; + PyObject *dependantsList = nullptr; + Py_ssize_t pos = 0; - // Iterate a list of dependants - for (Py_ssize_t depPos = 0; depPos < PyList_Size(dependantsList); ++depPos) - { - auto dependant = PyList_GetItem(dependantsList, depPos); - auto dependantLabel = new QLabel(PyUnicode_AsUTF8(dependant), extTreeWidget); - gridLayout->addWidget(dependantLabel , pos, depPos + 1); + // Iterate the tree + while (PyDict_Next(dependencyTree, &pos, &extName, &dependantsList)) + { + // Create a parent node if it doesn't already exist + auto parentName = PyUnicode_AsUTF8(extName); + Agnode_t* parentNode = agnode(extTreeGraph, const_cast(parentName), true); + SCRUTE(parentName); - SCRUTE(dependantLabel->text().toStdString()); - } + // Iterate a list of dependants + for (Py_ssize_t depPos = 0; depPos < PyList_Size(dependantsList); ++depPos) + { + auto dependant = PyList_GetItem(dependantsList, depPos); + + // Create a child node if it doesn't already exist + auto dependantName = PyUnicode_AsUTF8(dependant); + Agnode_t* dependantNode = agnode(extTreeGraph, const_cast(dependantName), true); + SCRUTE(dependantName); + + // Make an edge + std::string edgeNameStr(parentName); + edgeNameStr += dependantName; + auto edgeName = edgeNameStr.c_str(); + agedge(extTreeGraph, parentNode, dependantNode, const_cast(edgeName), true); + SCRUTE(edgeName); } } - else + + // Layout and render to buffer + int res = gvLayout(gvc, extTreeGraph, "dot"); + if (res) { - PyErr_Print(); + MESSAGE("gvLayout failed!\n"); + return extTreeWidget; } + char* buffer = nullptr; + unsigned int bufferLength = 0; + res = gvRenderData(gvc, extTreeGraph, "svg", &buffer, &bufferLength); + if (res) + { + MESSAGE("gvRenderData failed!\n"); + return extTreeWidget; + } + + // Load the buffer into widget + QByteArray renderedGraph(buffer, bufferLength); + extTreeWidget->load(renderedGraph); + + // Clean up section + gvFreeLayout(gvc, extTreeGraph); + gvFreeRenderData(buffer); + agclose(extTreeGraph); + return extTreeWidget; } -- 2.39.2