FreeCAD

Форк
0
/
PythonWrapper.cpp 
869 строк · 27.6 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2021 Werner Mayer <wmayer[at]users.sourceforge.net>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
# include <limits>
26
# include <unordered_map>
27
# include <list>
28
# include <QAction>
29
# include <QApplication>
30
# include <QDir>
31
# include <QIcon>
32
# include <QPrinter>
33
# include <QWidget>
34
#endif
35

36
#include <QMetaType>
37

38
// Uncomment this block to remove PySide C++ support and switch to its Python interface
39
//#undef HAVE_SHIBOKEN2
40
//#undef HAVE_PYSIDE2
41
//#undef HAVE_SHIBOKEN6
42
//#undef HAVE_PYSIDE6
43

44
#ifdef FC_OS_WIN32
45
#undef max
46
#undef min
47
#ifdef _MSC_VER
48
#pragma warning( disable : 4099 )
49
#pragma warning( disable : 4522 )
50
#endif
51
#endif
52

53
#if defined(__clang__)
54
# pragma clang diagnostic push
55
# pragma clang diagnostic ignored "-Wmismatched-tags"
56
# pragma clang diagnostic ignored "-Wunused-parameter"
57
# if __clang_major__ > 3
58
# pragma clang diagnostic ignored "-Wkeyword-macro"
59
# endif
60
#elif defined (__GNUC__)
61
# pragma GCC diagnostic push
62
# pragma GCC diagnostic ignored "-Wunused-parameter"
63
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
64
#endif
65

66
//-----------------------------------------------------------------------------
67
//
68
// shiboken2 and PySide2 specific defines and includes
69
//
70

71
// class and struct used for SbkObject
72
//
73
#ifdef HAVE_SHIBOKEN2
74
# define HAVE_SHIBOKEN
75
# ifdef HAVE_PYSIDE2
76
#  define HAVE_PYSIDE
77

78
// Since version 5.12 shiboken offers a method to get wrapper by class name (typeForTypeName)
79
// This helps to avoid to include the PySide2 headers since MSVC has a compiler bug when
80
// compiling together with std::bitset (https://bugreports.qt.io/browse/QTBUG-72073)
81

82
// Do not use SHIBOKEN_MICRO_VERSION; it might contain a dot
83
#  define SHIBOKEN_FULL_VERSION QT_VERSION_CHECK(SHIBOKEN_MAJOR_VERSION, SHIBOKEN_MINOR_VERSION, 0)
84
#  if (SHIBOKEN_FULL_VERSION >= QT_VERSION_CHECK(5, 12, 0))
85
#   define HAVE_SHIBOKEN_TYPE_FOR_TYPENAME
86
#  endif
87

88
#  ifndef HAVE_SHIBOKEN_TYPE_FOR_TYPENAME
89
#   include <pyside2_qtcore_python.h>
90
#   include <pyside2_qtgui_python.h>
91
#   include <pyside2_qtwidgets_python.h>
92
#  endif
93
# endif // HAVE_PYSIDE2
94
#endif // HAVE_SHIBOKEN2
95

96
//-----------------------------------------------------------------------------
97
//
98
// shiboken6 and PySide6 specific defines and includes
99
//
100

101
// class and struct used for SbkObject
102
//
103
#ifdef HAVE_SHIBOKEN6
104
# define HAVE_SHIBOKEN
105
# ifdef HAVE_PYSIDE6
106
#  define HAVE_PYSIDE
107
#  define HAVE_SHIBOKEN_TYPE_FOR_TYPENAME
108
# endif // HAVE_PYSIDE6
109
#endif // HAVE_SHIBOKEN6
110

111
//-----------------------------------------------------------------------------
112

113
#ifdef HAVE_SHIBOKEN
114
# undef _POSIX_C_SOURCE
115
# undef _XOPEN_SOURCE
116
# include <basewrapper.h>
117
# include <sbkconverter.h>
118
# include <sbkmodule.h>
119
# include <shiboken.h>
120
#endif // HAVE_SHIBOKEN
121

122
#ifdef HAVE_PYSIDE
123
# include <signalmanager.h>
124
#endif // HAVE_PYSIDE
125

126
//-----------------------------------------------------------------------------
127

