FreeCAD

Форк
0
/
PythonWrapper.cpp 
942 строки · 29.8 Кб
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
// Include shiboken first to get the version
83
#  include <shiboken.h>
84

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

91
#  ifndef HAVE_SHIBOKEN_TYPE_FOR_TYPENAME
92
#   include <pyside2_qtcore_python.h>
93
#   include <pyside2_qtgui_python.h>
94
#   include <pyside2_qtwidgets_python.h>
95
#  endif
96
# endif // HAVE_PYSIDE2
97
#endif // HAVE_SHIBOKEN2
98

99
//-----------------------------------------------------------------------------
100
//
101
// shiboken6 and PySide6 specific defines and includes
102
//
103

104
// class and struct used for SbkObject
105
//
106
#ifdef HAVE_SHIBOKEN6
107
# define HAVE_SHIBOKEN
108
# ifdef HAVE_PYSIDE6
109
#  define HAVE_PYSIDE
110
#  define HAVE_SHIBOKEN_TYPE_FOR_TYPENAME
111
# endif // HAVE_PYSIDE6
112
# include <sbkversion.h>
113
# define SHIBOKEN_FULL_VERSION QT_VERSION_CHECK(SHIBOKEN_MAJOR_VERSION, SHIBOKEN_MINOR_VERSION, 0)
114
# if (SHIBOKEN_FULL_VERSION >= QT_VERSION_CHECK(6, 7, 0))
115
#  define HAVE_SHIBOKEN_TYPEINITSTRUCT
116
# endif
117
#endif // HAVE_SHIBOKEN6
118

119
//-----------------------------------------------------------------------------
120

121
#ifdef HAVE_SHIBOKEN
122
# undef _POSIX_C_SOURCE
123
# undef _XOPEN_SOURCE
124
# include <basewrapper.h>
125
# include <sbkconverter.h>
126
# include <sbkmodule.h>
127
# include <shiboken.h>
128
#endif // HAVE_SHIBOKEN
129

130
#ifdef HAVE_PYSIDE
131
# include <signalmanager.h>
132
#endif // HAVE_PYSIDE
133

134
//-----------------------------------------------------------------------------
135

136
#if defined(__clang__)
137
# pragma clang diagnostic pop
138
#elif defined (__GNUC__)
139
# pragma GCC diagnostic pop
140
#endif
141

142
// Must be imported after PySide headers
143
#ifndef _PreComp_
144
# include <QGraphicsItem>
145
# include <QGraphicsObject>
146
#endif
147

148
#include <App/Application.h>
149
#include <Base/Interpreter.h>
150
#include <Base/Quantity.h>
151
#include <Base/QuantityPy.h>
152

153
#include "PythonWrapper.h"
154
#include "UiLoader.h"
155
#include "MetaTypes.h"
156

