GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/gui/pythonwidget.cc Lines: 0 152 0.0 %
Date: 2024-04-14 11:13:22 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(
91
      QStringList() << "BodyTreeWidget" << "BodyTreeItem" << "SelectionEvent"
92
                    << "MainWindow");
93
94
  PythonQt::self()->registerCPPClass("MainWindow", "QMainWindow", "gepetto");
95
  PythonQt::self()->registerCPPClass("OSGWidget", "QWidget", "gepetto");
96
97
  console_->QTextEdit::clear();
98
  console_->consoleMessage(
99
      "PythonQt command prompt\n"
100
      "Use Shift+Enter for multiline code.\n");
101
  console_->appendCommandPrompt();
102
103
  QWidget* widget = new QWidget;
104
  QVBoxLayout* layout = new QVBoxLayout;
105
  button_ = new QPushButton;
106
107
  button_->setText("Choose file");
108
  layout->addWidget(console_);
109
  layout->addWidget(button_);
110
  widget->setLayout(layout);
111
  this->setWidget(widget);
112
113
  toggleViewAction()->setShortcut(gepetto::gui::DockKeyShortcutBase +
114
                                  Qt::Key_A);
115
  connect(button_, SIGNAL(clicked()), SLOT(browseFile()));
116
117
  bp::import("gepettogui_internals");
