1 // Copyright (C) 2007-2024 CEA, EDF, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File: LightApp_ExtInfoDlg.cxx
24 // Author: Konstantin Leontev
26 #include "LightApp_ExtInfoDlg.h"
27 #include "utilities.h"
29 // Prevent slot compilation error
30 #pragma push_macro("slots")
32 #include "PyInterp_Utils.h"
33 #pragma pop_macro("slots")
35 #include <QHBoxLayout>
37 // Show extensions info
38 #include <QTableWidget>
39 #include <QTableWidgetItem>
40 #include <QHeaderView>
42 // Render dependency tree
45 #include <graphviz/gvc.h>
48 /*! RAII wrapper to graphviz Agraph_t.*/
52 virtual ~GraphWrapper();
54 void init(char* name, Agdesc_t desc = Agstrictdirected, Agdisc_t* disc = nullptr);
56 Agraph_t* getGraph() const { return m_graph; }
57 GVC_t* getGvc() const { return m_gvc; }
60 Agraph_t* m_graph = nullptr;
61 GVC_t* m_gvc = nullptr;
64 GraphWrapper::~GraphWrapper()
79 void GraphWrapper::init(char *name, Agdesc_t desc /* = Agstrictdirected */, Agdisc_t* disc /* = nullptr */)
81 m_graph = agopen(name, desc, disc);
86 LightApp_ExtInfoDlg::LightApp_ExtInfoDlg(QWidget* parent)
87 : QtxDialog(parent, true, true, ButtonFlags::OK)
89 MESSAGE("Start creating a dialog...\n");
91 setObjectName("salome_ext_info_dialog");
92 setWindowTitle(tr("EXT_INFO_CAPTION"));
93 setSizeGripEnabled(true);
94 setButtonPosition(ButtonPosition::Center, ButtonFlags::OK);
96 auto extInfoWiget = getExtListWidget(mainFrame());
97 auto extTreeWiget = getExtTreeWidget(mainFrame());
99 auto layout = new QHBoxLayout(mainFrame());
100 layout->addWidget(extInfoWiget);
101 layout->addWidget(extTreeWiget);
105 LightApp_ExtInfoDlg::~LightApp_ExtInfoDlg()
110 /*! Fill the given widget with info about installed extensions */
111 bool LightApp_ExtInfoDlg::fillExtListWidget(QTableWidget* extListWidget) const
113 MESSAGE("Getting info from SalomeOnDemandTK.extension_query...\n");
115 // Import Python module that manages SALOME extensions
116 PyLockWrapper lck; // acquire GIL
117 PyObjWrapper extensionQuery = PyImport_ImportModule((char*)"SalomeOnDemandTK.extension_query");
118 auto extRootDir = getenv("SALOME_APPLICATION_DIR");
119 PyObjWrapper extInfoDict = PyObject_CallMethod(extensionQuery, (char*)"ext_info_dict", (char*)"s", extRootDir);
126 // Check if we have any info to display
127 Py_ssize_t rowCount = PyDict_Size(extInfoDict);
130 MESSAGE("Didn't find any extensions! Return.\n");
134 extListWidget->setRowCount(rowCount);
135 const int columnCount = extListWidget->columnCount();
137 auto makeTableWidgetItem = [](PyObject* itemText) -> QTableWidgetItem*
139 const char* itemTextStr = PyUnicode_AsUTF8(itemText);
142 return new QTableWidgetItem(itemTextStr);
145 PyObject* keyName = nullptr;
146 PyObject* infoList = nullptr;
147 Py_ssize_t keyNamePos = 0;
149 // Iterate name:info_list dictionary
150 while (PyDict_Next(extInfoDict, &keyNamePos, &keyName, &infoList))
152 auto widgetItem = makeTableWidgetItem(keyName);
154 // keyNamePos is already 1 on the first iteration, so we need to decrease it
155 extListWidget->setItem(keyNamePos - 1, 0, widgetItem);
157 // Iterate an extension info list
158 for (Py_ssize_t infoPos = 0; infoPos < PyList_Size(infoList); ++infoPos)
160 if (infoPos >= columnCount)
162 MESSAGE("Number of info items is greater than column count! Skip.\n");
166 auto info = PyList_GetItem(infoList, infoPos);
167 widgetItem = makeTableWidgetItem(info);
169 // keyNamePos started from 1 instead of 0, so decrease
170 // info need to be filled from column 1, so increase
171 extListWidget->setItem(keyNamePos - 1, infoPos + 1, widgetItem);
178 /*! Return widget with info about installed extensions */
179 QWidget* LightApp_ExtInfoDlg::getExtListWidget(QWidget* parent) const
181 MESSAGE("Make a widget to display extensions info...\n");
183 auto extListWidget = new QTableWidget(parent);
185 // Setup the table params
186 const QStringList headerLabels = {
187 "Name", "Description", "Author", "Components", "Size"
190 extListWidget->setColumnCount(headerLabels.count());
191 extListWidget->setHorizontalHeaderLabels(headerLabels);
193 // Fill it with data about extensions
194 if (fillExtListWidget(extListWidget))
196 // Tune an appearance
197 extListWidget->sortItems(0);
198 extListWidget->horizontalHeader()->setStretchLastSection(true);
199 extListWidget->resizeColumnsToContents();
200 extListWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
203 return extListWidget;
206 /*! Fill a given extension tree graph with nodes and edges */
207 bool LightApp_ExtInfoDlg::fillExtTreeGraph(const GraphWrapper& graph) const
209 MESSAGE("Start to get info from SalomeOnDemandTK.extension_query...\n");
211 // Import Python module that manages SALOME extensions
212 PyLockWrapper lck; // acquire GIL
213 PyObjWrapper extensionQuery = PyImport_ImportModule((char*)"SalomeOnDemandTK.extension_query");
214 auto extRootDir = getenv("SALOME_APPLICATION_DIR");
215 PyObjWrapper dependencyTree = PyObject_CallMethod(extensionQuery, (char*)"dependency_tree", (char*)"s", extRootDir);
222 Agraph_t* extTreeGraph = graph.getGraph();
224 // Python declarations
225 PyObject* extName = nullptr;
226 PyObject* dependantsList = nullptr;
230 while (PyDict_Next(dependencyTree, &pos, &extName, &dependantsList))
232 // Create a parent node if it doesn't already exist
233 auto parentName = PyUnicode_AsUTF8(extName);
234 Agnode_t* parentNode = agnode(extTreeGraph, const_cast<char*>(parentName), true);
237 // Iterate a list of dependants
238 for (Py_ssize_t depPos = 0; depPos < PyList_Size(dependantsList); ++depPos)
240 auto dependant = PyList_GetItem(dependantsList, depPos);
242 // Create a child node if it doesn't already exist
243 auto dependantName = PyUnicode_AsUTF8(dependant);
244 Agnode_t* dependantNode = agnode(extTreeGraph, const_cast<char*>(dependantName), true);
245 SCRUTE(dependantName);
248 std::string edgeNameStr(parentName);
249 edgeNameStr += dependantName;
250 auto edgeName = edgeNameStr.c_str();
251 agedge(extTreeGraph, parentNode, dependantNode, const_cast<char*>(edgeName), true);
259 /*! Render dependency tree to array of bytes */
260 QByteArray LightApp_ExtInfoDlg::renderExtTreeGraph(const GraphWrapper& graph) const
262 Agraph_t* extTreeGraph = graph.getGraph();
263 GVC_t* gvc = graph.getGvc();
265 // Layout and render to buffer
266 MESSAGE("Layout dependency tree...\n");
267 int res = gvLayout(gvc, extTreeGraph, "dot");
270 MESSAGE("gvLayout failed!\n");
274 MESSAGE("Render dependency tree...\n");
275 char* buffer = nullptr;
276 unsigned int bufferLength = 0;
277 res = gvRenderData(gvc, extTreeGraph, "svg", &buffer, &bufferLength);
280 MESSAGE("gvRenderData failed!\n");
284 QByteArray renderedGraph(buffer, bufferLength);
286 // Clean up layout and buffer
287 gvFreeLayout(gvc, extTreeGraph);
288 gvFreeRenderData(buffer);
290 return renderedGraph;
293 /*! Return widget with an image of dependency tree fot installed extensions */
294 QWidget* LightApp_ExtInfoDlg::getExtTreeWidget(QWidget* parent) const
296 MESSAGE("Start to make a widget to show dependency tree...\n");
298 auto extTreeWidget = new QSvgWidget(parent);
300 // Graph to be filled up from python data
301 GraphWrapper extTreeGraph;
302 char graphName[] = "extTreeGraph";
303 extTreeGraph.init(graphName);
304 if (fillExtTreeGraph(extTreeGraph))
306 extTreeWidget->load(renderExtTreeGraph(extTreeGraph));
309 return extTreeWidget;