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

Reply via email to