128
#if defined(__clang__)
129
# pragma clang diagnostic pop
130
#elif defined (__GNUC__)
131
# pragma GCC diagnostic pop
132
#endif
133

134
// Must be imported after PySide headers
135
#ifndef _PreComp_
136
# include <QGraphicsItem>
137
# include <QGraphicsObject>
138
#endif
139

140
#include <App/Application.h>
141
#include <Base/Interpreter.h>
142
#include <Base/Quantity.h>
143
#include <Base/QuantityPy.h>
144

145
#include "PythonWrapper.h"
146
#include "UiLoader.h"
147
#include "MetaTypes.h"
148

149
// NOLINTBEGIN
150
#if defined(HAVE_SHIBOKEN2)
151
PyTypeObject** SbkPySide2_QtCoreTypes           = nullptr;
152
PyTypeObject** SbkPySide2_QtGuiTypes            = nullptr;
153
PyTypeObject** SbkPySide2_QtWidgetsTypes        = nullptr;
154
PyTypeObject** SbkPySide2_QtPrintSupportTypes   = nullptr;
155
PyTypeObject** SbkPySide2_QtUiToolsTypes        = nullptr;
156
constexpr auto &SbkPySide_QtCoreTypes           = SbkPySide2_QtCoreTypes;
157
constexpr auto &SbkPySide_QtGuiTypes            = SbkPySide2_QtGuiTypes;
158
constexpr auto &SbkPySide_QtWidgetsTypes        = SbkPySide2_QtWidgetsTypes;
159
constexpr auto &SbkPySide_QtPrintSupportTypes   = SbkPySide2_QtPrintSupportTypes;
160
constexpr auto &SbkPySide_QtUiToolsTypes        = SbkPySide2_QtUiToolsTypes;
161
#if !defined(HAVE_PYSIDE2)
162
constexpr const char* ModuleShiboken            = "shiboken2";
163
#endif
164
constexpr const char* ModulePySide              = "PySide2";
165
#elif defined(HAVE_SHIBOKEN6)
166
PyTypeObject** SbkPySide6_QtCoreTypes           = nullptr;
167
PyTypeObject** SbkPySide6_QtGuiTypes            = nullptr;
168
PyTypeObject** SbkPySide6_QtWidgetsTypes        = nullptr;
169
PyTypeObject** SbkPySide6_QtPrintSupportTypes   = nullptr;
170
PyTypeObject** SbkPySide6_QtUiToolsTypes        = nullptr;
171
constexpr auto &SbkPySide_QtCoreTypes           = SbkPySide6_QtCoreTypes;
172
constexpr auto &SbkPySide_QtGuiTypes            = SbkPySide6_QtGuiTypes;
173
constexpr auto &SbkPySide_QtWidgetsTypes        = SbkPySide6_QtWidgetsTypes;
174
constexpr auto &SbkPySide_QtPrintSupportTypes   = SbkPySide6_QtPrintSupportTypes;
175
constexpr auto &SbkPySide_QtUiToolsTypes        = SbkPySide6_QtUiToolsTypes;
176
#if !defined(HAVE_PYSIDE6)
177
constexpr const char* ModuleShiboken            = "shiboken6";
178
#endif
179
constexpr const char* ModulePySide              = "PySide6";
180
#else
181
static PyTypeObject** SbkPySide_DummyTypes;
182
constexpr auto &SbkPySide_QtCoreTypes           = SbkPySide_DummyTypes;
183
constexpr auto &SbkPySide_QtGuiTypes            = SbkPySide_DummyTypes;
184
constexpr auto &SbkPySide_QtWidgetsTypes        = SbkPySide_DummyTypes;
185
constexpr auto &SbkPySide_QtPrintSupportTypes   = SbkPySide_DummyTypes;
186
constexpr auto &SbkPySide_QtUiToolsTypes        = SbkPySide_DummyTypes;
187
# if QT_VERSION < QT_VERSION_CHECK(6,0,0)
188
constexpr const char* ModuleShiboken            = "shiboken2";
189
constexpr const char* ModulePySide              = "PySide2";
190
# else
191
constexpr const char* ModuleShiboken            = "shiboken6";
192
constexpr const char* ModulePySide              = "PySide6";
193
# endif
194
#endif
195
// NOLINTEND
196