157
// NOLINTBEGIN
158
#if defined(HAVE_SHIBOKEN2)
159
PyTypeObject** SbkPySide2_QtCoreTypes           = nullptr;
160
PyTypeObject** SbkPySide2_QtGuiTypes            = nullptr;
161
PyTypeObject** SbkPySide2_QtWidgetsTypes        = nullptr;
162
PyTypeObject** SbkPySide2_QtPrintSupportTypes   = nullptr;
163
PyTypeObject** SbkPySide2_QtUiToolsTypes        = nullptr;
164
constexpr auto &SbkPySide_QtCoreTypes           = SbkPySide2_QtCoreTypes;
165
constexpr auto &SbkPySide_QtGuiTypes            = SbkPySide2_QtGuiTypes;
166
constexpr auto &SbkPySide_QtWidgetsTypes        = SbkPySide2_QtWidgetsTypes;
167
constexpr auto &SbkPySide_QtPrintSupportTypes   = SbkPySide2_QtPrintSupportTypes;
168
constexpr auto &SbkPySide_QtUiToolsTypes        = SbkPySide2_QtUiToolsTypes;
169
#if !defined(HAVE_PYSIDE2)
170
constexpr const char* ModuleShiboken            = "shiboken2";
171
#endif
172
constexpr const char* ModulePySide              = "PySide2";
173
#elif defined(HAVE_SHIBOKEN6)
174
#ifdef HAVE_SHIBOKEN_TYPEINITSTRUCT
175
Shiboken::Module::TypeInitStruct* SbkPySide6_QtCoreTypes           = nullptr;
176
Shiboken::Module::TypeInitStruct* SbkPySide6_QtGuiTypes            = nullptr;
177
Shiboken::Module::TypeInitStruct* SbkPySide6_QtWidgetsTypes        = nullptr;
178
Shiboken::Module::TypeInitStruct* SbkPySide6_QtPrintSupportTypes   = nullptr;
179
Shiboken::Module::TypeInitStruct* SbkPySide6_QtUiToolsTypes        = nullptr;
180
#else
181
PyTypeObject** SbkPySide6_QtCoreTypes           = nullptr;
182
PyTypeObject** SbkPySide6_QtGuiTypes            = nullptr;
183
PyTypeObject** SbkPySide6_QtWidgetsTypes        = nullptr;
184
PyTypeObject** SbkPySide6_QtPrintSupportTypes   = nullptr;
185
PyTypeObject** SbkPySide6_QtUiToolsTypes        = nullptr;
186
#endif
187
constexpr auto &SbkPySide_QtCoreTypes           = SbkPySide6_QtCoreTypes;
188
constexpr auto &SbkPySide_QtGuiTypes            = SbkPySide6_QtGuiTypes;
189
constexpr auto &SbkPySide_QtWidgetsTypes        = SbkPySide6_QtWidgetsTypes;
190
constexpr auto &SbkPySide_QtPrintSupportTypes   = SbkPySide6_QtPrintSupportTypes;
191
constexpr auto &SbkPySide_QtUiToolsTypes        = SbkPySide6_QtUiToolsTypes;
192
#if !defined(HAVE_PYSIDE6)
193
constexpr const char* ModuleShiboken            = "shiboken6";
194
#endif
195
constexpr const char* ModulePySide              = "PySide6";
196
#else
197
static PyTypeObject** SbkPySide_DummyTypes;
198
constexpr auto &SbkPySide_QtCoreTypes           = SbkPySide_DummyTypes;
199
constexpr auto &SbkPySide_QtGuiTypes            = SbkPySide_DummyTypes;
200
constexpr auto &SbkPySide_QtWidgetsTypes        = SbkPySide_DummyTypes;
201
constexpr auto &SbkPySide_QtPrintSupportTypes   = SbkPySide_DummyTypes;
202
constexpr auto &SbkPySide_QtUiToolsTypes        = SbkPySide_DummyTypes;
203
# if QT_VERSION < QT_VERSION_CHECK(6,0,0)
204
constexpr const char* ModuleShiboken            = "shiboken2";
205
constexpr const char* ModulePySide              = "PySide2";
206
# else
207
constexpr const char* ModuleShiboken            = "shiboken6";
208
constexpr const char* ModulePySide              = "PySide6";
209
# endif
210
#endif
211
// NOLINTEND
212

213
using namespace Gui;
214

215
#if defined (HAVE_SHIBOKEN)
216

217
/**
218
  Example:
219
  \code
220
    ui = FreeCADGui.UiLoader()
221
    w = ui.createWidget("Gui::InputField")
222
    w.show()
223
    w.property("quantity")
224
  \endcode
225
  */
226

227
PyObject* toPythonFuncQuantityTyped(Base::Quantity cpx)
228
{
229
    return new Base::QuantityPy(new Base::Quantity(cpx));
230
}
231

232
PyObject* toPythonFuncQuantity(const void* cpp)
233
{
234
    return toPythonFuncQuantityTyped(*static_cast<const Base::Quantity*>(cpp));
235
}
236

237
void toCppPointerConvFuncQuantity(PyObject* pyobj,void* cpp)
238
{
239
   *static_cast<Base::Quantity*>(cpp) = *static_cast<Base::QuantityPy*>(pyobj)->getQuantityPtr();
240
}
241

242
PythonToCppFunc toCppPointerCheckFuncQuantity(PyObject* obj)
243
{
244
    if (PyObject_TypeCheck(obj, &(Base::QuantityPy::Type))) {
245
        return toCppPointerConvFuncQuantity;
246
    }
247
    return nullptr;
248
}
249

