GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/gui/pythonwidget.cc Lines: 0 153 0.0 %
Date: 2023-03-14 11:04:37 Branches: 0 446 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