GCC Code Coverage Report


Directory: ./
File: src/gui/pythonwidget.cc
Date: 2025-04-14 11:18:00
Exec Total Coverage
Lines: 0 156 0.0%
Branches: 0 428 0.0%

Line Branch Exec Source
1 // Copyright (c) 2015-2018, LAAS-CNRS
2 // Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
3 //
4 // This file is part of gepetto-viewer.
5 // gepetto-viewer is free software: you can redistribute it
6 // and/or modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation, either version
8 // 3 of the License, or (at your option) any later version.
9 //
10 // gepetto-viewer is distributed in the hope that it will be
11 // useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Lesser Public License for more details. You should have
14 // received a copy of the GNU Lesser General Public License along with
15 // gepetto-viewer. If not, see <http://www.gnu.org/licenses/>.
16
17 #include "gepetto/gui/pythonwidget.hh"
18
19 #include <PythonQt.h>
20 #include <PythonQtClassInfo.h>
21 #include <PythonQt_QtAll.h>
22 #include <gui/PythonQtScriptingConsole.h>
23
24 #include <QAction>
25 #include <QFileDialog>
26 #include <QSettings>
27 #include <boost/python.hpp>
28
29 #include "../log.hh"
30 #include "gepetto/gui/mainwindow.hh"
31 #include "gepetto/gui/osgwidget.hh"
32 #include "gepetto/gui/plugin-interface.hh"
33 #include "python-bindings.hh"
34 #include "python-decorator.hh"
35
36 namespace bp = boost::python;
37
38 ✗ BOOST_PYTHON_MODULE(gepettogui_internals) {
39 ✗ exposeOSG();
40 ✗ exposeGV();
41 ✗ exposeGG();
42 }
43
44 namespace gepetto {
45 typedef gui::MainWindow GMainWindow;
46
47 namespace gui {
48 namespace {
49 ✗ void addSignalHandler(PythonQtObjectPtr obj, const QString& callable,
50 QObject* sender, const char* signal) {
51 ✗ PythonQt* pqt = PythonQt::self();
52 ✗ PythonQtObjectPtr call = pqt->lookupCallable(obj, callable);
53 ✗ if (call.isNull()) {
54 ✗ log() << "Callable" << callable << "not found." << std::endl;
55 ✗ return;
56 }
57 ✗ if (!pqt->addSignalHandler(sender, signal, call)) {
58 ✗ log() << "Signal" << signal << "not found in object" << sender->objectName()
59 ✗ << std::endl;
60 } else {
61 ✗ log() << "Connected" << signal << "of" << sender->objectName() << "to"
62 ✗ << callable << std::endl;
63 }
64 }
65
66 const QString var = "pluginInstance";
67 } // namespace
68
69 ✗ PythonWidget::PythonWidget(QWidget* parent)
70 ✗ : QDockWidget("&PythonQt console", parent) {
71 ✗ PyImport_AppendInittab("gepettogui_internals",
72 #if PY_MAJOR_VERSION == 3
73 &PyInit_gepettogui_internals
74 #elif PY_MAJOR_VERSION == 2
75 &initgepettogui_internals
76 #endif
77 );
78
79 ✗ setObjectName("gepetto-gui.pythonqtconsole");
80 ✗ PythonQt::init(PythonQt::RedirectStdOut);
81 ✗ PythonQt_QtAll::init();
82 ✗ PythonQtObjectPtr mainContext = PythonQt::self()->getMainModule();
83 ✗ PythonQtObjectPtr sys = PythonQt::self()->importModule("sys");
84 ✗ sys.evalScript("argv = ['gepetto-gui']");
85 ✗ mainContext.evalScript("import PythonQt");
86 ✗ console_ = new PythonQtScriptingConsole(NULL, mainContext);
87
88 ✗ PythonQt::self()->addDecorators(new PythonDecorator());
89
90 ✗ PythonQt::self()->registerQObjectClassNames(QStringList() << "BodyTreeWidget"
91 ✗ << "BodyTreeItem"
92 ✗ << "SelectionEvent"
93 ✗ << "MainWindow");
94
95 ✗ PythonQt::self()->registerCPPClass("MainWindow", "QMainWindow", "gepetto");
96 ✗ PythonQt::self()->registerCPPClass("OSGWidget", "QWidget", "gepetto");
97
98 ✗ console_->QTextEdit::clear();
99 ✗ console_->consoleMessage(
100 "PythonQt command prompt\n"
101 "Use Shift+Enter for multiline code.\n");
102 ✗ console_->appendCommandPrompt();
103
104 ✗ QWidget* widget = new QWidget;
105 ✗ QVBoxLayout* layout = new QVBoxLayout;
106 ✗ button_ = new QPushButton;
107
108 ✗ button_->setText("Choose file");
109 ✗ layout->addWidget(console_);
110 ✗ layout->addWidget(button_);
111 ✗ widget->setLayout(layout);
112 ✗ this->setWidget(widget);
113
114 ✗ toggleViewAction()->setShortcut(gepetto::gui::DockKeyShortcutBase +
115 Qt::Key_A);
116 ✗ connect(button_, SIGNAL(clicked()), SLOT(browseFile()));
117
118 ✗ bp::import("gepettogui_internals");
119 }
120
121 ✗ PythonWidget::~PythonWidget() {
122 ✗ foreach (const PythonQtObjectPtr& m, modules_) unloadModulePlugin(m);
123 ✗ PythonQt::cleanup();
124 }
125
126 ✗ void PythonWidget::browseFile() {
127 ✗ QFileDialog* fd = new QFileDialog;
128
129 ✗ fd->setFileMode(QFileDialog::ExistingFile);
130 ✗ fd->setNameFilter("All python file (*.py)");
131 ✗ if (fd->exec() == QDialog::Accepted) {
132 ✗ QStringList file = fd->selectedFiles();
133
134 ✗ PythonQtObjectPtr mainContext = PythonQt::self()->getMainModule();
135 ✗ mainContext.evalFile(file.at(0));
136 }
137 ✗ fd->close();
138 ✗ fd->deleteLater();
139 }
140
141 ✗ void PythonWidget::saveHistory(QSettings& settings) {
142 ✗ settings.beginGroup("pythonqt");
143 ✗ QStringList history = console_->history();
144 ✗ int limit = 200;
145 ✗ int start = std::max(history.length() - limit, 0);
146 ✗ QList<QVariant> h;
147 ✗ foreach (QString s, history.mid(start)) {
148 ✗ h << s;
149 }
150 ✗ settings.setValue("history", h);
151 ✗ settings.endGroup();
152 }
153
154 ✗ void PythonWidget::restoreHistory(QSettings& settings) {
155 ✗ settings.beginGroup("pythonqt");
156 ✗ QList<QVariant> h = settings.value("history").toList();
157 ✗ QStringList history;
158 ✗ foreach (QVariant v, h) {
159 ✗ history << v.toString();
160 }
161 ✗ console_->setHistory(history);
162 ✗ settings.endGroup();
163 }
164
165 ✗ bool PythonWidget::hasPlugin(const QString& name) {
166 ✗ return modules_.contains(name);
167 }
168
169 ✗ void PythonWidget::loadScriptPlugin(QString moduleName, QString fileName) {
170 ✗ PythonQt* pqt = PythonQt::self();
171 ✗ PythonQtObjectPtr module = pqt->createModuleFromFile(moduleName, fileName);
172 ✗ if (pqt->handleError()) {
173 ✗ pqt->clearError();
174 ✗ return;
175 }
176 ✗ if (module.isNull()) {
177 ✗ qDebug() << "Enable to load module" << moduleName << "from script"
178 << fileName;
179 ✗ return;
180 }
181 ✗ loadPlugin(moduleName, module);
182 }
183
184 ✗ void PythonWidget::runScript(QString fileName) {
185 ✗ PythonQt* pqt = PythonQt::self();
186 ✗ PythonQtObjectPtr mainContext = pqt->getMainModule();
187 ✗ mainContext.evalFile(fileName);
188
189 ✗ if (pqt->handleError()) {
190 ✗ pqt->clearError();
191 ✗ qDebug() << "Failed to run script" << fileName;
192 }
193 }
194
195 ✗ void PythonWidget::loadModulePlugin(QString moduleName) {
196 ✗ PythonQt* pqt = PythonQt::self();
197 ✗ PythonQtObjectPtr module = pqt->importModule(moduleName);
198 ✗ if (pqt->handleError()) {
199 ✗ pqt->clearError();
200 ✗ return;
201 }
202 ✗ if (module.isNull()) {
203 ✗ qDebug() << "Unable to load module" << moduleName;
204 ✗ return;
205 }
206 ✗ loadPlugin(moduleName, module);
207 }
208
209 ✗ void PythonWidget::loadPlugin(QString moduleName, PythonQtObjectPtr module) {
210 ✗ PythonQt* pqt = PythonQt::self();
211 ✗ MainWindow* main = MainWindow::instance();
212 ✗ module.addObject("windowsManager", main->osg().get());
213
214 ✗ QVariantList args;
215 ✗ args << QVariant::fromValue((QObject*)main);
216 ✗ QVariant instance = module.call("Plugin", args);
217 ✗ module.addVariable(var, instance);
218
219 ✗ QDockWidget* dw = qobject_cast<QDockWidget*>(instance.value<QObject*>());
220 ✗ if (dw) main->insertDockWidget(dw, Qt::RightDockWidgetArea);
221 // PythonQtObjectPtr dockPyObj (instance);
222 ✗ PythonQtObjectPtr dockPyObj = pqt->lookupObject(module, var);
223 ✗ addSignalHandlersToPlugin(dockPyObj);
224 ✗ modules_[moduleName] = module;
225 }
226
227 ✗ void PythonWidget::unloadModulePlugin(QString moduleName) {
228 ✗ if (modules_.contains(moduleName)) {
229 ✗ PythonQtObjectPtr module = modules_.value(moduleName);
230 ✗ unloadModulePlugin(module);
231 ✗ modules_.remove(moduleName);
232 }
233 }
234
235 ✗ void PythonWidget::unloadModulePlugin(PythonQtObjectPtr module) {
236 ✗ PythonQt* pqt = PythonQt::self();
237 ✗ QVariant instance = pqt->getVariable(module, var);
238 ✗ QDockWidget* dw = qobject_cast<QDockWidget*>(instance.value<QObject*>());
239 ✗ if (dw) MainWindow::instance()->removeDockWidget(dw);
240 ✗ module.removeVariable(var);
241 }
242
243 ✗ void PythonWidget::addToContext(QString const& name, QObject* obj) {
244 ✗ PythonQtObjectPtr mainContext = PythonQt::self()->getMainModule();
245 ✗ mainContext.addObject(name, obj);
246 }
247
248 ✗ void PythonWidget::addSignalHandlersToPlugin(PythonQtObjectPtr plugin) {
249 ✗ MainWindow* main = MainWindow::instance();
250 ✗ addSignalHandler(plugin, "osgWidget", main, SIGNAL(viewCreated(OSGWidget*)));
251 ✗ QAction* reconnect = main->findChild<QAction*>("actionReconnect");
252 ✗ if (reconnect)
253 ✗ addSignalHandler(plugin, "resetConnection", reconnect, SIGNAL(triggered()));
254 else
255 ✗ log() << "Could not find actionReconnect button. The plugin will"
256 ✗ << "not be able to reset CORBA connections" << std::endl;
257 ✗ QAction* refresh = main->findChild<QAction*>("actionRefresh");
258 ✗ if (refresh)
259 ✗ addSignalHandler(plugin, "refreshInterface", refresh, SIGNAL(triggered()));
260 else
261 ✗ log() << "Could not find actionRefresh button. The plugin will"
262 ✗ << "not be able to refresh interface." << std::endl;
263 }
264
265 ✗ QVariantList PythonWidget::callPluginMethod(const QString& method,
266 const QVariantList& args,
267 const QVariantMap& kwargs) const {
268 ✗ PythonQt* pqt = PythonQt::self();
269 ✗ QVariantList ret;
270 ✗ foreach (const PythonQtObjectPtr& m, modules_) {
271 ✗ PythonQtObjectPtr dockPyObj = pqt->lookupObject(m, var);
272 ✗ PythonQtObjectPtr call = pqt->lookupCallable(dockPyObj, method);
273 ✗ if (!call.isNull()) {
274 ✗ ret << call.call(args, kwargs);
275 }
276 }
277 ✗ return ret;
278 }
279 } // namespace gui
280 } // namespace gepetto
281