250
void BaseQuantity_PythonToCpp_QVariant(PyObject* pyIn, void* cppOut)
251
{
252
    Base::Quantity* q = static_cast<Base::QuantityPy*>(pyIn)->getQuantityPtr();
253
    *((QVariant*)cppOut) = QVariant::fromValue<Base::Quantity>(*q);
254
}
255

256
PythonToCppFunc isBaseQuantity_PythonToCpp_QVariantConvertible(PyObject* obj)
257
{
258
    if (PyObject_TypeCheck(obj, &(Base::QuantityPy::Type))) {
259
        return BaseQuantity_PythonToCpp_QVariant;
260
    }
261
    return nullptr;
262
}
263

264
#if defined (HAVE_PYSIDE)
265
Base::Quantity convertWrapperToQuantity(const PySide::PyObjectWrapper &w)
266
{
267
    auto pyIn = static_cast<PyObject*>(w);
268
    if (PyObject_TypeCheck(pyIn, &(Base::QuantityPy::Type))) {
269
        return *static_cast<Base::QuantityPy*>(pyIn)->getQuantityPtr();
270
    }
271

272
    return Base::Quantity(std::numeric_limits<double>::quiet_NaN());
273
}
274
#endif
275

276
void registerTypes()
277
{
278
    SbkConverter* convert = Shiboken::Conversions::createConverter(&Base::QuantityPy::Type,
279
                                                                   toPythonFuncQuantity);
280
    Shiboken::Conversions::setPythonToCppPointerFunctions(convert,
281
                                                          toCppPointerConvFuncQuantity,
282
                                                          toCppPointerCheckFuncQuantity);
283
    Shiboken::Conversions::registerConverterName(convert, "Base::Quantity");
284

285
    SbkConverter* qvariant_conv = Shiboken::Conversions::getConverter("QVariant");
286
    if (qvariant_conv) {
287
        // The type QVariant already has a converter from PyBaseObject_Type which will
288
        // come before our own converter.
289
        Shiboken::Conversions::addPythonToCppValueConversion(qvariant_conv,
290
                                                             BaseQuantity_PythonToCpp_QVariant,
291
                                                             isBaseQuantity_PythonToCpp_QVariantConvertible);
292
    }
293

294
#if defined (HAVE_PYSIDE)
295
    QMetaType::registerConverter<PySide::PyObjectWrapper, Base::Quantity>(&convertWrapperToQuantity);
296
#endif
297
}
298
#endif
299

300
// --------------------------------------------------------
301

302
namespace Gui {
303

304
static std::string getPySideModuleName(const std::string& moduleName)
305
{
306
    std::string name(ModulePySide);
307
    name += '.';
308
    name += moduleName;
309

310
    return name;
311
}
312

313
#ifdef HAVE_SHIBOKEN_TYPEINITSTRUCT
314
static bool loadPySideModule(const std::string& moduleName, Shiboken::Module::TypeInitStruct*& types)
315
#else
316
static bool loadPySideModule(const std::string& moduleName, PyTypeObject**& types)
317
#endif
318
{
319
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
320
    if (!types) {
321
        Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(getPySideModuleName(moduleName).c_str()));
322
        if (requiredModule.isNull()) {
323
            return false;
324
        }
325
        types = Shiboken::Module::getTypes(requiredModule);
326
    }
327
#else
328
    Q_UNUSED(moduleName)
329
    Q_UNUSED(types)
330
#endif
331
    return true;
332
}
333

334
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
335
template<typename qttype>
336
#if defined (HAVE_SHIBOKEN2)
337
SbkObjectType*
338
#else
339
PyTypeObject*
340
#endif
341
getPyTypeObjectForTypeName()
342
{
343
#if defined (HAVE_SHIBOKEN_TYPE_FOR_TYPENAME)
344
# if defined (HAVE_SHIBOKEN2)
345
    auto sbkType = Shiboken::ObjectType::typeForTypeName(typeid(qttype).name());
346
    return reinterpret_cast<SbkObjectType*>(&sbkType->type);
347
# else
348
    return Shiboken::ObjectType::typeForTypeName(typeid(qttype).name());
349
# endif
350
#else
351
# if defined (HAVE_SHIBOKEN2)
352
    return reinterpret_cast<SbkObjectType*>(Shiboken::SbkType<qttype>());
353
# else
354
    return Shiboken::SbkType<qttype>();
355
# endif
356
#endif
357
}
358

