/********************************************************************** PythonTool - PythonTool Tool for Avogadro Copyright (C) 2008,2009 Tim Vandermeersch This file is part of the Avogadro molecular editor project. For more information, see Avogadro is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Avogadro is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **********************************************************************/ #include "pythontool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace Eigen; using namespace boost::python; namespace Avogadro { class PythonToolPrivate { public: PythonToolPrivate() : script(0), settingsWidget(0) {} PythonInterpreter interpreter; PythonScript *script; boost::python::object instance; QWidget *settingsWidget; }; PythonTool::PythonTool(QObject *parent, const QString &filename) : Tool(parent), d(new PythonToolPrivate) { QAction *action = activateAction(); action->setIcon(QIcon(QString::fromUtf8(":/python/python.png"))); //action->setShortcut(Qt::Key_F12); loadScript(filename); if (PyObject_HasAttrString(d->instance.ptr(), "toolTip")) { try { prepareToCatchError(); const char *toolTip = extract(d->instance.attr("toolTip")()); action->setToolTip(QString(toolTip)); } catch(error_already_set const &) { catchError(); } } } PythonTool::~PythonTool() { if (d->script) delete d->script; if (d->settingsWidget) d->settingsWidget->deleteLater(); delete d; } QString PythonTool::identifier() const { if (!PyObject_HasAttrString(d->instance.ptr(), "identifier")) return "Python Tool"; try { prepareToCatchError(); const char *name = extract(d->instance.attr("identifier")()); return QString(name); } catch(error_already_set const &) { catchError(); return "Python Tool"; } } QString PythonTool::name() const { if (!PyObject_HasAttrString(d->instance.ptr(), "name")) return tr("Unknown Python Tool"); try { prepareToCatchError(); const char *name = extract(d->instance.attr("name")()); return QString(name); } catch(error_already_set const &) { catchError(); return tr("Unknown Python Tool"); } } QString PythonTool::description() const { if (!PyObject_HasAttrString(d->instance.ptr(), "description")) return tr("N/A"); try { prepareToCatchError(); const char *desc = extract(d->instance.attr("description")()); return QString(desc); } catch(error_already_set const &) { catchError(); return tr("N/A"); } } QUndoCommand* PythonTool::mouseEvent(const QString &what, GLWidget *widget, QMouseEvent *event) { if (!PyObject_HasAttrString(d->instance.ptr(), what.toStdString().c_str())) return 0; try { prepareToCatchError(); boost::python::reference_existing_object::apply::type converter; PyObject *obj = converter(widget); object real_obj = object(handle<>(obj)); boost::python::return_by_value::apply::type qconverter; PyObject *qobj = qconverter(event); object real_qobj = object(handle<>(qobj)); return extract(d->instance.attr(what.toStdString().c_str())(real_obj, real_qobj)); } catch(error_already_set const &) { catchError(); } return 0; } QUndoCommand* PythonTool::mousePressEvent(GLWidget *widget, QMouseEvent *event) { return mouseEvent("mousePressEvent", widget, event); } QUndoCommand* PythonTool::mouseMoveEvent(GLWidget *widget, QMouseEvent *event) { return mouseEvent("mouseMoveEvent", widget, event); } QUndoCommand* PythonTool::mouseReleaseEvent(GLWidget *widget, QMouseEvent *event) { return mouseEvent("mouseReleaseEvent", widget, event); } QUndoCommand* PythonTool::wheelEvent(GLWidget *widget, QWheelEvent *event) { if (!PyObject_HasAttrString(d->instance.ptr(), "wheelEvent")) return 0; try { prepareToCatchError(); boost::python::reference_existing_object::apply::type converter; PyObject *obj = converter(widget); object real_obj = object(handle<>(obj)); boost::python::return_by_value::apply::type qconverter; PyObject *qobj = qconverter(event); object real_qobj = object(handle<>(qobj)); return extract(d->instance.attr("wheelEvent")(real_obj, real_qobj)); } catch(error_already_set const &) { catchError(); } return 0; } bool PythonTool::paint(GLWidget *widget) { if (!PyObject_HasAttrString(d->instance.ptr(), "paint")) return false; try { prepareToCatchError(); boost::python::reference_existing_object::apply::type converter; PyObject *obj = converter(widget); object real_obj = object(handle<>(obj)); d->instance.attr("paint")(real_obj); } catch(error_already_set const &) { catchError(); } return true; } QWidget* PythonTool::settingsWidget() { if (!d->script) return 0; // nothing we can do -- we don't have any real scripts if(!d->settingsWidget) { d->settingsWidget = new QWidget(); d->settingsWidget->setLayout( new QVBoxLayout() ); if (PyObject_HasAttrString(d->instance.ptr(), "settingsWidget")) { try { prepareToCatchError(); QWidget *widget = extract(d->instance.attr("settingsWidget")()); if (widget) d->settingsWidget->layout()->addWidget(widget); } catch (error_already_set const &) { catchError(); } } connect(d->settingsWidget, SIGNAL(destroyed()), this, SLOT(settingsWidgetDestroyed())); } return d->settingsWidget; } void PythonTool::settingsWidgetDestroyed() { d->settingsWidget = 0; } void PythonTool::readSettings(QSettings &settings) { Tool::readSettings(settings); if (!d->script) return; if (!PyObject_HasAttrString(d->instance.ptr(), "readSettings")) return; try { prepareToCatchError(); boost::python::return_by_value::apply::type qconverter; PyObject *qobj = qconverter(&settings); object real_qobj = object(handle<>(qobj)); d->instance.attr("readSettings")(real_qobj); } catch(error_already_set const &) { catchError(); } } void PythonTool::writeSettings(QSettings &settings) const { Tool::writeSettings(settings); if (!d->script) return; if (!PyObject_HasAttrString(d->instance.ptr(), "writeSettings")) return; try { prepareToCatchError(); boost::python::return_by_value::apply::type qconverter; PyObject *qobj = qconverter(&settings); object real_qobj = object(handle<>(qobj)); d->instance.attr("writeSettings")(real_qobj); } catch(error_already_set const &) { catchError(); } } void PythonTool::loadScript(const QString &filename) { QFileInfo info(filename); d->interpreter.addSearchPath(info.canonicalPath()); PythonScript *script = new PythonScript(filename); if(script->module()) { // make sure there is a Tool class defined if (PyObject_HasAttrString(script->module().ptr(), "Tool")) { try { prepareToCatchError(); // instantiate the new tool d->instance = script->module().attr("Tool")(); // if we have a settings widget already, add the python content... if (d->settingsWidget) { if (PyObject_HasAttrString(d->instance.ptr(), "settingsWidget")) { QWidget *widget = extract(d->instance.attr("settingsWidget")()); if (widget) d->settingsWidget->layout()->addWidget(widget); } } } catch (error_already_set const &) { catchError(); return; } d->script = script; } else { delete script; pythonError()->append(tr("PythonTool: checking ") + filename + "..."); pythonError()->append(tr(" - script has no 'Tool' class defined")); } } else { delete script; pythonError()->append(tr("PythonTool: checking ") + filename + "..."); pythonError()->append(tr(" - no module")); } } } #include "pythontool.moc"