【问题标题】:import classes from pyside inside of a boost python module?从boost python模块中的pyside导入类?
【发布时间】:2013-08-30 23:17:14
【问题描述】:

我想使用 PySide 来定义基本的 QT 类以及 C++ 和 python 之间的映射,但是要在独立的 python 代码和使用 boost::python 的嵌入式 python 中这样做。

一、模块定义和返回QPointF的类:

QPointF  X::getY() { 
  return QPointF(); 
}    

BOOST_PYTHON_MODULE(myBoostPythonModule)
{
// is there some magic init/register commands to put here?
boost::python::api::object module     = import("__main__");
boost::python::api::object name_space = module.attr("__dict__");
boost::python::exec("from PySide.QtCore import *",name_space,name_space);
boost::python::exec("import sys,os\nprint(sys.modules)",name_space,name_space);

class_<X, boost::noncopyable>(
            "X", init<const char*>())
        .def("getY",&X::getY)
        ;
}

现在,应用程序的嵌入式 python 代码,最后一行是失败的,我想知道如何解决:

execute("import myBoostPythonModule");     // OK
execute("x=myBoostPythonModule.X('foo')"); // OK

execute("w=QPointF()\nprint(w)");          // OK
// PySide.QtCore.QPointF(0.000000, 0.000000)

execute("y=x.getY()");                     // FAIL:
// TypeError: No to_python (by-value) converter found for C++ type: QPointF

这里发生了什么,我可以创建一个 QPointF,但名称不知何故未绑定在 python 和 c++ 之间?我是否缺少模块中的一些导入来告诉它从 PySide 导入?

