#include <QByteArray>
#include <graphviz/gvc.h>
+
+/*! RAII wrapper to graphviz Agraph_t.*/
+class GraphWrapper
+{
+ public:
+ virtual ~GraphWrapper();
+
+ void init(char* name, Agdesc_t desc = Agstrictdirected, Agdisc_t* disc = nullptr);
+
+ Agraph_t* getGraph() const { return m_graph; }
+ GVC_t* getGvc() const { return m_gvc; }
+
+ private:
+ Agraph_t* m_graph = nullptr;
+ GVC_t* m_gvc = nullptr;
+};
+
+GraphWrapper::~GraphWrapper()
+{
+ if (m_graph)
+ {
+ agclose(m_graph);
+ m_graph = nullptr;
+ }
+
+ if (m_gvc)
+ {
+ gvFreeContext(m_gvc);
+ m_gvc = nullptr;
+ }
+}
+
+void GraphWrapper::init(char *name, Agdesc_t desc /* = Agstrictdirected */, Agdisc_t* disc /* = nullptr */)
+{
+ m_graph = agopen(name, desc, disc);
+ m_gvc = gvContext();
+}
+
/*!Constructor.*/
LightApp_ExtInfoDlg::LightApp_ExtInfoDlg(QWidget* parent)
: QtxDialog(parent, true, true, ButtonFlags::OK)
return extListWidget;
}
-/*! Return widget with an image of dependency tree fot installed extensions */
-QWidget* LightApp_ExtInfoDlg::getExtTreeWidget(QWidget* parent) const
+/*! Fill a given extension tree graph with nodes and edges */
+bool LightApp_ExtInfoDlg::fillExtTreeGraph(const GraphWrapper& graph) const
{
- MESSAGE("Start to get extensions dependency tree...\n");
-
- auto extTreeWidget = new QSvgWidget(parent);
+ MESSAGE("Start to get info from SalomeOnDemandTK.extension_query...\n");
// 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)
{
PyErr_Print();
- return extTreeWidget;
+ return false;
}
- // Graph to be filled up from python data
- char graphName[] = "extTreeGraph";
- Agraph_t* extTreeGraph = agopen(graphName, Agstrictdirected, nullptr);
- GVC_t* gvc = gvContext();
+ Agraph_t* extTreeGraph = graph.getGraph();
// Python declarations
- PyObject *extName = nullptr;
- PyObject *dependantsList = nullptr;
+ PyObject* extName = nullptr;
+ PyObject* dependantsList = nullptr;
Py_ssize_t pos = 0;
// Iterate the tree
}
}
+ return true;
+}
+
+/*! Render dependency tree to array of bytes */
+QByteArray LightApp_ExtInfoDlg::renderExtTreeGraph(const GraphWrapper& graph) const
+{
+ Agraph_t* extTreeGraph = graph.getGraph();
+ GVC_t* gvc = graph.getGvc();
+
// Layout and render to buffer
+ MESSAGE("Layout dependency tree...\n");
int res = gvLayout(gvc, extTreeGraph, "dot");
if (res)
{
MESSAGE("gvLayout failed!\n");
- return extTreeWidget;
+ return {};
}
+ MESSAGE("Render dependency tree...\n");
char* buffer = nullptr;
unsigned int bufferLength = 0;
res = gvRenderData(gvc, extTreeGraph, "svg", &buffer, &bufferLength);
if (res)
{
MESSAGE("gvRenderData failed!\n");
- return extTreeWidget;
+ return {};
}
- // Load the buffer into widget
QByteArray renderedGraph(buffer, bufferLength);
- extTreeWidget->load(renderedGraph);
-
- // Clean up section
+
+ // Clean up layout and buffer
gvFreeLayout(gvc, extTreeGraph);
gvFreeRenderData(buffer);
- agclose(extTreeGraph);
+
+ return renderedGraph;
+}
+
+/*! Return widget with an image of dependency tree fot installed extensions */
+QWidget* LightApp_ExtInfoDlg::getExtTreeWidget(QWidget* parent) const
+{
+ MESSAGE("Start to make a widget to show dependency tree...\n");
+
+ auto extTreeWidget = new QSvgWidget(parent);
+
+ // Graph to be filled up from python data
+ GraphWrapper extTreeGraph;
+ char graphName[] = "extTreeGraph";
+ extTreeGraph.init(graphName);
+ if (fillExtTreeGraph(extTreeGraph))
+ {
+ extTreeWidget->load(renderExtTreeGraph(extTreeGraph));
+ }
return extTreeWidget;
}