118
}
119
120
PythonWidget::~PythonWidget() {
121
  foreach (const PythonQtObjectPtr& m, modules_) unloadModulePlugin(m);
122
  PythonQt::cleanup();
123
}
124
125
void PythonWidget::browseFile() {
126
  QFileDialog* fd = new QFileDialog;
127
128
  fd->setFileMode(QFileDialog::ExistingFile);
129
  fd->setNameFilter("All python file (*.py)");
130
  if (fd->exec() == QDialog::Accepted) {
131
    QStringList file = fd->selectedFiles();
132
133
    PythonQtObjectPtr mainContext = PythonQt::self()->getMainModule();
134
    mainContext.evalFile(file.at(0));
135
  }
136
  fd->close();
137
  fd->deleteLater();
138
}
139
140
void PythonWidget::saveHistory(QSettings& settings) {
141
  settings.beginGroup("pythonqt");
142
  QStringList history = console_->history();
143
  int limit = 200;
144
  int start = std::max(history.length() - limit, 0);
145
  QList<QVariant> h;
146
  foreach (QString s, history.mid(start)) {
147
    h << s;
148
  }
149
  settings.setValue("history", h);
150
  settings.endGroup();
151
}
152
153
void PythonWidget::restoreHistory(QSettings& settings) {
154
  settings.beginGroup("pythonqt");
155
  QList<QVariant> h = settings.value("history").toList();
156
  QStringList history;
157
  foreach (QVariant v, h) {
158
    history << v.toString();
159
  }
160
  console_->setHistory(history);
161
  settings.endGroup();
162
}
163
164
bool PythonWidget::hasPlugin(const QString& name) {
165
  return modules_.contains(name);
166
}
167
168
void PythonWidget::loadScriptPlugin(QString moduleName, QString fileName) {
169
  PythonQt* pqt = PythonQt::self();
170
  PythonQtObjectPtr module = pqt->createModuleFromFile(moduleName, fileName);
171
  if (pqt->handleError()) {
172
    pqt->clearError();
173
    return;
174
  }
175
  if (module.isNull()) {
176
    qDebug() << "Enable to load module" << moduleName << "from script"
177
             << fileName;
178
    return;
179
  }
180
  loadPlugin(moduleName, module);
181
}
182
183
void PythonWidget::runScript(QString fileName) {
184
  PythonQt* pqt = PythonQt::self();
185
  PythonQtObjectPtr mainContext = pqt->getMainModule();
186
  mainContext.evalFile(fileName);
187
188
  if (pqt->handleError()) {
189
    pqt->clearError();
190
    qDebug() << "Failed to run script" << fileName;
191
  }
192
}
193
194
void PythonWidget::loadModulePlugin(QString moduleName) {
195
  PythonQt* pqt = PythonQt::self();
196
  PythonQtObjectPtr module = pqt->importModule(moduleName);
197
  if (pqt->handleError()) {
198
    pqt->clearError();
199
    return;
200
  }
201
  if (module.isNull()) {
202
    qDebug() << "Unable to load module" << moduleName;
203
    return;
204
  }
205
  loadPlugin(moduleName, module);
206
}
207
208
void PythonWidget::loadPlugin(QString moduleName, PythonQtObjectPtr module) {
209
  PythonQt* pqt = PythonQt::self();
210
  MainWindow* main = MainWindow::instance();
211
  module.addObject("windowsManager", main->osg().get());
212
213
  QVariantList args;
214
  args << QVariant::fromValue((QObject*)main);
215
  QVariant instance = module.call("Plugin", args);
216
  module.addVariable(var, instance);
217
218
  QDockWidget* dw = qobject_cast<QDockWidget*>(instance.value<QObject*>());
219
  if (dw) main->insertDockWidget(dw, Qt::RightDockWidgetArea);
220
  // PythonQtObjectPtr dockPyObj (instance);
221
  PythonQtObjectPtr dockPyObj = pqt->lookupObject(module, var);
222
  addSignalHandlersToPlugin(dockPyObj);
223
  modules_[moduleName] = module;
224
}
225
226
void PythonWidget::unloadModulePlugin(QString moduleName) {
227
  if (modules_.contains(moduleName)) {
228
    PythonQtObjectPtr module = modules_.value(moduleName);
229
    unloadModulePlugin(module);
230
    modules_.remove(moduleName);
231
  }
232
}
233
234
void PythonWidget::unloadModulePlugin(PythonQtObjectPtr module) {
235
  PythonQt* pqt = PythonQt::self();
236
  QVariant instance = pqt->getVariable(module, var);
237
  QDockWidget* dw = qobject_cast<QDockWidget*>(instance.value<QObject*>());
238
  if (dw) MainWindow::instance()->removeDockWidget(dw);
239
  module.removeVariable(var);
240
}
241
242
void PythonWidget::addToContext(QString const& name, QObject* obj) {
243
  PythonQtObjectPtr mainContext = PythonQt::self()->getMainModule();
244
  mainContext.addObject(name, obj);
245
}
246
247
void PythonWidget::addSignalHandlersToPlugin(PythonQtObjectPtr plugin) {
248
  MainWindow* main = MainWindow::instance();
249
  addSignalHandler(plugin, "osgWidget", main, SIGNAL(viewCreated(OSGWidget*)));
250
  QAction* reconnect = main->findChild<QAction*>("actionReconnect");
251
  if (reconnect)
252
    addSignalHandler(plugin, "resetConnection", reconnect, SIGNAL(triggered()));
253
  else
254
    log() << "Could not find actionReconnect button. The plugin will"
255
          << "not be able to reset CORBA connections" << std::endl;
256
  QAction* refresh = main->findChild<QAction*>("actionRefresh");
257
  if (refresh)
258
    addSignalHandler(plugin, "refreshInterface", refresh, SIGNAL(triggered()));
259
  else
260
    log() << "Could not find actionRefresh button. The plugin will"
261
          << "not be able to refresh interface." << std::endl;
262
}
263
264
QVariantList PythonWidget::callPluginMethod(const QString& method,
265
                                            const QVariantList& args,
266
                                            const QVariantMap& kwargs) const {
267
  PythonQt* pqt = PythonQt::self();
268
  QVariantList ret;
269
  foreach (const PythonQtObjectPtr& m, modules_) {
270
    PythonQtObjectPtr dockPyObj = pqt->lookupObject(m, var);
271
    PythonQtObjectPtr call = pqt->lookupCallable(dockPyObj, method);
272
    if (!call.isNull()) {
273
      ret << call.call(args, kwargs);
274
    }
275
  }
276
  return ret;
277
}
278
}  // namespace gui
279
}  // namespace gepetto