197
using namespace Gui;
198

199
#if defined (HAVE_SHIBOKEN)
200

201
/**
202
  Example:
203
  \code
204
    ui = FreeCADGui.UiLoader()
205
    w = ui.createWidget("Gui::InputField")
206
    w.show()
207
    w.property("quantity")
208
  \endcode
209
  */
210

211
PyObject* toPythonFuncQuantityTyped(Base::Quantity cpx)
212
{
213
    return new Base::QuantityPy(new Base::Quantity(cpx));
214
}
215

216
PyObject* toPythonFuncQuantity(const void* cpp)
217
{
218
    return toPythonFuncQuantityTyped(*static_cast<const Base::Quantity*>(cpp));
219
}
220

221
void toCppPointerConvFuncQuantity(PyObject* pyobj,void* cpp)
222
{
223
   *static_cast<Base::Quantity*>(cpp) = *static_cast<Base::QuantityPy*>(pyobj)->getQuantityPtr();
224
}
225

226
PythonToCppFunc toCppPointerCheckFuncQuantity(PyObject* obj)
227
{
228
    if (PyObject_TypeCheck(obj, &(Base::QuantityPy::Type))) {
229
        return toCppPointerConvFuncQuantity;
230
    }
231
    return nullptr;
232
}
233

234
void BaseQuantity_PythonToCpp_QVariant(PyObject* pyIn, void* cppOut)
235
{
236
    Base::Quantity* q = static_cast<Base::QuantityPy*>(pyIn)->getQuantityPtr();
237
    *((QVariant*)cppOut) = QVariant::fromValue<Base::Quantity>(*q);
238
}
239

240
PythonToCppFunc isBaseQuantity_PythonToCpp_QVariantConvertible(PyObject* obj)
241
{
242
    if (PyObject_TypeCheck(obj, &(Base::QuantityPy::Type))) {
243
        return BaseQuantity_PythonToCpp_QVariant;
244
    }
245
    return nullptr;
246
}
247

248
#if defined (HAVE_PYSIDE)
249
Base::Quantity convertWrapperToQuantity(const PySide::PyObjectWrapper &w)
250
{
251
    auto pyIn = static_cast<PyObject*>(w);
252
    if (PyObject_TypeCheck(pyIn, &(Base::QuantityPy::Type))) {
253
        return *static_cast<Base::QuantityPy*>(pyIn)->getQuantityPtr();
254
    }
255

256
    return Base::Quantity(std::numeric_limits<double>::quiet_NaN());
257
}
258
#endif
259

260
void registerTypes()
261
{
262
    SbkConverter* convert = Shiboken::Conversions::createConverter(&Base::QuantityPy::Type,
263
                                                                   toPythonFuncQuantity);
264
    Shiboken::Conversions::setPythonToCppPointerFunctions(convert,
265
                                                          toCppPointerConvFuncQuantity,
266
                                                          toCppPointerCheckFuncQuantity);
267
    Shiboken::Conversions::registerConverterName(convert, "Base::Quantity");
268

269
    SbkConverter* qvariant_conv = Shiboken::Conversions::getConverter("QVariant");
270
    if (qvariant_conv) {
271
        // The type QVariant already has a converter from PyBaseObject_Type which will
272
        // come before our own converter.
273
        Shiboken::Conversions::addPythonToCppValueConversion(qvariant_conv,
274
                                                             BaseQuantity_PythonToCpp_QVariant,
275
                                                             isBaseQuantity_PythonToCpp_QVariantConvertible);
276
    }
277

278
#if defined (HAVE_PYSIDE)
279
    QMetaType::registerConverter<PySide::PyObjectWrapper, Base::Quantity>(&convertWrapperToQuantity);
280
#endif
281
}
282
#endif
283

284
// --------------------------------------------------------
285