359
template<typename qttype>
360
qttype* qt_getCppType(PyObject* pyobj)
361
{
362
    auto type = getPyTypeObjectForTypeName<qttype>();
363
    if (type) {
364
        if (Shiboken::Object::checkType(pyobj)) {
365
            auto skbobj = reinterpret_cast<SbkObject *>(pyobj);
366
            auto pytypeobj = reinterpret_cast<PyTypeObject *>(type);
367
            return static_cast<qttype*>(Shiboken::Object::cppPointer(skbobj, pytypeobj));
368
        }
369
    }
370
    return nullptr;
371
}
372

373
/*!
374
 * \brief The WrapperManager class
375
 * This is a helper class that records the Python wrappers of a QObject and invalidates
376
 * them when the QObject is about to be destroyed.
377
 * This is to make sure that if the Python wrapper doesn't own the QObject it won't be notified
378
 * if the QObject is destroyed.
379
 * \code
380
 * ui = Gui.UiLoader()
381
 * lineedit = ui.createWidget("QLineEdit")
382
 * lineedit.deleteLater()
383
 * # Make sure this won't crash
384
 * lineedit.show()
385
 * \endcode
386
 */
387
class WrapperManager : public QObject
388
{
389

390
public:
391
    static WrapperManager& instance()
392
    {
393
        static WrapperManager singleton;
394
        return singleton;
395
    }
396
    /*!
397
     * \brief addQObject
398
     * \param obj
399
     * \param pyobj
400
     * Connects destruction event of a QObject with invalidation of its PythonWrapper via a helper QObject.
401
     */
402
    void addQObject(QObject* obj, PyObject* pyobj)
403
    {
404
        const auto PyW_unique_name = QString::number(reinterpret_cast <quintptr> (pyobj));
405
        auto PyW_invalidator = findChild <QObject *> (PyW_unique_name, Qt::FindDirectChildrenOnly);
406

407
        if (PyW_invalidator == nullptr) {
408
            PyW_invalidator = new QObject(this);
409
            PyW_invalidator->setObjectName(PyW_unique_name);
410

411
            Py_INCREF (pyobj);
412
        }
413
        else {
414
            PyW_invalidator->disconnect();
415
        }
416

417
        auto destroyedFun = [pyobj](){
418
            Base::PyGILStateLocker lock;
419
            auto sbk_ptr = reinterpret_cast <SbkObject *> (pyobj);
420
            if (sbk_ptr != nullptr) {
421
                Shiboken::Object::setValidCpp(sbk_ptr, false);
422
            }
423
            else {
424
                Base::Console().DeveloperError("WrapperManager", "A QObject has just been destroyed after its Pythonic wrapper.\n");
425
            }
426
            Py_DECREF (pyobj);
427
        };
428

429
        QObject::connect(PyW_invalidator, &QObject::destroyed, this, destroyedFun);
430
        QObject::connect(obj, &QObject::destroyed, PyW_invalidator, &QObject::deleteLater);
431
}
432

433
private:
434
    void wrapQApplication()
435
    {
436
        // We have to explicitly hold a reference to the wrapper of the QApplication
437
        // as otherwise it can happen that when running the gc the program crashes
438
        // The code snippet below caused a crash on older versions:
439
        // mw = Gui.getMainWindow()
440
        // mw.style()
441
        // import gc
442
        // gc.collect()
443
        auto type = getPyTypeObjectForTypeName<QApplication>();
444
        if (type) {
445
            PyObject* pyobj = Shiboken::Object::newObject(type, qApp, false, false, "QApplication");
446
            addQObject(qApp, pyobj);
447
        }
448
    }
449

450
    WrapperManager()
451
    {
452
        wrapQApplication();
453
    }
454
    ~WrapperManager() override = default;
455
};
456

457
#else
458

459
static std::string formatModuleError(const std::string& name)
460
{
461
    std::string error = "Cannot load " + name + " module";
462
    return error;
463
}
464

465
static PyObject* importShiboken()
466
{
467
    PyObject* obj = PyImport_ImportModule(ModuleShiboken);
468
    if (obj) {
469
        return obj;
470
    }
471

472
    throw Py::Exception(PyExc_ImportError, formatModuleError(ModuleShiboken));
473
}
474

