ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
pycc.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <CVTypes.h>
9 
10 #include <QMainWindow>
11 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
12 #include <QtOpenGLWidgets/QOpenGLWidget>
13 #else
14 #include <QOpenGLWidget>
15 #endif
16 #include <QProgressDialog>
17 #include <QWidget>
18 
19 #include "casters.h"
20 
21 #undef slots
22 #include <pybind11/numpy.h>
23 #include <pybind11/stl_bind.h>
24 
25 #ifdef PYCC_STAND_ALONE
26 #include "PyPrintLogger.h"
27 #include <FileIOFilter.h>
28 #include <QApplication>
29 #include <QCoreApplication>
30 #endif
31 
32 namespace py = pybind11;
33 using namespace pybind11::literals;
34 
35 void define_qcc_db(py::module &);
36 void define_qcc_io(py::module &);
37 void load_pluginWrappers(py::module &);
38 
39 template <class T> using observer_ptr = std::unique_ptr<T, py::nodelete>;
40 
41 template <typename T>
42 typename std::enable_if<std::is_same<T, double>::value, void>::type
43 define_scalar_type(py::module &m)
44 {
45  m.attr("ScalarType") = py::dtype("float64");
46 }
47 
48 template <typename T>
49 typename std::enable_if<std::is_same<T, float>::value, void>::type define_scalar_type(py::module &m)
50 {
51  m.attr("ScalarType") = py::dtype("float32");
52 }
53 
54 // Some plugins in ACloudViewer are written to always display some
55 // kind of dialogs.
56 //
57 // However, to display a dialog, a QApplication must be running
58 // which is not the case in stand-alone build, thus we provide a function
59 // that allows user to quickly set up a QApplication.
60 //
61 // We do not set a QApplication up by default in stand alone
62 // as we think user might want to do it themselves if they plan on using PyQt5 for example
63 // but we may have to change that.
65 {
66 #ifdef PYCC_STAND_ALONE
67 #ifdef Q_OS_WIN32
68  const py::module_ os = py::module_::import("os");
69  const py::module_ sysconfig = py::module_::import("sysconfig");
70  const QString site_packages = sysconfig.attr("get_path")("purelib").cast<QString>();
71  const QString platform_path = QString("%1\\pycc\\platforms").arg(site_packages);
72  const std::string std_platform_path = platform_path.toStdString();
73  os.attr("environ")["QT_QPA_PLATFORM_PLUGIN_PATH"] = std_platform_path;
74 #endif
75  // TODO use sys.argv ? and if then we don't need the env var thing on windows
76  static char fakeArg0[] = "pycc";
77  static char *fakeArgv[] = {&fakeArg0[0], NULL};
78  static int argc = 1;
79  if (QCoreApplication::instance() == nullptr)
80  {
81  new QApplication(argc, &fakeArgv[0]);
82  }
83 #endif
84  // Whe not built as stand alone, the QApplication is ACloudViewer
85  // so, we have nothing to do.
86 }
87 
88 void import_pyccRuntime(py::module &m)
89 {
90 #if PYBIND11_VERSION_MINOR >= 5
91  const py::module_ pycc_runtime = py::module_::import("pycc_runtime");
92 #else
93  const py::module pycc_runtime = py::module::import("pycc_runtime");
94 #endif
95  const auto runtime_module_content = pycc_runtime.attr("__dict__").cast<py::dict>();
96  const auto pycc_module_content = m.attr("__dict__").cast<py::dict>();
97 
98  for (const auto &entry : runtime_module_content)
99  {
100  pycc_module_content[entry.first] = entry.second;
101  }
102 }
103 
104 void define_someQtThings(py::module &m)
105 {
106  py::class_<QWidget>(m, "QWidget");
107  py::class_<QOpenGLWidget>(m, "QOpenGLWidget");
108  py::class_<QProgressDialog, QWidget>(m, "QProgressDialog");
109  py::class_<QMainWindow, QWidget>(m, "QMainWindow");
110  py::class_<QFont>(m, "QFont")
111  .def("__repr__",
112  [](const QFont &self)
113  { return std::string("<QFont(") + self.toString().toStdString() + ")>"; });
114  py::class_<QSize>(m, "QSize")
115  .def(py::init<>())
116  .def(py::init<int, int>(), "width"_a, "height"_a)
117  .def("height", &QSize::height)
118  .def("width", &QSize::width)
119  .def("isEmpty", &QSize::isEmpty)
120  .def("isValid", &QSize::isValid)
121  .def("__repr__",
122  [](const QSize &self)
123  {
124  return std::string("<QSize(") + std::to_string(self.width()) + ", " +
125  std::to_string(self.height()) + ")>";
126  });
127 
128  py::class_<QPointF>(m, "QPointF");
129  // TODO: https://doc.qt.io/qt-5/qt.html#CursorShape-enum
130  py::enum_<Qt::CursorShape>(m, "QtCursorShape");
131 }
132 
133 void define_pycc(py::module &m)
134 {
135  m.doc() = R"pbdoc(
136  Python module exposing some ACloudViewer functions
137  )pbdoc";
138 
140 
141  py::module::import("cccorelib");
142 
143 #ifdef PYCC_STAND_ALONE
144  // When pycc is a standalone python module, there are
145  // some ACloudViewer facilities we have to initialize ourselves
146  // to have a more complete experience
148  FileIOFilter::InitInternalFilters();
149 
150  // TODO at module exit definition to cleanup ?
151 #endif
152 
153  m.attr("PointCoordinateType") = py::dtype("float32");
154  define_scalar_type<ScalarType>(m);
155 
156  define_qcc_db(m);
157  define_qcc_io(m);
158 
159  m.def("InitQApplication", &InitQApplication);
160 
161  try
162  {
164  }
165  catch (std::exception &e)
166  {
167  CVLog::Warning("Failed to import wrapped plugins: ", e.what());
168  }
169 
170 #ifdef DEFINE_PYCC_RUNTIME
171  try
172  {
174  }
175  catch (std::exception &e)
176  {
177  CVLog::Warning("Failed to import pycc_runtime content: ", e.what());
178  }
179 #endif
180 }
181 
182 #ifdef USE_EMBEDDED_MODULES
183 #include <pybind11/embed.h>
185 {
186  define_pycc(m);
187 }
188 #else
190 {
191  define_pycc(m);
192 }
193 #endif
int width
int height
char type
PYBIND11_EMBEDDED_MODULE(ccinternals, m)
Definition: Runtime.cpp:117
#define NULL
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static void RegisterInstance(CVLog *logInstance)
Registers a unique instance.
Definition: CVLog.cpp:81
std::string to_string(const T &n)
Definition: Common.h:20
void import_pyccRuntime(py::module &m)
Definition: pycc.cpp:88
void load_pluginWrappers(py::module &)
std::enable_if< std::is_same< T, double >::value, void >::type define_scalar_type(py::module &m)
Definition: pycc.cpp:43
void define_someQtThings(py::module &m)
Definition: pycc.cpp:104
void define_pycc(py::module &m)
Definition: pycc.cpp:133
void InitQApplication()
Definition: pycc.cpp:64
void define_qcc_db(py::module &)
Definition: qcc_db.cpp:70
void define_qcc_io(py::module &)
Definition: qcc_io.cpp:63
PYBIND11_MODULE(pycc, m)
Definition: pycc.cpp:189
std::unique_ptr< T, py::nodelete > observer_ptr
A unique_ptr that never free its ptr.
Definition: wrappers.h:24