286
namespace Gui {
287

288
static std::string getPySideModuleName(const std::string& moduleName)
289
{
290
    std::string name(ModulePySide);
291
    name += '.';
292
    name += moduleName;
293

294
    return name;
295
}
296

297
static bool loadPySideModule(const std::string& moduleName, PyTypeObject**& types)
298
{
299
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
300
    if (!types) {
301
        Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(getPySideModuleName(moduleName).c_str()));
302
        if (requiredModule.isNull()) {
303
            return false;
304
        }
305
        types = Shiboken::Module::getTypes(requiredModule);
306
    }
307
#else
308
    Q_UNUSED(moduleName)
309
    Q_UNUSED(types)
310
#endif
311
    return true;
312
}
313

314
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
315
template<typename qttype>
316
#if defined (HAVE_SHIBOKEN2)
317
SbkObjectType*
318
#else
319
PyTypeObject*
320
#endif
321
getPyTypeObjectForTypeName()
322
{
323
#if defined (HAVE_SHIBOKEN_TYPE_FOR_TYPENAME)
324
# if defined (HAVE_SHIBOKEN2)
325
    auto sbkType = Shiboken::ObjectType::typeForTypeName(typeid(qttype).name());
326
    if (sbkType) {
327
        return reinterpret_cast<SbkObjectType*>&(sbkType->type);
328
    }
329
# else
330
    return Shiboken::ObjectType::typeForTypeName(typeid(qttype).name());
331
# endif
332
#else
333
# if defined (HAVE_SHIBOKEN2)
334
    return reinterpret_cast<SbkObjectType*>(Shiboken::SbkType<qttype>());
335
# else
336
    return Shiboken::SbkType<qttype>();
337
# endif
338
#endif
339
}
340

341
template<typename qttype>
342
qttype* qt_getCppType(PyObject* pyobj)
343
{
344
    auto type = getPyTypeObjectForTypeName<qttype>();
345
    if (type) {
346
        if (Shiboken::Object::checkType(pyobj)) {
347
            auto skbobj = reinterpret_cast<SbkObject *>(pyobj);
348
            auto pytypeobj = reinterpret_cast<PyTypeObject *>(type);
349
            return static_cast<qttype*>(Shiboken::Object::cppPointer(skbobj, pytypeobj));
350
        }
351
    }
352
    return nullptr;
353
}
354

355
/*!
356
 * \brief The WrapperManager class
357
 * This is a helper class that records the Python wrappers of a QObject and invalidates
358
 * them when the QObject is about to be destroyed.
359
 * This is to make sure that if the Python wrapper doesn't own the QObject it won't be notified
360
 * if the QObject is destroyed.
361
 * \code
362
 * ui = Gui.UiLoader()
363
 * lineedit = ui.createWidget("QLineEdit")
364
 * lineedit.deleteLater()
365
 * # Make sure this won't crash
366
 * lineedit.show()
367
 * \endcode
368
 */
369
class WrapperManager : public QObject
370
{
371

372
public:
373
    static WrapperManager& instance()
374
    {
375
        static WrapperManager singleton;
376
        return singleton;
377
    }
378
    /*!
379
     * \brief addQObject
380
     * \param obj
381
     * \param pyobj
382
     * Connects destruction event of a QObject with invalidation of its PythonWrapper via a helper QObject.
383
     */
384
    void addQObject(QObject* obj, PyObject* pyobj)
385
    {
386
        const auto PyW_unique_name = QString::number(reinterpret_cast <quintptr> (pyobj));
387
        auto PyW_invalidator = findChild <QObject *> (PyW_unique_name, Qt::FindDirectChildrenOnly);
388

389
        if (PyW_invalidator == nullptr) {
390
            PyW_invalidator = new QObject(this);
391
            PyW_invalidator->setObjectName(PyW_unique_name);
392

393
            Py_INCREF (pyobj);
394
        }
395
        else {
396
            PyW_invalidator->disconnect();
397
        }
398

399
        auto destroyedFun = [pyobj](){
400
            Base::PyGILStateLocker lock;
401
            auto sbk_ptr = reinterpret_cast <SbkObject *> (pyobj);
402
            if (sbk_ptr != nullptr) {
403
                Shiboken::Object::setValidCpp(sbk_ptr, false);
404
            }
405
            else {
406
                Base::Console().DeveloperError("WrapperManager", "A QObject has just been destroyed after its Pythonic wrapper.\n");
407
            }
408
            Py_DECREF (pyobj);
409
        };
410

411
        QObject::connect(PyW_invalidator, &QObject::destroyed, this, destroyedFun);
412
        QObject::connect(obj, &QObject::destroyed, PyW_invalidator, &QObject::deleteLater);
413
}
414

415
private:
416
    void wrapQApplication()
417
    {
418
        // We have to explicitly hold a reference to the wrapper of the QApplication
419
        // as otherwise it can happen that when running the gc the program crashes
420
        // The code snippet below caused a crash on older versions:
421
        // mw = Gui.getMainWindow()
422
        // mw.style()
423
        // import gc
424
        // gc.collect()
425
        auto type = getPyTypeObjectForTypeName<QApplication>();
426
        if (type) {
427
            PyObject* pyobj = Shiboken::Object::newObject(type, qApp, false, false, "QApplication");
428
            addQObject(qApp, pyobj);
429
        }
430
    }
431

432
    WrapperManager()
433
    {
434
        wrapQApplication();
435
    }
436
    ~WrapperManager() override = default;
437
};
438