【问题讨论】:

    标签: c++ python qt pyside boost-python


    【解决方案1】:

    PySide 通过Shiboken 提供其Qt 绑定。 Shiboken 生成支持其自己的类型转换系统的 Python C API 绑定。这些转换的知识存在于 Shiboken 生成的绑定中,而不是 Python 类型系统中。因此,PySide 知道如何将 QPointF 对象与 C++/Python 相互转换; Python 的类型系统没有。

    当对象通过 Boost.Python 公开的函数进行转换时,Boost.Python 将检查其注册表以查找适当的类型转换器。这些转换器为 Boost.Python 提供了有关如何将通过 Boost.Python 公开的类型转换为/从 C++/Python 转换的知识。因此,当 Boost.Python 尝试将 QPointF C++ 类型返回给 Python 时,它会抛出异常,因为该转换尚未在 Boost.Python 中注册。

    这里是注释代码:

    import myBoostPythonModule
    from PySide.QtCore import *
    ...
    x=myBoostPythonModule.X('foo') # Boost.Python knows how to convert C++ X
                                   # to Python X.  Python's type system does not.
    
    w=QPointF()                    # Shiboken knows how to convert C++ QPointF to
                                   # Python QPointF.  Python's type system does not.
    print(w)                       # Shiboken knows how to represent C++ QPointF as
                                   # a string.
    
    y=x.getY()                     # Boost.Python knows how to invoke X::getY(),
                                   # but only Shiboken knows how to convert C++
                                   # QPointF to Python QPointF.  Thus, the TypeError
                                   # exception is raised.
    

    Boost.Python 的converters 可以用另一个实现来实现。扩展Shiboken type converter example,下面是使用 Shiboken 的旧类型转换器实现的 Boost.Python 转换器的完整示例。我会使用新的 Shiboken 类型转换器 API,但我不清楚它基于文档是什么。

    #include <iostream>
    
    #include <boost/python.hpp>
    
    /// @brief Mockup Complex class from Shiboken documentation.
    class Complex
    {
    public:
      Complex(double real, double imaginary)
        : real_(real),
          imaginary_(imaginary)
      {}
    
      double real() const      { return real_;      }
      double imaginary() const { return imaginary_; }
    
    private:
      double real_;
      double imaginary_;
    };
    
    /// @brief Mocked up Shiboken converter.
    namespace Shiboken {
    
    template <typename> struct Converter;
    
    template <> struct Converter<Complex>
    {
    public:
      // ...
    
      static inline bool isConvertible(PyObject* pyObj)
      {
        std::cout << "Shiboken::Converter<Complex>::isConvertible()" << std::endl;
        return PyComplex_Check(pyObj);
      }
    
      // ...
    
      static inline PyObject* toPython(const Complex& cpx)
      {
        std::cout << "Shiboken::Converter<Complex>::toPython()" << std::endl;
        return PyComplex_FromDoubles(cpx.real(), cpx.imaginary());
      }
    
      static inline Complex toCpp(PyObject* pyobj)
      {
        std::cout << "Shiboken::Converter<Complex>::toCpp()" << std::endl;
        double real      =  PyComplex_RealAsDouble(pyobj);
        double imaginary =  PyComplex_ImagAsDouble(pyobj);
        return Complex(real, imaginary);
      }
    };
    } // namespace Shiboken
    
    /// @brief Type used to convert a complex to Python.
    struct complex_converter_to_python
    {
      static PyObject* convert(const Complex& c)
      {
        // Delegate to Shiboken.
        std::cout << "complex_converter_to_python::convert()" << std::endl;
        return Shiboken::Converter<Complex>::toPython(c);
      }
    };
    
    /// @brief Type that registers a Python Complex type to C++
    ///        Complex when passing through Boost.Python.
    struct complex_converter_from_python
    {
      /// @note Registers converter from a python complex to C++ complex.
      complex_converter_from_python()
      {
        boost::python::converter::registry::push_back(
          &complex_converter_from_python::convertible,
          &complex_converter_from_python::construct,
          boost::python::type_id<Complex>());
      }
    
      /// @brief Check if PyObject is a Complex.
      static void* convertible(PyObject* object)
      {
        // Delegate to Shiboken.  Based on the documentation, the
        // isConvertible function is gone, so explicit checking may
        // be required based on the version of Shiboken.
        std::cout << "complex_converter_from_python::convertible()" << std::endl;
        return Shiboken::Converter<Complex>::isConvertible(object)
          ? object 
          : NULL;
      }
    
      /// @brief Convert Python Complex to C++ Complex.
      static void construct(
        PyObject* object,
        boost::python::converter::rvalue_from_python_stage1_data* data)
      {
        // Obtain a handle to the memory block that the Boost.Python
        // converter has allocated for the C++ type.
        namespace python = boost::python;
        typedef python::converter::rvalue_from_python_storage<Complex>
                                                                    storage_type;
        void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
    
        // In-place construct a Complex type via copy-constructor, passing
        // in a Complex created from Shiboken.
        std::cout << "complex_converter_from_python::construct()" << std::endl;
        data->convertible = new (storage) Complex(
          Shiboken::Converter<Complex>::toCpp(object));
      }
    };
    
    /// @brief Factory function used to exercise to-python conversion.
    Complex make_complex(double real, double imaginary)
    {
      return Complex(real, imaginary);
    }
    
    /// @brief Printing function used to exercise from-python converison.
    void print_complex(const Complex& c)
    {
      std::cout << "In print_complex: "
                << c.real() << ", "
                << c.imaginary() << std::endl;
    }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
    
      // Register Complex from python converter.
      complex_converter_from_python();
    
      // Register Complex to python converter.
      python::to_python_converter<
        Complex,
        complex_converter_to_python>();
    
      python::def("make_complex",  &make_complex);
      python::def("print_complex", &print_complex);
    }
    

    及其用法:

    >>> import example
    >>> x = example.make_complex(4, 2)
    complex_converter_to_python::convert()
    Shiboken::Converter<Complex>::toPython()
    >>> example.print_complex(x)
    complex_converter_from_python::convertible()
    Shiboken::Converter<Complex>::isConvertible()
    complex_converter_from_python::construct()
    Shiboken::Converter<Complex>::toCpp()
    In print_complex: 4, 2
    

    另一种方法,虽然不是最优雅的方法,但可以让 Boost.Python 公开的函数使用 boost::python::object 类型,并通过 Python 语句与对象交互。类似于:

    boost::python::object X::getY()
    { 
      return boost::python::exec("QPointF()", ...); 
    }
    

    以上代码将让 Python 实例化一个 QPointF Python 对象,该对象将委托给 Shiboken 的类型系统。由于X::getY() 返回一个泛型对象,当对象句柄从 C++ 转换到 Python 时,Boost.Python 不会尝试执行类型转换。

    【讨论】:

    • 但是没有一个全局的python转换器列表吗?例如,如果您尝试为 QString 注册两个转换器,则会收到运行时错误。我认为有一个更优雅的解决方案,只需从 shiboken 调用适当的注册函数,例如正如这家伙在这里尝试做的那样:mail-archive.com/pyside@lists.openbossa.org/msg00952.html
    • @peterkarasev:Boost.Python 类型转换器的底层注册表是一个单例。我扩展了在 Boost.Python 中显示/从转换器显示的答案,以及如何根据另一个 API 实现它们。在这种情况下,我使用了您提供的链接中提到的 API。
    • 是的,这是正确的想法,尽管该链接中的 API 似乎无效。 “根据对 Shiboken::Conversions 和 Shiboken::Module 的调用注册 boost::python 转换器”是我必须正常工作的。
    【解决方案2】:

    根据我发现的早期回复和其他信息,这里有一个通用的例程,允许将 PySide.QtGui.QColor 参数之类的东西传递到 boost::python 包装的 c++ 方法中,该方法需要 QColor&amp; 输入参数:

    template<class QtGuiClass,int SBK_BOGAN_IDX>
    struct QtGui_from_python {
    QtGui_from_python() {
      qDebug()<<" registering type: " << typeid(this).name() << " in " << __FUNCTION__;
      boost::python::converter::registry::push_back(
        &convertible,
        &construct,
        boost::python::type_id<QtGuiClass>());
    }
    static void* convertible(PyObject* obj_ptr) {
        if(!PyObject_TypeCheck(obj_ptr,
                (PyTypeObject*)XSbkPySide_QtGuiTypes[SBK_BOGAN_IDX]))
        {    qDebug()<<"Failed type check!?"; }
        return obj_ptr;
    }
    static void construct( PyObject* obj_ptr, 
       bp::converter::rvalue_from_python_stage1_data* data)
    {
        void* storage = (
        (boost::python::converter::rvalue_from_python_storage<QtGuiClass>*)
        data)->storage.bytes;
    
        SbkObject* result = reinterpret_cast<SbkObject*>(obj_ptr);
        auto _ptr = (QtGuiClass*) (Shiboken::Object::cppPointer(
                                           result,Py_TYPE(result)));
        new (storage) QtGuiClass(*_ptr);
        qDebug() << "__alloc'd " << typeid(*_ptr).name() <<" at "<<_ptr;
        data->convertible = storage;
    }
    };
    

    并且,从启动函数调用上述内容:

    QtGui_from_python<QColor,SBK_QCOLOR_IDX>();
    

    【讨论】:

      猜你喜欢
      • 2020-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 2020-12-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多