GCC Code Coverage Report


Directory: ./
File: src/gui/pythonwidget.cc
Date: 2024-08-14 11:04:57
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