439
#else
440

441
static std::string formatModuleError(const std::string& name)
442
{
443
    std::string error = "Cannot load " + name + " module";
444
    return error;
445
}
446

447
static PyObject* importShiboken()
448
{
449
    PyObject* obj = PyImport_ImportModule(ModuleShiboken);
450
    if (obj) {
451
        return obj;
452
    }
453

454
    throw Py::Exception(PyExc_ImportError, formatModuleError(ModuleShiboken));
455
}
456

457
static PyObject* importPySide(const std::string& moduleName)
458
{
459
    std::string name = getPySideModuleName(moduleName);
460
    PyObject* obj = PyImport_ImportModule(name.c_str());
461
    if (obj) {
462
        return obj;
463
    }
464

465
    throw Py::Exception(PyExc_ImportError, formatModuleError(name));
466
}
467

468
template<typename qttype>
469
qttype* qt_getCppType(PyObject* pyobj)
470
{
471
    // https://github.com/PySide/Shiboken/blob/master/shibokenmodule/typesystem_shiboken.xml
472
    Py::Module mainmod(importShiboken(), true);
473
    Py::Callable func = mainmod.getDict().getItem("getCppPointer");
474
    if (func.isNull()) {
475
        throw Py::RuntimeError("Failed to get C++ pointer");
476
    }
477

478
    Py::Tuple arguments(1);
479
    arguments[0] = Py::Object(pyobj); // PySide pointer
480
    Py::Tuple result(func.apply(arguments));
481
    return reinterpret_cast<qttype*>(PyLong_AsVoidPtr(result[0].ptr()));
482
}
483

484
template<typename qttype>
485
Py::Object qt_wrapInstance(qttype object,
486
                           const std::string& className,
487
                           const std::string& moduleName)
488
{
489
    Py::Module mainmod(importShiboken(), true);
490
    Py::Callable func = mainmod.getDict().getItem("wrapInstance");
491
    if (func.isNull()) {
492
        // Failure will be handled in the calling instance
493
        return func;
494
    }
495

496
    Py::Module qtmod(importPySide(moduleName));
497
    Py::Object item = qtmod.getDict().getItem(className);
498
    if (item.isNull()) {
499
        // Failure will be handled in the calling instance
500
        return item;
501
    }
502

503
    Py::Tuple arguments(2);
504
    arguments[0] = Py::asObject(PyLong_FromVoidPtr((void*)object));
505
    arguments[1] = item;
506

507
    return func.apply(arguments);
508
}
509

510
const char* qt_identifyType(QObject* ptr, const std::string& moduleName)
511
{
512
    Py::Module qtmod(importPySide(moduleName));
513
    const QMetaObject* metaObject = ptr->metaObject();
514
    while (metaObject) {
515
        const char* className = metaObject->className();
516
        if (qtmod.getDict().hasKey(className)) {
517
            return className;
518
        }
519
        metaObject = metaObject->superClass();
520
    }
521

522
    return nullptr;
523
}
524

525
#endif
526

527
}
528

529
// --------------------------------------------------------
530