475
static PyObject* importPySide(const std::string& moduleName)
476
{
477
    std::string name = getPySideModuleName(moduleName);
478
    PyObject* obj = PyImport_ImportModule(name.c_str());
479
    if (obj) {
480
        return obj;
481
    }
482

483
    throw Py::Exception(PyExc_ImportError, formatModuleError(name));
484
}
485

486
template<typename qttype>
487
qttype* qt_getCppType(PyObject* pyobj)
488
{
489
    // https://github.com/PySide/Shiboken/blob/master/shibokenmodule/typesystem_shiboken.xml
490
    Py::Module mainmod(importShiboken(), true);
491
    Py::Callable func = mainmod.getDict().getItem("getCppPointer");
492
    if (func.isNull()) {
493
        throw Py::RuntimeError("Failed to get C++ pointer");
494
    }
495

496
    Py::Tuple arguments(1);
497
    arguments[0] = Py::Object(pyobj); // PySide pointer
498
    Py::Tuple result(func.apply(arguments));
499
    return reinterpret_cast<qttype*>(PyLong_AsVoidPtr(result[0].ptr()));
500
}
501

502
template<typename qttype>
503
Py::Object qt_wrapInstance(qttype object,
504
                           const std::string& className,
505
                           const std::string& moduleName)
506
{
507
    Py::Module mainmod(importShiboken(), true);
508
    Py::Callable func = mainmod.getDict().getItem("wrapInstance");
509
    if (func.isNull()) {
510
        // Failure will be handled in the calling instance
511
        return func;
512
    }
513

514
    Py::Module qtmod(importPySide(moduleName));
515
    Py::Object item = qtmod.getDict().getItem(className);
516
    if (item.isNull()) {
517
        // Failure will be handled in the calling instance
518
        return item;
519
    }
520

521
    Py::Tuple arguments(2);
522
    arguments[0] = Py::asObject(PyLong_FromVoidPtr((void*)object));
523
    arguments[1] = item;
524

525
    return func.apply(arguments);
526
}
527

528
const char* qt_identifyType(QObject* ptr, const std::string& moduleName)
529
{
530
    Py::Module qtmod(importPySide(moduleName));
531
    const QMetaObject* metaObject = ptr->metaObject();
532
    while (metaObject) {
533
        const char* className = metaObject->className();
534
        if (qtmod.getDict().hasKey(className)) {
535
            return className;
536
        }
537
        metaObject = metaObject->superClass();
538
    }
539

540
    return nullptr;
541
}
542

543
#endif
544

545
}
546

547
// --------------------------------------------------------
548

549
PythonWrapper::PythonWrapper()
550
{
551
#if defined (HAVE_SHIBOKEN)
552
    static bool init;
553
    if (!init) {
554
        init = true;
555
        registerTypes();
556
    }
557
#endif
558
}
559

560
bool PythonWrapper::toCString(const Py::Object& pyobject, std::string& str)
561
{
562
    if (PyUnicode_Check(pyobject.ptr())) {
563
        PyObject* unicode = PyUnicode_AsUTF8String(pyobject.ptr());
564
        str = PyBytes_AsString(unicode);
565
        Py_DECREF(unicode);
566
        return true;
567
    }
568
    else if (PyBytes_Check(pyobject.ptr())) {
569
        str = PyBytes_AsString(pyobject.ptr());
570
        return true;
571
    }
572
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
573
    if (Shiboken::String::check(pyobject.ptr())) {
574
        const char* s = Shiboken::String::toCString(pyobject.ptr());
575
        if (s) str = s;
576
        return true;
577
    }
578
#endif
579
    return false;
580
}
581

582
QObject* PythonWrapper::toQObject(const Py::Object& pyobject)
583
{
584
    return qt_getCppType<QObject>(pyobject.ptr());
585
}
586

587
qsizetype PythonWrapper::tryEnum(PyObject* pyPtr)
588
{
589
    if (PyObject* number = PyNumber_Long(pyPtr)) {
590
        Py::Long longObj(number, true);
591
        return longObj.as_long();
592
    }
593

594
    // if PyNumber_Long failed then an exception is set
595
    PyErr_Clear();
596

597
    Py::Object object(pyPtr);
598
    if (object.hasAttr(std::string("value"))) {
599
        Py::Long longObj(object.getAttr(std::string("value")));
600
        return longObj.as_long();
601
    }
602

603
    return 0;
604
}
605

