I'll answer myself here... I just managed to compile a small test program
which executes more C++ centric compared to what is described in e.g.
http://lists.openbossa.org/pipermail/pyside/2010-October/001133.html where a
Python module is created.
I did this by investigating the Shiboken include files and eventually came
up with the following not so nice code:
if(!Shiboken::isShibokenType(instance))
{
qDebug() << "Not of Shiboken type!";
return NULL;
}
Shiboken::SbkBaseWrapper *wrapper =
reinterpret_cast<Shiboken::SbkBaseWrapper*>(instance);
QWidget *widget = reinterpret_cast<QWidget*>(*(wrapper->cptr));
This way I can force a widget to become anything as long as I have full
knowledge about which class I am getting. However, I am not sure if the
PySide designers are grinding their teeth at this. Maybe there is a slightly
more robust method to do this?
This is what I got working (i am not sure if this list accepts attachments):
-----------------------------------------
CMakeLists.txt
-----------------------------------------
cmake_minimum_required(VERSION 2.8)
project(Integration CXX C)
set(MYPREFIX "/usr/lib/cmake")
LIST(APPEND CMAKE_MODULE_PATH
"${MYPREFIX}/Shiboken-0.5.0")
include(ShibokenConfig)
include_directories(${SHIBOKEN_INCLUDE_DIR})
find_package(PythonLibs)
include_directories(${PYTHON_INCLUDE_PATH})
find_package(Qt4 COMPONENTS QtCore QtGui REQUIRED)
include(${QT_USE_FILE})
qt4_wrap_cpp(MOC_FILES MainWindow.h)
add_executable(integrationtest
Main.cc
MainWindow.cc
${MOC_FILES}
)
target_link_libraries(integrationtest
${QT_LIBRARIES}
${PYTHON_LIBRARIES}
${SHIBOKEN_LIBRARY}
)
add_custom_command(
TARGET integrationtest
PRE_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy
${PROJECT_SOURCE_DIR}/MyWidget.py
${PROJECT_BINARY_DIR}/MyWidget.py
)
-----------------------------------------
MyWidget.py
-----------------------------------------
import PySide.QtGui as QtGui
class MyWidget(QtGui.QPushButton):
def __init__(self):
super(MyWidget, self).__init__("Python Button")
self.clicked.connect(self.onClick)
def onClick(self):
QtGui.QMessageBox.information(self,
"Hello from Python",
"This is done in Python")
-----------------------------------------
Main.cc
-----------------------------------------
#include <QtCore>
#include "Python.h"
#include "MainWindow.h"
int main(int argc, char *argv[])
{
Py_Initialize();
QApplication app(argc, argv);
MainWindow w;
w.show();
app.exec();
Py_Finalize();
}
-----------------------------------------
MainWindow.h
-----------------------------------------
#include <shiboken.h>
#include <QtCore>
#include <QtGui>
#include "Python.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
~MainWindow();
void preparePythonCode();
bool checkError();
QWidget* newWidget();
public slots:
void onClick();
private:
PyObject *_compiledClass;
QList<PyObject*> _pythonInstances;
};
-----------------------------------------
MainWindow.cc
-----------------------------------------
#include "MainWindow.h"
///////////////////////////////////////////////////////////////////////////
MainWindow::MainWindow()
{
_compiledClass = NULL;
preparePythonCode();
QWidget *mainWidget = new QWidget();
setCentralWidget(mainWidget);
QLayout *layout = new QVBoxLayout();
mainWidget->setLayout(layout);
QPushButton *cppButton = new QPushButton("C++ button");
layout->addWidget(cppButton);
connect(cppButton, SIGNAL(clicked()),
this, SLOT(onClick()));
QWidget *pyWidget = newWidget();
if(pyWidget)
{
layout->addWidget(pyWidget);
}
}
///////////////////////////////////////////////////////////////////////////
MainWindow::~MainWindow()
{
Py_XDECREF(_compiledClass);
foreach(PyObject *p, _pythonInstances)
{
Py_XDECREF(p);
}
}
///////////////////////////////////////////////////////////////////////////
void MainWindow::onClick()
{
QMessageBox::information(this,
"Hello from C++",
"This is done in C++");
}
///////////////////////////////////////////////////////////////////////////
void MainWindow::preparePythonCode()
{
// Read code.
QFile file("MyWidget.py");
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug() << "Could not find MyWidget.py";
return;
}
QByteArray src = file.readAll();
file.close();
// Compile code.
QString fileString = QString("<%1>").arg(file.fileName());
PyObject *compiledCode = Py_CompileStringFlags(src.data(),
fileString.toAscii().data(),
Py_file_input,
NULL);
if(!compiledCode)
{
checkError();
qDebug() << "Failed to compile code for" << fileString;
return;
}
// Create namespace.
PyObject *dict = PyDict_New();
PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins());
if(checkError())
{
Py_DECREF(compiledCode);
return;
}
// Evaluate code to create class.
PyObject *result =
PyEval_EvalCode(reinterpret_cast<PyCodeObject*>(compiledCode),
dict, dict);
Py_DECREF(compiledCode);
if(!result)
{
checkError();
Py_DECREF(dict);
qDebug() << "Evaluation error of" << fileString;
return;
}
Py_DECREF(result);
// Get class object.
_compiledClass = PyDict_GetItemString(dict, "MyWidget");
Py_DECREF(dict);
if(!_compiledClass)
{
checkError();
qDebug() << "Could not find class";
return;
}
Py_INCREF(_compiledClass);
qDebug() << "Successfully compiled class from" << fileString;
}
///////////////////////////////////////////////////////////////////////////
bool MainWindow::checkError()
{
if(PyErr_Occurred())
{
PyErr_Print();
PyErr_Clear();
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////
QWidget* MainWindow::newWidget()
{
PyObject *instance = PyObject_CallObject(_compiledClass, NULL);
if(!instance)
{
checkError();
qDebug() << "Failed to create instance of Python class";
return NULL;
}
_pythonInstances.append(instance);
if(!Shiboken::isShibokenType(instance))
{
qDebug() << "Not of Shiboken type!";
return NULL;
}
Shiboken::SbkBaseWrapper *wrapper =
reinterpret_cast<Shiboken::SbkBaseWrapper*>(instance);
QWidget *widget = reinterpret_cast<QWidget*>(*(wrapper->cptr));
return widget;
}
2010/10/2 Stefan Larsson <[email protected]>
> Thank you for your answer Hugo.
>
> I have tried to make a simple test but I keep failing when linking the
> program. I have extracted a minimal test case which will not run properly,
> but it reproduces the linking error. (I am using Ubuntu 10.04 32-bit.)
> The error message is shown below. SbkPySide_QtGuiTypes is defined in
> pyside_qtgui_python.h as "extern". I have tried to grep which library it
> resides in but I have so far been unsuccessful (only found in libraries in
> python2.6/site-packages). I would appreciate if anyone could point me in the
> correct direction.
>
> [100%] Building CXX object CMakeFiles/integrationtest.dir/Test.cc.o
> Linking CXX executable integrationtest
> CMakeFiles/integrationtest.dir/Test.cc.o: In function `_typeobject*
> Shiboken::SbkType<QWidget>()':
> /home/stefan/opt/include/PySide/QtGui/pyside_qtgui_python.h:1402: undefined
> reference to `SbkPySide_QtGuiTypes'
> collect2: ld returned 1 exit status
> make[2]: *** [integrationtest] Error 1
> make[1]: *** [CMakeFiles/integrationtest.dir/all] Error 2
> make: *** [all] Error 2
>
> Test.cc:
>
> #include "pyside_qtgui_python.h"
> #include "Python.h"
> #include <QtGui>
>
> int main(int argc, char *argv[])
> {
> PyObject *o;
> QWidget *widget =
> Shiboken::Converter<QWidget*>::toCpp(o);
> }
>
> CMakeLists.txt:
>
> cmake_minimum_required(VERSION 2.8)
> project(Integration CXX C)
>
> # *** CHANGE TO FIT YOUR ENVIRONMENT
> set(MYPREFIX "/home/stefan/opt/lib/cmake")
>
> LIST(APPEND CMAKE_MODULE_PATH
> "${MYPREFIX}/PySide-0.4.1"
> "${MYPREFIX}/Shiboken-0.5.0")
>
> include(ShibokenConfig)
> include(PySideConfig)
> include_directories(${SHIBOKEN_INCLUDE_DIR})
> include_directories(${PYSIDE_INCLUDE_DIR})
> include_directories(${PYSIDE_INCLUDE_DIR}/QtGui)
> include_directories(${PYSIDE_INCLUDE_DIR}/QtCore)
>
> find_package(PythonLibs)
> include_directories(${PYTHON_INCLUDE_PATH})
>
> find_package(Qt4 COMPONENTS QtCore QtGui REQUIRED)
> include(${QT_USE_FILE})
>
> add_executable(integrationtest
> Test.cc
> )
>
> target_link_libraries(integrationtest
> ${QT_LIBRARIES}
> ${PYTHON_LIBRARIES}
> # ${SHIBOKEN_LIBRARY}
> ${PYSIDE_LIBRARY}
> )
>
>
>
>
> 2010/10/1 Hugo Parente Lima <[email protected]>
>
> On Friday 01 October 2010 14:21:38 Stefan Larsson wrote:
>> > Ah, that looks simple enough, thank you.
>> >
>> > This make me think about two new issues which I need to understand
>> before
>> > making a first experiment:
>> >
>> > 1. Can PySide use the QApplication object from the C++ application? I
>> > assume that the object must be registered with PySide in some way such
>> > that PySide understands that a QApplication object is available?
>>
>> Yes, it *will* use the current QApplication.
>>
>> > I assume that I should expect problems when using a QWidget from another
>> > QApplication (with a different QPaintDevice) in different QApplication.
>> > The reason I ask is that when creating a QWidget without a QApplication
>> the
>> > error message "QWidget: Must construct a QApplication before a
>> > QPaintDevice" is obtained...
>>
>> Qt was designed to have just one QApplication instance per process, so the
>> Python code will use your QApplication automatically without changes in
>> your
>> code, our cleanup code uses "QApplication::instance()" so there'll be no
>> problems... only the qApp "macro" will not be available to your python
>> code.
>>
>> > 2. Have you experienced any best practices when it comes to destroying
>> > widgets which are embedded in this way? If a C++ parent widget with a
>> > PySide child is destroyed in the C++ application, would the PySide
>> widget
>> > be destroyed in a proper way or should I pay special attention to such
>> > embedded PySide widgets?
>>
>> Yes, it should be deleted automagically. We suffer from the same problem
>> in
>> pure python programs when Qt deletes some object. If the PySide widget was
>> explicitly created in Python will be even better, because the underlying
>> C++
>> object will have a destructor to tell PySide that the C++ object was
>> deleted.
>>
>> Regards
>>
>> > 2010/10/1 Renato Araujo Oliveira Filho <[email protected]>
>> >
>> > > Hi Stefan,
>> > >
>> > > you can use the header generated by pyside for QtGui module found in
>> > > "$PREFIX/include/PySide/QtGui/pyside_qtgui_python.h", then use these
>> > > functions:
>> > >
>> > > Retrieve the C++ pointer from a Python object:
>> > > cppObj* = Shiboken::Converter< [c++ type] >::toCpp(PythonObject*)
>> > >
>> > > Retrieve the Python object from a c++ object:
>> > > PyObject* = Shiboken::Converter< [c++ type] >::toPython(c++Obj*)
>> > >
>> > > BR
>> > >
>> > > On Fri, Oct 1, 2010 at 8:37 AM, Stefan Larsson <[email protected]>
>> wrote:
>> > > > Hello,
>> > > > I am writing a C++ application using Qt4 and I am using Python
>> embedded
>> > >
>> > > in
>> > >
>> > > > the application as a scripting backend for simple plugin
>> development.
>> > > > My question is if it is possible to acquire a QWidget* pointer from
>> > >
>> > > Pyside
>> > >
>> > > > such that custom widgets written using Pyside can be embedded
>> directly
>> > > > within the C++ application? If not, would it be difficult to
>> implement
>> > >
>> > > such
>> > >
>> > > > a feature?
>> > > > I have also looked at PythonQt, but it seems as if Pyside has better
>> > > > potential.
>> > > > Best Regards,
>> > > > Stefan Larsson
>> > > >
>> > > > _______________________________________________
>> > > > PySide mailing list
>> > > > [email protected]
>> > > > http://lists.openbossa.org/listinfo/pyside
>> > >
>> > > --
>> > > Renato Araujo Oliveira Filho
>> > > Instituto Nokia de Tecnologia - INdT
>> > > Mobile: +55 (81) 8704-2144
>>
>> --
>> Hugo Parente Lima
>> INdT - Instituto Nokia de Tecnologia
>>
>
>
_______________________________________________
PySide mailing list
[email protected]
http://lists.openbossa.org/listinfo/pyside