531
PythonWrapper::PythonWrapper()
532
{
533
#if defined (HAVE_SHIBOKEN)
534
    static bool init;
535
    if (!init) {
536
        init = true;
537
        registerTypes();
538
    }
539
#endif
540
}
541

542
bool PythonWrapper::toCString(const Py::Object& pyobject, std::string& str)
543
{
544
    if (PyUnicode_Check(pyobject.ptr())) {
545
        PyObject* unicode = PyUnicode_AsUTF8String(pyobject.ptr());
546
        str = PyBytes_AsString(unicode);
547
        Py_DECREF(unicode);
548
        return true;
549
    }
550
    else if (PyBytes_Check(pyobject.ptr())) {
551
        str = PyBytes_AsString(pyobject.ptr());
552
        return true;
553
    }
554
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
555
    if (Shiboken::String::check(pyobject.ptr())) {
556
        const char* s = Shiboken::String::toCString(pyobject.ptr());
557
        if (s) str = s;
558
        return true;
559
    }
560
#endif
561
    return false;
562
}
563

564
QObject* PythonWrapper::toQObject(const Py::Object& pyobject)
565
{
566
    return qt_getCppType<QObject>(pyobject.ptr());
567
}
568

569
QGraphicsItem* PythonWrapper::toQGraphicsItem(PyObject* pyPtr)
570
{
571
    return qt_getCppType<QGraphicsItem>(pyPtr);
572
}
573

574
QGraphicsItem* PythonWrapper::toQGraphicsItem(const Py::Object& pyobject)
575
{
576
    return toQGraphicsItem(pyobject.ptr());
577
}
578

579
QGraphicsObject* PythonWrapper::toQGraphicsObject(PyObject* pyPtr)
580
{
581
    return qt_getCppType<QGraphicsObject>(pyPtr);
582
}
583

584
QGraphicsObject* PythonWrapper::toQGraphicsObject(const Py::Object& pyobject)
585
{
586
    return toQGraphicsObject(pyobject.ptr());
587
}
588

589
Py::Object PythonWrapper::fromQImage(const QImage& img)
590
{
591
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
592
    auto type = getPyTypeObjectForTypeName<QImage>();
593
    if (type) {
594
        PyObject* pyobj = Shiboken::Conversions::copyToPython(type, const_cast<QImage*>(&img));
595
        return Py::asObject(pyobj);
596
    }
597
#else
598
    // Access shiboken/PySide via Python
599
    Py::Object obj = qt_wrapInstance<const QImage*>(&img, "QImage", "QtGui");
600
    if (!obj.isNull()) {
601
        return obj;
602
    }
603
#endif
604
    throw Py::RuntimeError("Failed to wrap image");
605
}
606

607
QImage *PythonWrapper::toQImage(PyObject *pyobj)
608
{
609
    return qt_getCppType<QImage>(pyobj);
610
}
611

612
Py::Object PythonWrapper::fromQIcon(const QIcon* icon)
613
{
614
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
615
    auto type = getPyTypeObjectForTypeName<QIcon>();
616
    if (type) {
617
        const char* typeName = typeid(*const_cast<QIcon*>(icon)).name();
618
        PyObject* pyobj = Shiboken::Object::newObject(type, const_cast<QIcon*>(icon), true, false, typeName);
619
        return Py::asObject(pyobj);
620
    }
621
#else
622
    // Access shiboken/PySide via Python
623
    Py::Object obj = qt_wrapInstance<const QIcon*>(icon, "QIcon", "QtGui");
624
    if (!obj.isNull()) {
625
        return obj;
626
    }
627
#endif
628
    throw Py::RuntimeError("Failed to wrap icon");
629
}
630

631
QIcon *PythonWrapper::toQIcon(PyObject *pyobj)
632
{
633
    return qt_getCppType<QIcon>(pyobj);
634
}
635

636
Py::Object PythonWrapper::fromQDir(const QDir& dir)
637
{
638
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
639
    auto type = getPyTypeObjectForTypeName<QDir>();
640
    if (type) {
641
        const char* typeName = typeid(dir).name();
642
        PyObject* pyobj = Shiboken::Object::newObject(type, const_cast<QDir*>(&dir), false, false, typeName);
643
        return Py::asObject(pyobj);
644
    }
645
#else
646
    // Access shiboken/PySide via Python
647
    Py::Object obj = qt_wrapInstance<const QDir*>(&dir, "QDir", "QtGui");
648
    if (!obj.isNull()) {
649
        return obj;
650
    }
651
#endif
652
    throw Py::RuntimeError("Failed to wrap directory");
653
}
654