606
qsizetype PythonWrapper::toEnum(PyObject* pyPtr)
607
{
608
    try {
609
        return tryEnum(pyPtr);
610
    }
611
    catch (Py::Exception&) {
612
        Base::PyException e;
613
        e.ReportException();
614
        return 0;
615
    }
616
}
617

618
qsizetype PythonWrapper::toEnum(const Py::Object& pyobject)
619
{
620
    return toEnum(pyobject.ptr());
621
}
622

623
Py::Object PythonWrapper::tryToStandardButton(qsizetype value)
624
{
625
    std::stringstream cmd;
626
    cmd << "from PySide import QtWidgets\n";
627
    cmd << "btn = QtWidgets.QDialogButtonBox.StandardButton(" << value << ")";
628
    return Py::asObject(Base::Interpreter().getValue(cmd.str().c_str(), "btn"));
629
}
630

631
Py::Object PythonWrapper::toStandardButton(qsizetype value)
632
{
633
    try {
634
        return tryToStandardButton(value);
635
    }
636
    catch (Py::Exception& e) {
637
        e.clear();
638
        return Py::Long(value);
639
    }
640
}
641

642
QGraphicsItem* PythonWrapper::toQGraphicsItem(PyObject* pyPtr)
643
{
644
    return qt_getCppType<QGraphicsItem>(pyPtr);
645
}
646

647
QGraphicsItem* PythonWrapper::toQGraphicsItem(const Py::Object& pyobject)
648
{
649
    return toQGraphicsItem(pyobject.ptr());
650
}
651

652
QGraphicsObject* PythonWrapper::toQGraphicsObject(PyObject* pyPtr)
653
{
654
    return qt_getCppType<QGraphicsObject>(pyPtr);
655
}
656

657
QGraphicsObject* PythonWrapper::toQGraphicsObject(const Py::Object& pyobject)
658
{
659
    return toQGraphicsObject(pyobject.ptr());
660
}
661

662
Py::Object PythonWrapper::fromQImage(const QImage& img)
663
{
664
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
665
    auto type = getPyTypeObjectForTypeName<QImage>();
666
    if (type) {
667
        PyObject* pyobj = Shiboken::Conversions::copyToPython(type, const_cast<QImage*>(&img));
668
        return Py::asObject(pyobj);
669
    }
670
#else
671
    // Access shiboken/PySide via Python
672
    Py::Object obj = qt_wrapInstance<const QImage*>(&img, "QImage", "QtGui");
673
    if (!obj.isNull()) {
674
        return obj;
675
    }
676
#endif
677
    throw Py::RuntimeError("Failed to wrap image");
678
}
679

680
QImage *PythonWrapper::toQImage(PyObject *pyobj)
681
{
682
    return qt_getCppType<QImage>(pyobj);
683
}
684

685
Py::Object PythonWrapper::fromQIcon(const QIcon* icon)
686
{
687
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
688
    auto type = getPyTypeObjectForTypeName<QIcon>();
689
    if (type) {
690
        const char* typeName = typeid(*const_cast<QIcon*>(icon)).name();
691
        PyObject* pyobj = Shiboken::Object::newObject(type, const_cast<QIcon*>(icon), true, false, typeName);
692
        return Py::asObject(pyobj);
693
    }
694
#else
695
    // Access shiboken/PySide via Python
696
    Py::Object obj = qt_wrapInstance<const QIcon*>(icon, "QIcon", "QtGui");
697
    if (!obj.isNull()) {
698
        return obj;
699
    }
700
#endif
701
    throw Py::RuntimeError("Failed to wrap icon");
702
}
703

704
QIcon *PythonWrapper::toQIcon(PyObject *pyobj)
705
{
706
    return qt_getCppType<QIcon>(pyobj);
707
}
708

