#ifndef _PYTHONQT_H1#define _PYTHONQT_H23/*4*5* Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.6*7* This library is free software; you can redistribute it and/or8* modify it under the terms of the GNU Lesser General Public9* License as published by the Free Software Foundation; either10* version 2.1 of the License, or (at your option) any later version.11*12* This library is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU15* Lesser General Public License for more details.16*17* Further, this software is distributed without any warranty that it is18* free of the rightful claim of any third person regarding infringement19* or the like. Any license provided herein, whether implied or20* otherwise, applies only to this software file. Patent licenses, if21* any, provided herein do not apply to combinations of this program with22* other software, or any other product whatsoever.23*24* You should have received a copy of the GNU Lesser General Public25* License along with this library; if not, write to the Free Software26* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA27*28* Contact information: MeVis Research GmbH, Universitaetsallee 29,29* 28359 Bremen, Germany or:30*31* http://www.mevis.de32*33*/3435//----------------------------------------------------------------------------------36/*!37// \file PythonQt.h38// \author Florian Link39// \author Last changed by $Author: florian $40// \date 2006-0541*/42//----------------------------------------------------------------------------------4344#include "PythonQtSystem.h"45#include "PythonQtWrapper.h"46#include "PythonQtVariantWrapper.h"47#include "PythonQtMetaObjectWrapper.h"48#include "PythonQtSlot.h"49#include "PythonQtObjectPtr.h"50#include <QObject>51#include <QVariant>52#include <QList>53#include <QHash>54#include <QByteArray>55#include <QStringList>56#include <QtDebug>57#include <iostream>585960class PythonQtClassInfo;61class PythonQtPrivate;62class PythonQtMethodInfo;63class PythonQtSignalReceiver;64class PythonQtImportFileInterface;65class PythonQtCppWrapperFactory;66class PythonQtConstructorHandler;6768//! the main interface to the Python Qt binding, realized as a singleton69class PYTHONQT_EXPORT PythonQt : public QObject {7071Q_OBJECT7273public:74enum InitFlags {75RedirectStdOut = 1, //!<< sets if the std out/err is redirected to pythonStdOut() and pythonStdErr() signals76IgnoreSiteModule = 2, //!<< sets if Python should ignore the site module77ExternalHelp = 4 //!<< sets if help() calls on PythonQt modules are forwarded to the pythonHelpRequest() signal78};7980//! initialize the python qt binding (flags are a or combination of InitFlags)81static void init(int flags = IgnoreSiteModule | RedirectStdOut);8283//! cleanup84static void cleanup();8586//! get the singleton instance87static PythonQt* self() { return _self; }8889//-----------------------------------------------------------------------------90// Public API:9192//! defines the object types for introspection93enum ObjectType {94Class,95Function,96Variable,97Module,98Anything,99CallOverloads100};101102//! overwrite the python sys path (call this directly after PythonQt::init() if you want to change the std python sys path)103void overwriteSysPath(const QStringList& paths);104105//! sets the __path__ list of a module to the given list (important for local imports)106void setModuleImportPath(PyObject* module, const QStringList& paths);107108//! get the __main__ module of python109PythonQtObjectPtr getMainModule();110111//! registers a QObject derived class to PythonQt (this is implicitly called by addObject as well)112//! All added metaobjects will be visible under the className in the PythonQt module as MetaObjectWrappers and the enums113//! and constructors (added by addConstructors) will be available.114/* Since Qt4 does not offer a way to detect if a given classname is derived from QObject and thus has a QMetaObject,115you MUST register all your QObject derived classes here when you want them to be detected in signal and slot calls */116void registerClass(const QMetaObject* metaobject);117118//! as an alternative to registerClass, you can tell PythonQt the names of QObject derived classes119//! and it will register the classes when it first sees a pointer to such a derived class120void registerQObjectClassNames(const QStringList& names);121122//! this will register CPP classnames as known CPP classes (NOT QObjects) and make their MetaObjectWrapper available in123//! the PythonQt module. In combination with addConstuctors(), this can be used to create CPP objects from PythonQt124void registerCPPClassNames(const QStringList& names);125126//! parses the given file and returns the python code object, this can then be used to call evalCode()127PythonQtObjectPtr parseFile(const QString& filename);128129//! evaluates the given code and returns the result value (use Py_Compile etc. to create pycode from string)130//! If pycode is NULL, a python error is printed.131QVariant evalCode(PyObject* module, PyObject* pycode);132133//! evaluates the given script code and returns the result value134QVariant evalScript(PyObject* module, const QString& script, int start = Py_file_input);135136//! evaluates the given script code from file137void evalFile(PyObject* module, const QString& filename);138139//@{ Signal handlers140141//! add a signal handler to the given \c signal of \c obj and connect it to a callable \c objectname in module142bool addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname);143144//! remove a signal handler from the given \c signal of \c obj145bool removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname);146147//! add a signal handler to the given \c signal of \c obj and connect it to a callable \c receiver148bool addSignalHandler(QObject* obj, const char* signal, PyObject* receiver);149150//! remove a signal handler from the given \c signal of \c obj151bool removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver);152153//@}154155//@{ Variable access156157//! add the given \c object to the \c module as a variable with \c name (it can be removed via clearVariable)158void addObject(PyObject* module, const QString& name, QObject* object);159160//! add the given variable to the module161void addVariable(PyObject* module, const QString& name, const QVariant& v);162163//! remove the given variable164void removeVariable(PyObject* module, const QString& name);165166//! get the variable with the \c name of the \c module, returns an invalid QVariant on error167QVariant getVariable(PyObject* module, const QString& name);168169//! read vars etc. in scope of a module, optional looking inside of an object \c objectname170QStringList introspection(PyObject* module, const QString& objectname, ObjectType type);171172//! returns the found callable object or NULL173//! @return new reference174PythonQtObjectPtr lookupCallable(PyObject* module, const QString& name);175176//@}177178//@{ Calling of python callables179180//! call the given python method, returns the result converted to a QVariant181QVariant call(PyObject* module, const QString& callable, const QVariantList& args);182183//@}184185//@{ Decorations, constructors, wrappers...186187188//! add an object whose slots will be used as decorator slots for189//! other QObjects or CPP classes. The slots need to follow the190//! convention that the first argument is a pointer to the wrapped object.191//! (ownership is passed to PythonQt)192/*!193Example:194195A slot with the signature196197\code198bool doSomething(QWidget* w, int a)199\endcode200201will extend QWidget instances (and derived classes) with a "bool doSomething(int a)" slot202that will be called with the concrete instance as first argument.203So in Python you can now e.g. call204205\code206someWidget.doSomething(12)207\endcode208209without QWidget really having this method. This allows to easily make normal methods210of Qt classes callable by forwarding them with such decorator slots211or to make CPP classes (which are not derived from QObject) callable from Python.212*/213void addInstanceDecorators(QObject* o);214215//! add an object whose slots will be used as decorator slots for216//! class objects (ownership is passed to PythonQt)217/*!218The slots need to follow the following convention:219- SomeClass* new_SomeClass(...)220- QVariant new_SomeClass(...)221- void delete_SomeClass(SomeClass*)222- ... static_SomeClass_someName(...)223224This will add:225- a constructor226- a constructor which generates a QVariant227- a destructor (only useful for CPP objects)228- a static decorator slot which will be available on the MetaObject (visible in PythonQt module)229230*/231void addClassDecorators(QObject* o);232233//! this will add the object both as class and instance decorator (ownership is passed to PythonQt)234void addDecorators(QObject* o);235236//! add a wrapper object for the given QMetaType typeName, also does an addClassDecorators() to add constructors for variants237//! (ownership of wrapper is passed to PythonQt)238/*! Make sure that you have done a qRegisterMetaType first, if typeName is a user type!239240This will add a wrapper object that is used to make calls to the given classname \c typeName.241All slots that take a pointer to typeName as the first argument will be callable from Python on242a variant object that contains such a type.243*/244void addVariantWrapper(const char* typeName, QObject* wrapper);245246//! add the given factory to PythonQt (ownership stays with caller)247void addWrapperFactory(PythonQtCppWrapperFactory* factory);248249//! add the given constructor handler to PythonQt (ownership stays with caller)250void addConstructorHandler(PythonQtConstructorHandler* handler);251252//! get list of constructor handlers253const QList<PythonQtConstructorHandler*>& constructorHandlers();254255//@}256257//@{ Custom importer (to replace internal import implementation of python)258259//! replace the internal import implementation and use the supplied interface to load files (both py and pyc files)260//! (this method should be called directly after initialization of init() and before calling overwriteSysPath().261//! It can only be called once, further calls will be ignored silently. (ownership stays with caller)262void setImporter(PythonQtImportFileInterface* importInterface);263264//! set paths that the importer should ignore265void setImporterIgnorePaths(const QStringList& paths);266267//! get paths that the importer should ignore268const QStringList& getImporterIgnorePaths();269270//@}271272//! get access to internal data (should not be used on the public API, but is used by some C functions)273static PythonQtPrivate* priv() { return _self->_p; }274275//! get access to the file importer (if set)276static PythonQtImportFileInterface* importInterface();277278//! handle a python error, call this when a python function fails. If no error occurred, it returns false.279//! The error is currently just output to the python stderr, future version might implement better trace printing280bool handleError();281282signals:283//! emitted when python outputs something to stdout (and redirection is turned on)284void pythonStdOut(const QString& str);285//! emitted when python outputs something to stderr (and redirection is turned on)286void pythonStdErr(const QString& str);287288//! emitted when help() is called on a PythonQt object and \c ExternalHelp is enabled289void pythonHelpRequest(const QByteArray& cppClassName);290291292public:293//! called by internal help methods294PyObject* helpCalled(PythonQtClassInfo* info);295296private:297void initPythonQtModule(bool redirectStdOut);298299//! callback for stdout redirection, emits pythonStdOut signal300static void stdOutRedirectCB(const QString& str);301//! callback for stderr redirection, emits pythonStdErr signal302static void stdErrRedirectCB(const QString& str);303304//! returns the found object or NULL305//! @return new reference306PythonQtObjectPtr lookupObject(PyObject* module, const QString& name);307308//! get (and create if not available) the signal receiver of that QObject, signal receiver is made child of the passed \c obj309PythonQtSignalReceiver* getSignalReceiver(QObject* obj);310311PythonQt(int flags);312~PythonQt();313314static PythonQt* _self;315316PythonQtPrivate* _p;317318};319320//! internal PythonQt details321class PythonQtPrivate : public QObject {322323Q_OBJECT324325public:326PythonQtPrivate();327~PythonQtPrivate();328329//! returns if the id is the id for PythonQtObjectPtr330bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; }331332//! remove the wrapper ptr again333void removeWrapperPointer(void* obj) { _wrappedObjects.take(obj); }334335//! wrap the given QObject into a Python object (or return existing wrapper!)336PyObject* wrapQObject(QObject* obj);337338//! wrap the given ptr into a Python object (or return existing wrapper!) if there is a known QObject of that name or a known wrapper in the factory339PyObject* wrapPtr(void* ptr, const QByteArray& name);340341//! registers a QObject derived class to PythonQt (this is implicitly called by addObject as well)342/* Since Qt4 does not offer a way to detect if a given classname is derived from QObject and thus has a QMetaObject,343you MUST register all your QObject derived classes here when you want them to be detected in signal and slot calls */344void registerClass(const QMetaObject* metaobject);345346//! as an alternative to registerClass, you can tell PythonQt the names of QObject derived classes347//! and it will register the classes when it first sees a pointer to such a derived class348void registerQObjectClassNames(const QStringList& names);349350//! add a decorator object351void addDecorators(QObject* o, bool instanceDeco, bool classDeco);352353//! add a wrapper object for the given qvariant, also does an addConstructors() to add constructors for variants354void addVariantWrapper(const char* typeName, QObject* wrapper);355356//! get list of all slots that are available as decorator slots357QList<PythonQtSlotInfo*> getDecoratorSlots(const QByteArray& className);358359//! check if the enum is either part of the \c meta class or contains a scope and is360//! an enum of another known metaobject (and as last resort, of the Qt namespace)361bool isEnumType(const QMetaObject* meta, const QByteArray& name);362363//! helper method that creates a PythonQtMetaObjectWrapper object364PythonQtMetaObjectWrapper* createNewPythonQtMetaObjectWrapper(PythonQtClassInfo* info);365366//! helper method that creates a PythonQtWrapper object and registers it in the object map367PythonQtWrapper* createNewPythonQtWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr = NULL);368369//! helper method that creates a PythonQtVariantWrapper object370PythonQtVariantWrapper* createNewPythonQtVariantWrapper(const QVariant& variant);371372//! get the class info for a meta object (if available)373PythonQtClassInfo* getClassInfo(const QMetaObject* meta) { return _knownQtClasses.value(meta->className()); }374375//! get the constructor slot for the given classname376PythonQtSlotInfo* getConstructorSlot(const QByteArray& className) { return _constructorSlots.value(className); }377378//! get the destructor slot for the given classname379PythonQtSlotInfo* getDestructorSlot(const QByteArray& className) { return _destructorSlots.value(className); }380381protected slots:382//! called when a wrapped QObject is destroyed383void wrappedObjectDestroyed(QObject* obj);384385//! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map386void destroyedSignalEmitter(QObject* obj);387388private:389390//! stores pointer to PyObject mapping of wrapped QObjects AND C++ objects391QHash<void* , PythonQtWrapper *> _wrappedObjects;392393//! stores the meta info of known Qt classes394QHash<QByteArray, PythonQtClassInfo *> _knownQtClasses;395396//! stores the meta info of known Qt classes397QHash<QByteArray, PythonQtClassInfo *> _knownQtWrapperClasses;398399//! stores the meta info of known Qt C++ wrapper classes400QMultiHash<QByteArray, PythonQtSlotInfo *> _knownQtDecoratorSlots;401402//! names of qobject derived classes that can be casted to qobject safely403QHash<QByteArray, bool> _knownQObjectClassNames;404405//! stores signal receivers for QObjects406QHash<QObject* , PythonQtSignalReceiver *> _signalReceivers;407408//! the PythonQt python module409PythonQtObjectPtr _pythonQtModule;410411//! the importer interface (if set)412PythonQtImportFileInterface* _importInterface;413414QStringList _importIgnorePaths;415416//! the cpp object wrapper factories417QList<PythonQtCppWrapperFactory*> _cppWrapperFactories;418419//! the cpp object wrapper factories420QList<PythonQtConstructorHandler*> _constructorHandlers;421422QHash<QByteArray , PythonQtSlotInfo *> _constructorSlots;423QHash<QByteArray , PythonQtSlotInfo *> _destructorSlots;424425QHash<int , QPair<PythonQtClassInfo*, QObject*> > _knownVariantWrappers;426427PythonQtClassInfo* _qtNamespace;428429int _initFlags;430int _PythonQtObjectPtr_metaId;431432friend class PythonQt;433};434435#endif436437438