655
QDir* PythonWrapper::toQDir(PyObject* pyobj)
656
{
657
    return qt_getCppType<QDir>(pyobj);
658
}
659

660
Py::Object PythonWrapper::fromQAction(QAction* action)
661
{
662
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
663
    // Access shiboken/PySide via C++
664
    auto type = getPyTypeObjectForTypeName<QAction>();
665
    if (type) {
666
        PyObject* pyobj = Shiboken::Object::newObject(type, action, false, false, "QAction");
667
        WrapperManager::instance().addQObject(action, pyobj);
668
        return Py::asObject(pyobj);
669
    }
670
#else
671
    // Access shiboken/PySide via Python
672
# if QT_VERSION < QT_VERSION_CHECK(6,0,0)
673
    constexpr const char* qtModWithQAction = "QtWidgets";
674
# else
675
    constexpr const char* qtModWithQAction = "QtGui";
676
# endif
677
    Py::Object obj = qt_wrapInstance<QAction*>(action, "QAction", qtModWithQAction);
678
    if (!obj.isNull()) {
679
        return obj;
680
    }
681
#endif
682
    throw Py::RuntimeError("Failed to wrap action");
683
}
684

685
Py::Object PythonWrapper::fromQPrinter(QPrinter* printer)
686
{
687
    if (!printer) {
688
        return Py::None();
689
    }
690
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
691
    // Access shiboken/PySide via C++
692
    auto type = getPyTypeObjectForTypeName<QPrinter>();
693
    if (!type) {
694
        // XXX: Why is QPrinter special?
695
#if defined (HAVE_SHIBOKEN2)
696
        type = reinterpret_cast<SbkObjectType*>(Shiboken::Conversions::getPythonTypeObject("QPrinter"));
697
#else
698
        type = Shiboken::Conversions::getPythonTypeObject("QPrinter");
699
#endif
700
    }
701
    if (type) {
702
        PyObject* pyobj = Shiboken::Object::newObject(type, printer, false, false, "QPrinter");
703
        return Py::asObject(pyobj);
704
    }
705
#else
706
    // Access shiboken/PySide via Python
707
    Py::Object obj = qt_wrapInstance<QPrinter*>(printer, "QPrinter", "QtCore");
708
    if (!obj.isNull()) {
709
        return obj;
710
    }
711
#endif
712
    throw Py::RuntimeError("Failed to wrap printer");
713
}
714

715
Py::Object PythonWrapper::fromQObject(QObject* object, const char* className)
716
{
717
    if (!object) {
718
        return Py::None();
719
    }
720
    const char* typeName;
721
    if (className) {
722
        typeName = className;
723
    }
724
    else {
725
        typeName = object->metaObject()->className();
726
    }
727
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
728
    // Access shiboken/PySide via C++
729
    auto type = getPyTypeObjectForTypeName<QObject>();
730
    if (type) {
731
        PyObject* pyobj = Shiboken::Object::newObject(type, object, false, false, typeName);
732
        WrapperManager::instance().addQObject(object, pyobj);
733
        return Py::asObject(pyobj);
734
    }
735
#else
736
    // Access shiboken/PySide via Python
737
    Py::Object obj = qt_wrapInstance<QObject*>(object, typeName, "QtCore");
738
    if (!obj.isNull()) {
739
        return obj;
740
    }
741
#endif
742
    throw Py::RuntimeError("Failed to wrap object");
743
}
744