709
Py::Object PythonWrapper::fromQDir(const QDir& dir)
710
{
711
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
712
    auto type = getPyTypeObjectForTypeName<QDir>();
713
    if (type) {
714
        const char* typeName = typeid(dir).name();
715
        PyObject* pyobj = Shiboken::Object::newObject(type, const_cast<QDir*>(&dir), false, false, typeName);
716
        return Py::asObject(pyobj);
717
    }
718
#else
719
    // Access shiboken/PySide via Python
720
    Py::Object obj = qt_wrapInstance<const QDir*>(&dir, "QDir", "QtGui");
721
    if (!obj.isNull()) {
722
        return obj;
723
    }
724
#endif
725
    throw Py::RuntimeError("Failed to wrap directory");
726
}
727

728
QDir* PythonWrapper::toQDir(PyObject* pyobj)
729
{
730
    return qt_getCppType<QDir>(pyobj);
731
}
732

733
Py::Object PythonWrapper::fromQAction(QAction* action)
734
{
735
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
736
    // Access shiboken/PySide via C++
737
    auto type = getPyTypeObjectForTypeName<QAction>();
738
    if (type) {
739
        PyObject* pyobj = Shiboken::Object::newObject(type, action, false, false, "QAction");
740
        WrapperManager::instance().addQObject(action, pyobj);
741
        return Py::asObject(pyobj);
742
    }
743
#else
744
    // Access shiboken/PySide via Python
745
# if QT_VERSION < QT_VERSION_CHECK(6,0,0)
746
    constexpr const char* qtModWithQAction = "QtWidgets";
747
# else
748
    constexpr const char* qtModWithQAction = "QtGui";
749
# endif
750
    Py::Object obj = qt_wrapInstance<QAction*>(action, "QAction", qtModWithQAction);
751
    if (!obj.isNull()) {
752
        return obj;
753
    }
754
#endif
755
    throw Py::RuntimeError("Failed to wrap action");
756
}
757

758
Py::Object PythonWrapper::fromQPrinter(QPrinter* printer)
759
{
760
    if (!printer) {
761
        return Py::None();
762
    }
763
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
764
    // Access shiboken/PySide via C++
765
    auto type = getPyTypeObjectForTypeName<QPrinter>();
766
    if (!type) {
767
        // XXX: Why is QPrinter special?
768
#if defined (HAVE_SHIBOKEN2)
769
        type = reinterpret_cast<SbkObjectType*>(Shiboken::Conversions::getPythonTypeObject("QPrinter"));
770
#else
771
        type = Shiboken::Conversions::getPythonTypeObject("QPrinter");
772
#endif
773
    }
774
    if (type) {
775
        PyObject* pyobj = Shiboken::Object::newObject(type, printer, false, false, "QPrinter");
776
        return Py::asObject(pyobj);
777
    }
778
#else
779
    // Access shiboken/PySide via Python
780
    Py::Object obj = qt_wrapInstance<QPrinter*>(printer, "QPrinter", "QtCore");
781
    if (!obj.isNull()) {
782
        return obj;
783
    }
784
#endif
785
    throw Py::RuntimeError("Failed to wrap printer");
786
}
787

788
Py::Object PythonWrapper::fromQObject(QObject* object, const char* className)
789
{
790
    if (!object) {
791
        return Py::None();
792
    }
793
    const char* typeName;
794
    if (className) {
795
        typeName = className;
796
    }
797
    else {
798
        typeName = object->metaObject()->className();
799
    }
800
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
801
    // Access shiboken/PySide via C++
802
    auto type = getPyTypeObjectForTypeName<QObject>();
803
    if (type) {
804
        PyObject* pyobj = Shiboken::Object::newObject(type, object, false, false, typeName);
805
        WrapperManager::instance().addQObject(object, pyobj);
806
        return Py::asObject(pyobj);
807
    }
808
#else
809
    // Access shiboken/PySide via Python
810
    Py::Object obj = qt_wrapInstance<QObject*>(object, typeName, "QtCore");
811
    if (!obj.isNull()) {
812
        return obj;
813
    }
814
#endif
815
    throw Py::RuntimeError("Failed to wrap object");
816
}
817

818
Py::Object PythonWrapper::fromQWidget(QWidget* widget, const char* className)
819
{
820
    const char* typeName;
821
    if (className) {
822
        typeName = className;
823
    }
824
    else {
825
        typeName = widget->metaObject()->className();
826
    }
827
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
828
    // Access shiboken/PySide via C++
829
    auto type = getPyTypeObjectForTypeName<QWidget>();
830
    if (type) {
831
        PyObject* pyobj = Shiboken::Object::newObject(type, widget, false, false, typeName);
832
        WrapperManager::instance().addQObject(widget, pyobj);
833
        return Py::asObject(pyobj);
834
    }
835
#else
836
    // Access shiboken/PySide via Python
837
    Py::Object obj = qt_wrapInstance<QWidget*>(widget, typeName, "QtWidgets");
838
    if (!obj.isNull()) {
839
        return obj;
840
    }
841
#endif
842
    throw Py::RuntimeError("Failed to wrap widget");
843
}
844

845
const char* PythonWrapper::getWrapperName(QObject* obj) const
846
{
847
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
848
    const QMetaObject* meta = obj->metaObject();
849
    while (meta) {
850
        const char* typeName = meta->className();
851
        PyTypeObject* exactType = Shiboken::Conversions::getPythonTypeObject(typeName);
852
        if (exactType) {
853
            return typeName;
854
        }
855
        meta = meta->superClass();
856
    }
857
#else
858
    QUiLoader ui;
859
    QStringList names = ui.availableWidgets();
860
    const QMetaObject* meta = obj->metaObject();
861
    while (meta) {
862
        const char* typeName = meta->className();
863
        if (names.indexOf(QLatin1String(typeName)) >= 0) {
864
            return typeName;
865
        }
866
        meta = meta->superClass();
867
    }
868
#endif
869
    return "QObject";
870
}
871

872
bool PythonWrapper::loadCoreModule()
873
{
874
    return loadPySideModule("QtCore", SbkPySide_QtCoreTypes);
875
}
876

877
bool PythonWrapper::loadGuiModule()
878
{
879
    return loadPySideModule("QtGui", SbkPySide_QtGuiTypes);
880
}
881

882
bool PythonWrapper::loadWidgetsModule()
883
{
884
    return loadPySideModule("QtWidgets", SbkPySide_QtWidgetsTypes);
885
}
886

887
bool PythonWrapper::loadPrintSupportModule()
888
{
889
    return loadPySideModule("QtPrintSupport", SbkPySide_QtPrintSupportTypes);
890
}
891

892
bool PythonWrapper::loadUiToolsModule()
893
{
894
    return loadPySideModule("QtUiTools", SbkPySide_QtUiToolsTypes);
895
}
896

897
void PythonWrapper::createChildrenNameAttributes(PyObject* root, QObject* object)
898
{
899
    Q_FOREACH (QObject* child, object->children()) {
900
        const QByteArray name = child->objectName().toLocal8Bit();
901

902
        if (!name.isEmpty() && !name.startsWith("_") && !name.startsWith("qt_")) {
903
            bool hasAttr = PyObject_HasAttrString(root, name.constData());
904
            if (!hasAttr) {
905
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
906
                Shiboken::AutoDecRef pyChild(Shiboken::Conversions::pointerToPython(getPyTypeObjectForTypeName<QObject>(), child));
907
                PyObject_SetAttrString(root, name.constData(), pyChild);
908
#else
909
                const char* className = qt_identifyType(child, "QtWidgets");
910
                if (!className) {
911
                    if (qobject_cast<QWidget*>(child)) {
912
                        className = "QWidget";
913
                    }
914
                    else {
915
                        className = "QObject";
916
                    }
917
                }
918

919
                Py::Object pyChild(qt_wrapInstance<QObject*>(child, className, "QtWidgets"));
920
                if (!pyChild.isNull()) {
921
                    PyObject_SetAttrString(root, name.constData(), pyChild.ptr());
922
                }
923
#endif
924
            }
925
            createChildrenNameAttributes(root, child);
926
        }
927
        createChildrenNameAttributes(root, child);
928
    }
929
}
930

931
void PythonWrapper::setParent(PyObject* pyWdg, QObject* parent)
932
{
933
#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE)
934
    if (parent) {
935
        Shiboken::AutoDecRef pyParent(Shiboken::Conversions::pointerToPython(getPyTypeObjectForTypeName<QWidget>(), parent));
936
        Shiboken::Object::setParent(pyParent, pyWdg);
937
    }
938
#else
939
    Q_UNUSED(pyWdg);
940
    Q_UNUSED(parent);
941
#endif
942
}
943

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

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

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

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