745
Py::Object PythonWrapper::fromQWidget(QWidget* widget, const char* className)
746
{
747
    const char* typeName;
748
    if (className) {
749
        typeName = className;
750
    }
751
    else {
752
        typeName = widget->metaObject()->className();
753
    }
754
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
755
    // Access shiboken/PySide via C++
756
    auto type = getPyTypeObjectForTypeName<QWidget>();
757
    if (type) {
758
        PyObject* pyobj = Shiboken::Object::newObject(type, widget, false, false, typeName);
759
        WrapperManager::instance().addQObject(widget, pyobj);
760
        return Py::asObject(pyobj);
761
    }
762
#else
763
    // Access shiboken/PySide via Python
764
    Py::Object obj = qt_wrapInstance<QWidget*>(widget, typeName, "QtWidgets");
765
    if (!obj.isNull()) {
766
        return obj;
767
    }
768
#endif
769
    throw Py::RuntimeError("Failed to wrap widget");
770
}
771

772
const char* PythonWrapper::getWrapperName(QObject* obj) const
773
{
774
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
775
    const QMetaObject* meta = obj->metaObject();
776
    while (meta) {
777
        const char* typeName = meta->className();
778
        PyTypeObject* exactType = Shiboken::Conversions::getPythonTypeObject(typeName);
779
        if (exactType) {
780
            return typeName;
781
        }
782
        meta = meta->superClass();
783
    }
784
#else
785
    QUiLoader ui;
786
    QStringList names = ui.availableWidgets();
787
    const QMetaObject* meta = obj->metaObject();
788
    while (meta) {
789
        const char* typeName = meta->className();
790
        if (names.indexOf(QLatin1String(typeName)) >= 0) {
791
            return typeName;
792
        }
793
        meta = meta->superClass();
794
    }
795
#endif
796
    return "QObject";
797
}
798

799
bool PythonWrapper::loadCoreModule()
800
{
801
    return loadPySideModule("QtCore", SbkPySide_QtCoreTypes);
802
}
803

804
bool PythonWrapper::loadGuiModule()
805
{
806
    return loadPySideModule("QtGui", SbkPySide_QtGuiTypes);
807
}
808

809
bool PythonWrapper::loadWidgetsModule()
810
{
811
    return loadPySideModule("QtWidgets", SbkPySide_QtWidgetsTypes);
812
}
813

814
bool PythonWrapper::loadPrintSupportModule()
815
{
816
    return loadPySideModule("QtPrintSupport", SbkPySide_QtPrintSupportTypes);
817
}
818

819
bool PythonWrapper::loadUiToolsModule()
820
{
821
    return loadPySideModule("QtUiTools", SbkPySide_QtUiToolsTypes);
822
}
823

824
void PythonWrapper::createChildrenNameAttributes(PyObject* root, QObject* object)
825
{
826
    Q_FOREACH (QObject* child, object->children()) {
827
        const QByteArray name = child->objectName().toLocal8Bit();
828

829
        if (!name.isEmpty() && !name.startsWith("_") && !name.startsWith("qt_")) {
830
            bool hasAttr = PyObject_HasAttrString(root, name.constData());
831
            if (!hasAttr) {
832
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
833
                Shiboken::AutoDecRef pyChild(Shiboken::Conversions::pointerToPython(getPyTypeObjectForTypeName<QObject>(), child));
834
                PyObject_SetAttrString(root, name.constData(), pyChild);
835
#else
836
                const char* className = qt_identifyType(child, "QtWidgets");
837
                if (!className) {
838
                    if (qobject_cast<QWidget*>(child)) {
839
                        className = "QWidget";
840
                    }
841
                    else {
842
                        className = "QObject";
843
                    }
844
                }
845

846
                Py::Object pyChild(qt_wrapInstance<QObject*>(child, className, "QtWidgets"));
847
                if (!pyChild.isNull()) {
848
                    PyObject_SetAttrString(root, name.constData(), pyChild.ptr());
849
                }
850
#endif
851
            }
852
            createChildrenNameAttributes(root, child);
853
        }
854
        createChildrenNameAttributes(root, child);
855
    }
856
}
857

858
void PythonWrapper::setParent(PyObject* pyWdg, QObject* parent)
859
{
860
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
861
    if (parent) {
862
        Shiboken::AutoDecRef pyParent(Shiboken::Conversions::pointerToPython(getPyTypeObjectForTypeName<QWidget>(), parent));
863
        Shiboken::Object::setParent(pyParent, pyWdg);
864
    }
865
#else
866
    Q_UNUSED(pyWdg);
867
    Q_UNUSED(parent);
868
#endif
869
}
870

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.