aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/async/eratosthenes/doc/eratosthenes.rst53
-rw-r--r--examples/async/eratosthenes/doc/eratosthenes.svg1
-rw-r--r--examples/async/eratosthenes/eratosthenes.pyproject3
-rw-r--r--examples/async/eratosthenes/eratosthenes_asyncio.py220
-rw-r--r--examples/async/eratosthenes/eratosthenes_trio.py197
-rw-r--r--examples/async/eratosthenes/requirements_trio.txt2
-rw-r--r--examples/async/minimal/doc/minimal.pngbin0 -> 8135 bytes
-rw-r--r--examples/async/minimal/doc/minimal.rst52
-rw-r--r--examples/async/minimal/minimal.pyproject3
-rw-r--r--examples/async/minimal/minimal_asyncio.py123
-rw-r--r--examples/async/minimal/minimal_trio.py107
-rw-r--r--examples/async/minimal/requirements_trio.txt1
-rw-r--r--examples/bluetooth/btscanner/device.py6
-rw-r--r--examples/bluetooth/btscanner/main.py3
-rw-r--r--examples/bluetooth/btscanner/service.py2
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/App.qml99
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/BluetoothAlarmDialog.qml79
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/BottomLine.qml12
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/Connect.qml159
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/GameButton.qml39
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/GamePage.qml36
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/GameSettings.qml51
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/Main.qml71
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/Measure.qml212
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/SplashScreen.qml30
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/Stats.qml55
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/StatsLabel.qml34
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/TitleBar.qml54
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/images/bt_off_to_on.pngbin0 -> 6143 bytes
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/images/heart.pngbin0 -> 2664 bytes
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/images/logo.pngbin0 -> 31915 bytes
-rw-r--r--examples/bluetooth/heartrate_game/HeartRateGame/qmldir14
-rw-r--r--examples/bluetooth/heartrate_game/bluetoothbaseclass.py40
-rw-r--r--examples/bluetooth/heartrate_game/connectionhandler.py76
-rw-r--r--examples/bluetooth/heartrate_game/devicefinder.py137
-rw-r--r--examples/bluetooth/heartrate_game/devicehandler.py306
-rw-r--r--examples/bluetooth/heartrate_game/deviceinfo.py38
-rw-r--r--examples/bluetooth/heartrate_game/doc/heartrate_game.rst9
-rw-r--r--examples/bluetooth/heartrate_game/heartrate_game.pyproject23
-rw-r--r--examples/bluetooth/heartrate_game/heartrate_global.py19
-rw-r--r--examples/bluetooth/heartrate_game/main.py53
-rw-r--r--examples/bluetooth/heartrate_server/doc/heartrate_server.rst8
-rw-r--r--examples/bluetooth/heartrate_server/heartrate_server.py94
-rw-r--r--examples/bluetooth/heartrate_server/heartrate_server.pyproject3
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/Characteristics.qml121
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/Devices.qml121
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/Dialog.qml48
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/Header.qml25
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/Label.qml16
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/Main.qml31
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/Menu.qml55
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/Services.qml115
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/assets/busy_dark.pngbin0 -> 1130 bytes
-rw-r--r--examples/bluetooth/lowenergyscanner/Scanner/qmldir10
-rw-r--r--examples/bluetooth/lowenergyscanner/characteristicinfo.py88
-rw-r--r--examples/bluetooth/lowenergyscanner/device.py279
-rw-r--r--examples/bluetooth/lowenergyscanner/deviceinfo.py35
-rw-r--r--examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.pngbin0 -> 72365 bytes
-rw-r--r--examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.rst9
-rw-r--r--examples/bluetooth/lowenergyscanner/lowenergyscanner.pyproject7
-rw-r--r--examples/bluetooth/lowenergyscanner/main.py27
-rw-r--r--examples/bluetooth/lowenergyscanner/serviceinfo.py66
-rw-r--r--examples/charts/audio/audio.py3
-rw-r--r--examples/charts/chartthemes/README.md2
-rw-r--r--examples/charts/chartthemes/main.py61
-rw-r--r--examples/charts/donutbreakdown/donutbreakdown.py5
-rw-r--r--examples/charts/legend/legend.py10
-rw-r--r--examples/charts/lineandbar/lineandbar.py10
-rw-r--r--examples/charts/nesteddonuts/nesteddonuts.py3
-rw-r--r--examples/charts/percentbarchart/percentbarchart.py3
-rw-r--r--examples/charts/pointconfiguration/chartwindow.py156
-rw-r--r--examples/charts/pointconfiguration/doc/pointconfiguration.pngbin0 -> 34991 bytes
-rw-r--r--examples/charts/pointconfiguration/doc/pointconfiguration.rst144
-rw-r--r--examples/charts/pointconfiguration/pointconfiguration.py17
-rw-r--r--examples/charts/pointconfiguration/pointconfiguration.pyproject3
-rw-r--r--examples/charts/pointselectionandmarkers/doc/pointselectionandmarkers.pngbin0 -> 47669 bytes
-rw-r--r--examples/charts/pointselectionandmarkers/doc/pointselectionandmarkers.rst72
-rw-r--r--examples/charts/pointselectionandmarkers/images/blue_triangle.pngbin0 -> 2220 bytes
-rw-r--r--examples/charts/pointselectionandmarkers/images/green_triangle.pngbin0 -> 1389 bytes
-rw-r--r--examples/charts/pointselectionandmarkers/markers.qrc6
-rw-r--r--examples/charts/pointselectionandmarkers/pointselectionandmarkers.py127
-rw-r--r--examples/charts/pointselectionandmarkers/pointselectionandmarkers.pyproject3
-rw-r--r--examples/charts/pointselectionandmarkers/rc_markers.py275
-rw-r--r--examples/charts/pointselectionandmarkers/utilities.py67
-rw-r--r--examples/charts/qmlpolarchart/qmlpolarchart.py4
-rw-r--r--examples/corelib/settingseditor/settingseditor.py17
-rw-r--r--examples/corelib/threads/mandelbrot.py3
-rw-r--r--examples/datavisualization/graphgallery/axesinputhandler.py101
-rw-r--r--examples/datavisualization/graphgallery/bargraph.py281
-rw-r--r--examples/datavisualization/graphgallery/custominputhandler.py177
-rw-r--r--examples/datavisualization/graphgallery/data/layer_1.pngbin0 -> 34540 bytes
-rw-r--r--examples/datavisualization/graphgallery/data/layer_2.pngbin0 -> 10553 bytes
-rw-r--r--examples/datavisualization/graphgallery/data/layer_3.pngbin0 -> 7119 bytes
-rw-r--r--examples/datavisualization/graphgallery/data/license.txt77
-rw-r--r--examples/datavisualization/graphgallery/data/maptexture.jpgbin0 -> 352922 bytes
-rw-r--r--examples/datavisualization/graphgallery/data/oilrig.obj2322
-rw-r--r--examples/datavisualization/graphgallery/data/pipe.obj330
-rw-r--r--examples/datavisualization/graphgallery/data/raindata.txt158
-rw-r--r--examples/datavisualization/graphgallery/data/refinery.obj2330
-rw-r--r--examples/datavisualization/graphgallery/data/topography.pngbin0 -> 395504 bytes
-rw-r--r--examples/datavisualization/graphgallery/doc/graph_gallery.webpbin0 -> 95552 bytes
-rw-r--r--examples/datavisualization/graphgallery/doc/graphgallery.rst11
-rw-r--r--examples/datavisualization/graphgallery/graphgallery.pyproject18
-rw-r--r--examples/datavisualization/graphgallery/graphgallery.qrc13
-rw-r--r--examples/datavisualization/graphgallery/graphmodifier.py395
-rw-r--r--examples/datavisualization/graphgallery/highlightseries.py94
-rw-r--r--examples/datavisualization/graphgallery/main.py50
-rw-r--r--examples/datavisualization/graphgallery/rainfalldata.py125
-rw-r--r--examples/datavisualization/graphgallery/scatterdatamodifier.py151
-rw-r--r--examples/datavisualization/graphgallery/scattergraph.py128
-rw-r--r--examples/datavisualization/graphgallery/surfacegraph.py265
-rw-r--r--examples/datavisualization/graphgallery/surfacegraphmodifier.py643
-rw-r--r--examples/datavisualization/graphgallery/topographicseries.py57
-rw-r--r--examples/datavisualization/graphgallery/variantbardatamapping.py67
-rw-r--r--examples/datavisualization/graphgallery/variantbardataproxy.py100
-rw-r--r--examples/datavisualization/graphgallery/variantdataset.py39
-rw-r--r--examples/datavisualization/qmlsurfacegallery/datasource.py99
-rw-r--r--examples/datavisualization/qmlsurfacegallery/doc/qmlsurfacegallery.rst9
-rw-r--r--examples/datavisualization/qmlsurfacegallery/doc/qmlsurfacegallery.webpbin0 -> 100206 bytes
-rw-r--r--examples/datavisualization/qmlsurfacegallery/main.py39
-rw-r--r--examples/datavisualization/qmlsurfacegallery/qml/qmlsurfacegallery/SpectrogramData.qml1545
-rw-r--r--examples/datavisualization/qmlsurfacegallery/qml/qmlsurfacegallery/SurfaceHeightMap.qml227
-rw-r--r--examples/datavisualization/qmlsurfacegallery/qml/qmlsurfacegallery/SurfaceOscilloscope.qml430
-rw-r--r--examples/datavisualization/qmlsurfacegallery/qml/qmlsurfacegallery/SurfaceSpectrogram.qml272
-rw-r--r--examples/datavisualization/qmlsurfacegallery/qml/qmlsurfacegallery/heightmap.pngbin0 -> 638731 bytes
-rw-r--r--examples/datavisualization/qmlsurfacegallery/qml/qmlsurfacegallery/main.qml57
-rw-r--r--examples/datavisualization/qmlsurfacegallery/qmlsurfacegallery.pyproject10
-rw-r--r--examples/datavisualization/qmlsurfacegallery/qmlsurfacegallery.qrc5
-rw-r--r--examples/datavisualization/qmlsurfacegallery/rc_qmlsurfacegallery.py39967
-rw-r--r--examples/datavisualization/surface/surfacegraph.py14
-rw-r--r--examples/datavisualization/surface_model_numpy/surfacegraph.py14
-rw-r--r--examples/datavisualization/surface_numpy/doc/surface_numpy.rst2
-rw-r--r--examples/datavisualization/surface_numpy/surfacegraph.py14
-rw-r--r--examples/dbus/listnames/listnames.py3
-rw-r--r--examples/dbus/pingpong/ping.py2
-rw-r--r--examples/declarative/extending/chapter1-basics/app.qml27
-rw-r--r--examples/declarative/extending/chapter1-basics/basics.py68
-rw-r--r--examples/declarative/extending/chapter2-methods/app.qml33
-rw-r--r--examples/declarative/extending/chapter2-methods/methods.py75
-rw-r--r--examples/declarative/extending/chapter3-bindings/app.qml41
-rw-r--r--examples/declarative/extending/chapter3-bindings/bindings.py79
-rw-r--r--examples/declarative/extending/chapter4-customPropertyTypes/app.qml23
-rw-r--r--examples/declarative/extending/chapter4-customPropertyTypes/customPropertyTypes.py82
-rw-r--r--examples/declarative/extending/chapter5-listproperties/app.qml33
-rw-r--r--examples/declarative/extending/chapter5-listproperties/listproperties.py96
-rw-r--r--examples/declarative/openglunderqml/squirclerenderer.py99
-rw-r--r--examples/declarative/referenceexamples/adding/adding.pyproject5
-rw-r--r--examples/declarative/referenceexamples/adding/doc/adding.rst67
-rw-r--r--examples/declarative/referenceexamples/adding/example.qml9
-rw-r--r--examples/declarative/referenceexamples/adding/main.py30
-rw-r--r--examples/declarative/referenceexamples/adding/person.py35
-rw-r--r--examples/declarative/referenceexamples/attached/attached.pyproject3
-rw-r--r--examples/declarative/referenceexamples/attached/birthdayparty.py63
-rw-r--r--examples/declarative/referenceexamples/attached/doc/attached.rst12
-rw-r--r--examples/declarative/referenceexamples/attached/example.qml22
-rw-r--r--examples/declarative/referenceexamples/attached/main.py46
-rw-r--r--examples/declarative/referenceexamples/attached/person.py46
-rw-r--r--examples/declarative/referenceexamples/binding/binding.pyproject3
-rw-r--r--examples/declarative/referenceexamples/binding/birthdayparty.py83
-rw-r--r--examples/declarative/referenceexamples/binding/example.qml29
-rw-r--r--examples/declarative/referenceexamples/binding/happybirthdaysong.py47
-rw-r--r--examples/declarative/referenceexamples/binding/main.py52
-rw-r--r--examples/declarative/referenceexamples/binding/person.py53
-rw-r--r--examples/declarative/referenceexamples/coercion/birthdayparty.py41
-rw-r--r--examples/declarative/referenceexamples/coercion/coercion.pyproject3
-rw-r--r--examples/declarative/referenceexamples/coercion/doc/coercion.rst35
-rw-r--r--examples/declarative/referenceexamples/coercion/example.qml16
-rw-r--r--examples/declarative/referenceexamples/coercion/main.py36
-rw-r--r--examples/declarative/referenceexamples/coercion/person.py47
-rw-r--r--examples/declarative/referenceexamples/default/birthdayparty.py42
-rw-r--r--examples/declarative/referenceexamples/default/default.pyproject3
-rw-r--r--examples/declarative/referenceexamples/default/doc/default.rst30
-rw-r--r--examples/declarative/referenceexamples/default/example.qml15
-rw-r--r--examples/declarative/referenceexamples/default/main.py36
-rw-r--r--examples/declarative/referenceexamples/default/person.py46
-rw-r--r--examples/declarative/referenceexamples/grouped/birthdayparty.py42
-rw-r--r--examples/declarative/referenceexamples/grouped/doc/grouped.rst17
-rw-r--r--examples/declarative/referenceexamples/grouped/example.qml33
-rw-r--r--examples/declarative/referenceexamples/grouped/grouped.pyproject3
-rw-r--r--examples/declarative/referenceexamples/grouped/main.py43
-rw-r--r--examples/declarative/referenceexamples/grouped/person.py85
-rw-r--r--examples/declarative/referenceexamples/methods/birthdayparty.py47
-rw-r--r--examples/declarative/referenceexamples/methods/example.qml19
-rw-r--r--examples/declarative/referenceexamples/methods/main.py32
-rw-r--r--examples/declarative/referenceexamples/methods/methods.pyproject3
-rw-r--r--examples/declarative/referenceexamples/methods/person.py34
-rw-r--r--examples/declarative/referenceexamples/properties/birthdayparty.py41
-rw-r--r--examples/declarative/referenceexamples/properties/doc/properties.rst89
-rw-r--r--examples/declarative/referenceexamples/properties/example.qml16
-rw-r--r--examples/declarative/referenceexamples/properties/main.py34
-rw-r--r--examples/declarative/referenceexamples/properties/person.py35
-rw-r--r--examples/declarative/referenceexamples/properties/properties.pyproject6
-rw-r--r--examples/declarative/referenceexamples/valuesource/birthdayparty.py76
-rw-r--r--examples/declarative/referenceexamples/valuesource/doc/valuesource.rst20
-rw-r--r--examples/declarative/referenceexamples/valuesource/example.qml27
-rw-r--r--examples/declarative/referenceexamples/valuesource/happybirthdaysong.py47
-rw-r--r--examples/declarative/referenceexamples/valuesource/main.py51
-rw-r--r--examples/declarative/referenceexamples/valuesource/person.py46
-rw-r--r--examples/declarative/referenceexamples/valuesource/valuesource.pyproject3
-rw-r--r--examples/declarative/rendercontrol/rendercontrol_opengl/cuberenderer.py186
-rw-r--r--examples/declarative/rendercontrol/rendercontrol_opengl/demo.qml161
-rw-r--r--examples/declarative/rendercontrol/rendercontrol_opengl/window_singlethreaded.py271
-rw-r--r--examples/demos/documentviewer/abstractviewer.py185
-rw-r--r--examples/demos/documentviewer/doc/abstractviewer.py.rstinc25
-rw-r--r--examples/demos/documentviewer/doc/documentviewer.pngbin0 -> 22897 bytes
-rw-r--r--examples/demos/documentviewer/doc/documentviewer.rst13
-rw-r--r--examples/demos/documentviewer/doc/jsonviewer.py.rstinc11
-rw-r--r--examples/demos/documentviewer/doc/mainwindow.py.rstinc11
-rw-r--r--examples/demos/documentviewer/doc/viewerfactory.py.rstinc3
-rw-r--r--examples/demos/documentviewer/documentviewer.pyproject14
-rw-r--r--examples/demos/documentviewer/documentviewer.qrc42
-rw-r--r--examples/demos/documentviewer/images/copy.png (renamed from examples/widgets/mainwindows/mdi/images/copy.png)bin1338 -> 1338 bytes
-rw-r--r--examples/demos/documentviewer/images/copy@2x.pngbin0 -> 2219 bytes
-rw-r--r--examples/demos/documentviewer/images/cut.png (renamed from examples/widgets/mainwindows/mdi/images/cut.png)bin1323 -> 1323 bytes
-rw-r--r--examples/demos/documentviewer/images/cut@2x.pngbin0 -> 2888 bytes
-rw-r--r--examples/demos/documentviewer/images/document-open-recent.svgzbin0 -> 4656 bytes
-rw-r--r--examples/demos/documentviewer/images/go-next-view-page.pngbin0 -> 1125 bytes
-rw-r--r--examples/demos/documentviewer/images/go-next-view-page@2x.pngbin0 -> 2312 bytes
-rw-r--r--examples/demos/documentviewer/images/go-next-view.pngbin0 -> 990 bytes
-rw-r--r--examples/demos/documentviewer/images/go-next-view@2x.pngbin0 -> 1830 bytes
-rw-r--r--examples/demos/documentviewer/images/go-next.svgzbin0 -> 5473 bytes
-rw-r--r--examples/demos/documentviewer/images/go-previous-view-page.pngbin0 -> 1119 bytes
-rw-r--r--examples/demos/documentviewer/images/go-previous-view-page@2x.pngbin0 -> 2067 bytes
-rw-r--r--examples/demos/documentviewer/images/go-previous-view.pngbin0 -> 1010 bytes
-rw-r--r--examples/demos/documentviewer/images/go-previous-view@2x.pngbin0 -> 1875 bytes
-rw-r--r--examples/demos/documentviewer/images/go-previous.svgzbin0 -> 5690 bytes
-rw-r--r--examples/demos/documentviewer/images/help-about.svgzbin0 -> 7056 bytes
-rw-r--r--examples/demos/documentviewer/images/magnifier.pngbin0 -> 1006 bytes
-rw-r--r--examples/demos/documentviewer/images/magnifier@2x.pngbin0 -> 2249 bytes
-rw-r--r--examples/demos/documentviewer/images/open.png (renamed from examples/widgets/mainwindows/mdi/images/open.png)bin2073 -> 2073 bytes
-rw-r--r--examples/demos/documentviewer/images/open@2x.pngbin0 -> 1017 bytes
-rw-r--r--examples/demos/documentviewer/images/paste.png (renamed from examples/widgets/mainwindows/mdi/images/paste.png)bin1645 -> 1645 bytes
-rw-r--r--examples/demos/documentviewer/images/paste@2x.pngbin0 -> 2782 bytes
-rw-r--r--examples/demos/documentviewer/images/print.pngbin0 -> 678 bytes
-rw-r--r--examples/demos/documentviewer/images/print2x.pngbin0 -> 931 bytes
-rw-r--r--examples/demos/documentviewer/images/qt-logo.pngbin0 -> 1483 bytes
-rw-r--r--examples/demos/documentviewer/images/qt-logo@2x.pngbin0 -> 1933 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-fit-best.pngbin0 -> 1067 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-fit-best@2x.pngbin0 -> 1692 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-fit-width.pngbin0 -> 905 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-fit-width@2x.pngbin0 -> 1724 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-in.pngbin0 -> 952 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-in@2x.pngbin0 -> 2100 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-original.pngbin0 -> 946 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-original@2x.pngbin0 -> 2005 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-out.pngbin0 -> 940 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-out@2x.pngbin0 -> 1971 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-previous.pngbin0 -> 946 bytes
-rw-r--r--examples/demos/documentviewer/images/zoom-previous@2x.pngbin0 -> 2049 bytes
-rw-r--r--examples/demos/documentviewer/jsonviewer/jsonviewer.py396
-rw-r--r--examples/demos/documentviewer/main.py37
-rw-r--r--examples/demos/documentviewer/mainwindow.py179
-rw-r--r--examples/demos/documentviewer/mainwindow.ui272
-rw-r--r--examples/demos/documentviewer/pdfviewer/pdfviewer.py200
-rw-r--r--examples/demos/documentviewer/pdfviewer/zoomselector.py54
-rw-r--r--examples/demos/documentviewer/rc_documentviewer.py5135
-rw-r--r--examples/demos/documentviewer/recentfilemenu.py34
-rw-r--r--examples/demos/documentviewer/recentfiles.py159
-rw-r--r--examples/demos/documentviewer/txtviewer/txtviewer.py144
-rw-r--r--examples/demos/documentviewer/ui_mainwindow.py230
-rw-r--r--examples/demos/documentviewer/viewerfactory.py114
-rw-r--r--examples/designer/taskmenuextension/tictactoe.py2
-rw-r--r--examples/designer/taskmenuextension/tictactoeplugin.py3
-rw-r--r--examples/designer/taskmenuextension/tictactoetaskmenu.py2
-rw-r--r--examples/examples.pyproject3
-rw-r--r--examples/external/matplotlib/widget3d/widget3d.py2
-rw-r--r--examples/external/matplotlib/widget_gaussian/widget_gaussian.py7
-rw-r--r--examples/external/networkx/doc/networkx.pngbin0 -> 28017 bytes
-rw-r--r--examples/external/networkx/doc/networkx.rst8
-rw-r--r--examples/external/networkx/main.py346
-rw-r--r--examples/external/networkx/networkx.pyproject3
-rw-r--r--examples/external/networkx/requirements.txt3
-rw-r--r--examples/graphs/minimalsurfacegraph/doc/minimalsurfacegraph.rst4
-rw-r--r--examples/graphs/minimalsurfacegraph/main.py54
-rw-r--r--examples/graphs/widgetgallery/axesinputhandler.py101
-rw-r--r--examples/graphs/widgetgallery/bargraph.py272
-rw-r--r--examples/graphs/widgetgallery/custominputhandler.py177
-rw-r--r--examples/graphs/widgetgallery/data/layer_1.pngbin0 -> 34540 bytes
-rw-r--r--examples/graphs/widgetgallery/data/layer_2.pngbin0 -> 10553 bytes
-rw-r--r--examples/graphs/widgetgallery/data/layer_3.pngbin0 -> 7119 bytes
-rw-r--r--examples/graphs/widgetgallery/data/license.txt77
-rw-r--r--examples/graphs/widgetgallery/data/maptexture.jpgbin0 -> 352922 bytes
-rw-r--r--examples/graphs/widgetgallery/data/narrowarrow.meshbin0 -> 15420 bytes
-rw-r--r--examples/graphs/widgetgallery/data/oilrig.meshbin0 -> 69728 bytes
-rw-r--r--examples/graphs/widgetgallery/data/pipe.meshbin0 -> 4760 bytes
-rw-r--r--examples/graphs/widgetgallery/data/raindata.txt158
-rw-r--r--examples/graphs/widgetgallery/data/refinery.meshbin0 -> 75216 bytes
-rw-r--r--examples/graphs/widgetgallery/data/topography.pngbin0 -> 395504 bytes
-rw-r--r--examples/graphs/widgetgallery/doc/widgetgallery.rst11
-rw-r--r--examples/graphs/widgetgallery/doc/widgetgallery.webpbin0 -> 93150 bytes
-rw-r--r--examples/graphs/widgetgallery/graphmodifier.py394
-rw-r--r--examples/graphs/widgetgallery/highlightseries.py94
-rw-r--r--examples/graphs/widgetgallery/main.py42
-rw-r--r--examples/graphs/widgetgallery/rainfalldata.py125
-rw-r--r--examples/graphs/widgetgallery/scatterdatamodifier.py150
-rw-r--r--examples/graphs/widgetgallery/scattergraph.py121
-rw-r--r--examples/graphs/widgetgallery/surfacegraph.py256
-rw-r--r--examples/graphs/widgetgallery/surfacegraphmodifier.py642
-rw-r--r--examples/graphs/widgetgallery/topographicseries.py57
-rw-r--r--examples/graphs/widgetgallery/variantbardatamapping.py67
-rw-r--r--examples/graphs/widgetgallery/variantbardataproxy.py100
-rw-r--r--examples/graphs/widgetgallery/variantdataset.py39
-rw-r--r--examples/graphs/widgetgallery/widgetgallery.pyproject29
-rw-r--r--examples/gui/analogclock/analogclock.pyproject3
-rw-r--r--examples/gui/analogclock/doc/analogclock.rst8
-rw-r--r--examples/gui/analogclock/main.py77
-rw-r--r--examples/installer_test/hello.py5
-rw-r--r--examples/location/mapviewer/MapViewer/Main.qml460
-rw-r--r--examples/location/mapviewer/MapViewer/forms/Geocode.qml42
-rw-r--r--examples/location/mapviewer/MapViewer/forms/GeocodeForm.ui.qml136
-rw-r--r--examples/location/mapviewer/MapViewer/forms/Locale.qml45
-rw-r--r--examples/location/mapviewer/MapViewer/forms/LocaleForm.ui.qml116
-rw-r--r--examples/location/mapviewer/MapViewer/forms/Message.qml21
-rw-r--r--examples/location/mapviewer/MapViewer/forms/MessageForm.ui.qml69
-rw-r--r--examples/location/mapviewer/MapViewer/forms/ReverseGeocode.qml38
-rw-r--r--examples/location/mapviewer/MapViewer/forms/ReverseGeocodeForm.ui.qml103
-rw-r--r--examples/location/mapviewer/MapViewer/forms/RouteAddress.qml105
-rw-r--r--examples/location/mapviewer/MapViewer/forms/RouteAddressForm.ui.qml160
-rw-r--r--examples/location/mapviewer/MapViewer/forms/RouteCoordinate.qml41
-rw-r--r--examples/location/mapviewer/MapViewer/forms/RouteCoordinateForm.ui.qml136
-rw-r--r--examples/location/mapviewer/MapViewer/forms/RouteList.qml50
-rw-r--r--examples/location/mapviewer/MapViewer/forms/RouteListDelegate.qml42
-rw-r--r--examples/location/mapviewer/MapViewer/forms/RouteListHeader.qml47
-rw-r--r--examples/location/mapviewer/MapViewer/helper.js44
-rw-r--r--examples/location/mapviewer/MapViewer/map/MapComponent.qml497
-rw-r--r--examples/location/mapviewer/MapViewer/map/MapSliders.qml282
-rw-r--r--examples/location/mapviewer/MapViewer/map/Marker.qml64
-rw-r--r--examples/location/mapviewer/MapViewer/map/MiniMap.qml78
-rw-r--r--examples/location/mapviewer/MapViewer/menus/ItemPopupMenu.qml19
-rw-r--r--examples/location/mapviewer/MapViewer/menus/MainMenu.qml122
-rw-r--r--examples/location/mapviewer/MapViewer/menus/MapPopupMenu.qml30
-rw-r--r--examples/location/mapviewer/MapViewer/menus/MarkerPopupMenu.qml38
-rw-r--r--examples/location/mapviewer/MapViewer/qmldir27
-rw-r--r--examples/location/mapviewer/MapViewer/resources/marker.pngbin0 -> 752 bytes
-rw-r--r--examples/location/mapviewer/MapViewer/resources/marker_blue.pngbin0 -> 3523 bytes
-rw-r--r--examples/location/mapviewer/MapViewer/resources/scale.pngbin0 -> 98 bytes
-rw-r--r--examples/location/mapviewer/MapViewer/resources/scale_end.pngbin0 -> 93 bytes
-rw-r--r--examples/location/mapviewer/doc/mapviewer.rst12
-rw-r--r--examples/location/mapviewer/doc/mapviewer.webpbin0 -> 79588 bytes
-rw-r--r--examples/location/mapviewer/main.py75
-rw-r--r--examples/location/mapviewer/mapviewer.pyproject33
-rw-r--r--examples/macextras/macpasteboardmime/macpasteboardmime.py90
-rw-r--r--examples/macextras/macpasteboardmime/macpasteboardmime.pyproject3
-rw-r--r--examples/multimedia/audiooutput/audiooutput.py4
-rw-r--r--examples/multimedia/audiosource/audiosource.py2
-rw-r--r--examples/multimedia/camera/camera.py448
-rw-r--r--examples/multimedia/camera/camera.pyproject9
-rw-r--r--examples/multimedia/camera/camera.ui497
-rw-r--r--examples/multimedia/camera/doc/camera.pngbin63404 -> 0 bytes
-rw-r--r--examples/multimedia/camera/doc/camera.rst4
-rw-r--r--examples/multimedia/camera/doc/camera.webpbin0 -> 11666 bytes
-rw-r--r--examples/multimedia/camera/imagesettings.py56
-rw-r--r--examples/multimedia/camera/imagesettings.ui123
-rw-r--r--examples/multimedia/camera/main.py17
-rw-r--r--examples/multimedia/camera/metadatadialog.py86
-rw-r--r--examples/multimedia/camera/ui_camera.py232
-rw-r--r--examples/multimedia/camera/ui_imagesettings.py94
-rw-r--r--examples/multimedia/camera/ui_videosettings.py178
-rw-r--r--examples/multimedia/camera/videosettings.py161
-rw-r--r--examples/multimedia/camera/videosettings.ui213
-rw-r--r--examples/multimedia/player/player.py7
-rw-r--r--examples/multimedia/screencapture/doc/screencapture.rst42
-rw-r--r--examples/multimedia/screencapture/doc/screencapture.webpbin0 -> 53592 bytes
-rw-r--r--examples/multimedia/screencapture/main.py20
-rw-r--r--examples/multimedia/screencapture/screencapture.pyproject3
-rw-r--r--examples/multimedia/screencapture/screencapturepreview.py151
-rw-r--r--examples/multimedia/screencapture/screenlistmodel.py38
-rw-r--r--examples/multimedia/screencapture/windowlistmodel.py30
-rw-r--r--examples/network/fortuneclient/fortuneclient.py5
-rw-r--r--examples/network/fortuneserver/fortuneserver.py2
-rw-r--r--examples/network/googlesuggest/googlesuggest.py2
-rw-r--r--examples/opengl/contextinfo/contextinfo.py10
-rw-r--r--examples/opengl/hellogl2/doc/hellogl2.rst14
-rw-r--r--examples/opengl/hellogl2/glwidget.py272
-rw-r--r--examples/opengl/hellogl2/hellogl2.py438
-rw-r--r--examples/opengl/hellogl2/hellogl2.pyproject2
-rw-r--r--examples/opengl/hellogl2/logo.py101
-rw-r--r--examples/opengl/hellogl2/main.py58
-rw-r--r--examples/opengl/hellogl2/mainwindow.py29
-rw-r--r--examples/opengl/hellogl2/window.py111
-rw-r--r--examples/opengl/textures/textures.pyproject2
-rw-r--r--examples/opengl/threadedqopenglwidget/glwidget.py1
-rw-r--r--examples/pdf/quickpdfviewer/doc/quickpdfviewer.rst4
-rw-r--r--examples/pdf/quickpdfviewer/main.py49
-rw-r--r--examples/pdf/quickpdfviewer/quickpdfviewer.pyproject3
-rw-r--r--examples/pdf/quickpdfviewer/rc_viewer.py5505
-rw-r--r--examples/pdf/quickpdfviewer/resources/document-open.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/edit-clear.svg15
-rw-r--r--examples/pdf/quickpdfviewer/resources/edit-copy.svg15
-rw-r--r--examples/pdf/quickpdfviewer/resources/edit-select-all.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/go-down-search.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/go-next-view-page.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/go-previous-view-page.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/go-up-search.svg8
-rw-r--r--examples/pdf/quickpdfviewer/resources/rotate-left.svg6
-rw-r--r--examples/pdf/quickpdfviewer/resources/rotate-right.svg6
-rw-r--r--examples/pdf/quickpdfviewer/resources/test.pdfbin0 -> 76633 bytes
-rw-r--r--examples/pdf/quickpdfviewer/resources/zoom-fit-best.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/zoom-fit-width.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/zoom-in.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/zoom-original.svg13
-rw-r--r--examples/pdf/quickpdfviewer/resources/zoom-out.svg13
-rw-r--r--examples/pdf/quickpdfviewer/viewer.qml307
-rw-r--r--examples/pdf/quickpdfviewer/viewer.qrc20
-rw-r--r--examples/pdfwidgets/pdfviewer/doc/pdfviewer.rst5
-rw-r--r--examples/pdfwidgets/pdfviewer/images/document-open.svgzbin0 -> 4276 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/go-next-view-page.svgzbin0 -> 12615 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/go-next-view.svgzbin0 -> 6360 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/go-previous-view-page.svgzbin0 -> 12602 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/go-previous-view.svgzbin0 -> 6545 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-fit-best.svgzbin0 -> 6121 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-fit-width.svgzbin0 -> 6109 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-in.svgzbin0 -> 6033 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-original.svgzbin0 -> 5541 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-out.svgzbin0 -> 5420 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/images/zoom-previous.svgzbin0 -> 5665 bytes
-rw-r--r--examples/pdfwidgets/pdfviewer/main.py27
-rw-r--r--examples/pdfwidgets/pdfviewer/mainwindow.py141
-rw-r--r--examples/pdfwidgets/pdfviewer/mainwindow.ui327
-rw-r--r--examples/pdfwidgets/pdfviewer/pdfviewer.pyproject4
-rw-r--r--examples/pdfwidgets/pdfviewer/resources.qrc11
-rw-r--r--examples/pdfwidgets/pdfviewer/resources_rc.py3468
-rw-r--r--examples/pdfwidgets/pdfviewer/ui_mainwindow.py259
-rw-r--r--examples/pdfwidgets/pdfviewer/zoomselector.py59
-rw-r--r--examples/qml/editingmodel/MovingRectangle.qml (renamed from examples/declarative/editingmodel/MovingRectangle.qml)0
-rw-r--r--examples/qml/editingmodel/doc/editingmodel.rst (renamed from examples/declarative/editingmodel/doc/editingmodel.rst)0
-rw-r--r--examples/qml/editingmodel/doc/qabstractlistmodelqml.png (renamed from examples/declarative/editingmodel/doc/qabstractlistmodelqml.png)bin45810 -> 45810 bytes
-rw-r--r--examples/qml/editingmodel/main.py (renamed from examples/declarative/editingmodel/main.py)0
-rw-r--r--examples/qml/editingmodel/main.pyproject (renamed from examples/declarative/editingmodel/main.pyproject)0
-rw-r--r--examples/qml/editingmodel/main.qml (renamed from examples/declarative/editingmodel/main.qml)0
-rw-r--r--examples/qml/editingmodel/model.py (renamed from examples/declarative/editingmodel/model.py)0
-rw-r--r--examples/qml/signals/pytoqml1/main.py (renamed from examples/declarative/signals/pytoqml1/main.py)0
-rw-r--r--examples/qml/signals/pytoqml1/pytoqml1.pyproject (renamed from examples/declarative/signals/pytoqml1/pytoqml1.pyproject)0
-rw-r--r--examples/qml/signals/pytoqml1/view.qml (renamed from examples/declarative/signals/pytoqml1/view.qml)0
-rw-r--r--examples/qml/signals/pytoqml2/main.py (renamed from examples/declarative/signals/pytoqml2/main.py)0
-rw-r--r--examples/qml/signals/pytoqml2/pytoqml2.pyproject (renamed from examples/declarative/signals/pytoqml2/pytoqml2.pyproject)0
-rw-r--r--examples/qml/signals/pytoqml2/view.qml (renamed from examples/declarative/signals/pytoqml2/view.qml)0
-rw-r--r--examples/qml/signals/qmltopy1/main.py (renamed from examples/declarative/signals/qmltopy1/main.py)0
-rw-r--r--examples/qml/signals/qmltopy1/qmltopy1.pyproject (renamed from examples/declarative/signals/qmltopy1/qmltopy1.pyproject)0
-rw-r--r--examples/qml/signals/qmltopy1/view.qml (renamed from examples/declarative/signals/qmltopy1/view.qml)0
-rw-r--r--examples/qml/signals/qmltopy2/main.py (renamed from examples/declarative/signals/qmltopy2/main.py)0
-rw-r--r--examples/qml/signals/qmltopy2/qmltopy2.pyproject (renamed from examples/declarative/signals/qmltopy2/qmltopy2.pyproject)0
-rw-r--r--examples/qml/signals/qmltopy2/view.qml (renamed from examples/declarative/signals/qmltopy2/view.qml)0
-rw-r--r--examples/qml/signals/qmltopy3/main.py (renamed from examples/declarative/signals/qmltopy3/main.py)0
-rw-r--r--examples/qml/signals/qmltopy3/qmltopy3.pyproject (renamed from examples/declarative/signals/qmltopy3/qmltopy3.pyproject)0
-rw-r--r--examples/qml/signals/qmltopy3/view.qml (renamed from examples/declarative/signals/qmltopy3/view.qml)0
-rw-r--r--examples/qml/signals/qmltopy4/main.py (renamed from examples/declarative/signals/qmltopy4/main.py)0
-rw-r--r--examples/qml/signals/qmltopy4/qmltopy4.pyproject (renamed from examples/declarative/signals/qmltopy4/qmltopy4.pyproject)0
-rw-r--r--examples/qml/signals/qmltopy4/view.qml (renamed from examples/declarative/signals/qmltopy4/view.qml)0
-rw-r--r--examples/qml/textproperties/doc/textproperties.png (renamed from examples/declarative/textproperties/doc/textproperties.png)bin14082 -> 14082 bytes
-rw-r--r--examples/qml/textproperties/doc/textproperties.rst (renamed from examples/declarative/textproperties/doc/textproperties.rst)0
-rw-r--r--examples/qml/textproperties/main.py (renamed from examples/declarative/textproperties/main.py)0
-rw-r--r--examples/qml/textproperties/textproperties.pyproject (renamed from examples/declarative/textproperties/textproperties.pyproject)0
-rw-r--r--examples/qml/textproperties/view.qml (renamed from examples/declarative/textproperties/view.qml)0
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/People/Main.qml9
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/adding.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/doc/adding.rst67
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/main.py30
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/person.py35
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/Main.qml16
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/advanced1-Base-project.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.py46
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/doc/advanced1-Base-project.rst57
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.py32
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.py41
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/Main.qml16
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/advanced2-Inheritance-and-coercion.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.py46
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/doc/advanced2-Inheritance-and-coercion.rst62
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.py37
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.py52
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/Main.qml15
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/advanced3-Default-properties.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.py47
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/doc/advanced3-Default-properties.rst40
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.py36
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.py51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/Main.qml33
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/advanced4-Grouped-properties.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.py47
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/doc/advanced4-Grouped-properties.rst39
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.py44
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.py102
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/Main.qml22
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/advanced5-Attached-properties.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.py71
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/doc/advanced5-Attached-properties.rst51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.py46
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.py51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/Main.qml27
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/advanced6-Property-value-source.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.py89
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/doc/advanced6-Property-value-source.rst43
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.py49
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.py51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.py51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/People/Main.qml29
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/binding.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/birthdayparty.py83
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/doc/binding.rst (renamed from examples/declarative/referenceexamples/binding/doc/binding.rst)0
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/happybirthdaysong.py47
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/main.py52
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/person.py53
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extended/doc/extended.rst (renamed from examples/declarative/referenceexamples/extended/doc/extended.rst)0
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extended/example.qml (renamed from examples/declarative/referenceexamples/extended/example.qml)0
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extended/extended.pyproject (renamed from examples/declarative/referenceexamples/extended/extended.pyproject)0
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extended/main.py (renamed from examples/declarative/referenceexamples/extended/main.py)0
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/People/Main.qml19
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/birthdayparty.py47
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/doc/methods.rst (renamed from examples/declarative/referenceexamples/methods/doc/methods.rst)0
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/main.py32
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/methods.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/person.py34
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/People/Main.qml16
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/birthdayparty.py41
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/doc/properties.rst89
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/main.py34
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/person.py35
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/properties.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/app.qml26
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/basics.py68
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.pyproject (renamed from examples/declarative/extending/chapter1-basics/chapter1-basics.pyproject)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/doc/chapter1-basics.rst107
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/app.qml32
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.pyproject (renamed from examples/declarative/extending/chapter2-methods/chapter2-methods.pyproject)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/doc/chapter2-methods.rst36
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/methods.py75
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml40
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/bindings.py79
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.pyproject (renamed from examples/declarative/extending/chapter3-bindings/chapter3-bindings.pyproject)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/doc/chapter3-bindings.rst54
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml22
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.pyproject (renamed from examples/declarative/extending/chapter4-customPropertyTypes/chapter4-customPropertyTypes.pyproject)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/customPropertyTypes.py82
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/doc/chapter4-customPropertyTypes.rst73
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml32
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.pyproject (renamed from examples/declarative/extending/chapter5-listproperties/chapter5-listproperties.pyproject)0
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/doc/chapter5-listproperties.rst46
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/listproperties.py96
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/piechart.py40
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/pieslice.py52
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/plugins.pngbin0 -> 5014 bytes
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml32
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/chapter6-plugins.pyproject3
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst26
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/doc/plugins.pngbin0 -> 5014 bytes
-rw-r--r--examples/qml/usingmodel/doc/usingmodel.png (renamed from examples/declarative/usingmodel/doc/usingmodel.png)bin2791 -> 2791 bytes
-rw-r--r--examples/qml/usingmodel/doc/usingmodel.rst (renamed from examples/declarative/usingmodel/doc/usingmodel.rst)0
-rw-r--r--examples/qml/usingmodel/usingmodel.py (renamed from examples/declarative/usingmodel/usingmodel.py)0
-rw-r--r--examples/qml/usingmodel/usingmodel.pyproject (renamed from examples/declarative/usingmodel/usingmodel.pyproject)0
-rw-r--r--examples/qml/usingmodel/view.qml (renamed from examples/declarative/usingmodel/view.qml)0
-rw-r--r--examples/quick/models/objectlistmodel/doc/objectlistmodel.png (renamed from examples/declarative/objectlistmodel/doc/objectlistmodel.png)bin1327 -> 1327 bytes
-rw-r--r--examples/quick/models/objectlistmodel/doc/objectlistmodel.rst (renamed from examples/declarative/objectlistmodel/doc/objectlistmodel.rst)0
-rw-r--r--examples/quick/models/objectlistmodel/objectlistmodel.py (renamed from examples/declarative/objectlistmodel/objectlistmodel.py)0
-rw-r--r--examples/quick/models/objectlistmodel/objectlistmodel.pyproject (renamed from examples/declarative/objectlistmodel/objectlistmodel.pyproject)0
-rw-r--r--examples/quick/models/objectlistmodel/view.qml (renamed from examples/declarative/objectlistmodel/view.qml)0
-rw-r--r--examples/quick/models/stringlistmodel/doc/stringlistmodel.png (renamed from examples/declarative/stringlistmodel/doc/stringlistmodel.png)bin1978 -> 1978 bytes
-rw-r--r--examples/quick/models/stringlistmodel/doc/stringlistmodel.rst (renamed from examples/declarative/stringlistmodel/doc/stringlistmodel.rst)0
-rw-r--r--examples/quick/models/stringlistmodel/stringlistmodel.py (renamed from examples/declarative/stringlistmodel/stringlistmodel.py)0
-rw-r--r--examples/quick/models/stringlistmodel/stringlistmodel.pyproject (renamed from examples/declarative/stringlistmodel/stringlistmodel.pyproject)0
-rw-r--r--examples/quick/models/stringlistmodel/view.qml (renamed from examples/declarative/stringlistmodel/view.qml)0
-rw-r--r--examples/quick/painteditem/painteditem.py8
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.py185
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/demo.qml161
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/doc/rendercontrol_opengl.rst (renamed from examples/declarative/rendercontrol/rendercontrol_opengl/doc/rendercontrol_opengl.rst)0
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/main.py (renamed from examples/declarative/rendercontrol/rendercontrol_opengl/main.py)0
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pyproject (renamed from examples/declarative/rendercontrol/rendercontrol_opengl/rendercontrol_opengl.pyproject)0
-rw-r--r--examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.py272
-rw-r--r--examples/quick/scenegraph/openglunderqml/doc/openglunderqml.rst (renamed from examples/declarative/openglunderqml/doc/openglunderqml.rst)0
-rw-r--r--examples/quick/scenegraph/openglunderqml/doc/squircle.png (renamed from examples/declarative/openglunderqml/doc/squircle.png)bin37963 -> 37963 bytes
-rw-r--r--examples/quick/scenegraph/openglunderqml/main.py (renamed from examples/declarative/openglunderqml/main.py)0
-rw-r--r--examples/quick/scenegraph/openglunderqml/main.qml (renamed from examples/declarative/openglunderqml/main.qml)0
-rw-r--r--examples/quick/scenegraph/openglunderqml/openglunderqml.pyproject (renamed from examples/declarative/openglunderqml/openglunderqml.pyproject)0
-rw-r--r--examples/quick/scenegraph/openglunderqml/squircle.py (renamed from examples/declarative/openglunderqml/squircle.py)0
-rw-r--r--examples/quick/scenegraph/openglunderqml/squirclerenderer.py98
-rw-r--r--examples/quick/scenegraph/scenegraph_customgeometry/doc/scenegraph_customgeometry.rst7
-rw-r--r--examples/quick/scenegraph/scenegraph_customgeometry/main.py152
-rw-r--r--examples/quick/scenegraph/scenegraph_customgeometry/main.qml34
-rw-r--r--examples/quick/scenegraph/scenegraph_customgeometry/scenegraph_customgeometry.pyproject3
-rw-r--r--examples/quick/shared/Button.qml50
-rw-r--r--examples/quick/shared/CheckBox.qml59
-rw-r--r--examples/quick/shared/FlickrRssModel.qml45
-rw-r--r--examples/quick/shared/Images.qml14
-rw-r--r--examples/quick/shared/Label.qml9
-rw-r--r--examples/quick/shared/LauncherList.qml205
-rw-r--r--examples/quick/shared/README11
-rw-r--r--examples/quick/shared/SimpleLauncherDelegate.qml96
-rw-r--r--examples/quick/shared/Slider.qml82
-rw-r--r--examples/quick/shared/TabSet.qml69
-rw-r--r--examples/quick/shared/TextField.qml43
-rw-r--r--examples/quick/shared/images/back.pngbin0 -> 1590 bytes
-rw-r--r--examples/quick/shared/images/checkmark.pngbin0 -> 809 bytes
-rw-r--r--examples/quick/shared/images/next.pngbin0 -> 1371 bytes
-rw-r--r--examples/quick/shared/images/qt-logo.pngbin0 -> 13596 bytes
-rw-r--r--examples/quick/shared/images/slider_handle.pngbin0 -> 887 bytes
-rw-r--r--examples/quick/shared/images/tab.pngbin0 -> 309 bytes
-rw-r--r--examples/quick/shared/qmldir11
-rw-r--r--examples/quick/shared/quick_shared.qrc15
-rw-r--r--examples/quick/shared/shared.qrc21
-rw-r--r--examples/quick/shared/shared_rc.py2253
-rw-r--r--examples/quick/window/AllScreens.qml42
-rw-r--r--examples/quick/window/CurrentScreen.qml83
-rw-r--r--examples/quick/window/Splash.qml42
-rw-r--r--examples/quick/window/doc/window.pngbin0 -> 9863 bytes
-rw-r--r--examples/quick/window/doc/window.rst35
-rw-r--r--examples/quick/window/main.py40
-rw-r--r--examples/quick/window/rc_window.py335
-rw-r--r--examples/quick/window/resources/icon.icnsbin0 -> 59662 bytes
-rw-r--r--examples/quick/window/resources/icon.icobin0 -> 11825 bytes
-rw-r--r--examples/quick/window/resources/icon.svg208
-rw-r--r--examples/quick/window/resources/icon64.pngbin0 -> 3004 bytes
-rw-r--r--examples/quick/window/window.pyproject3
-rw-r--r--examples/quick/window/window.qml151
-rw-r--r--examples/quick/window/window.qrc8
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/Main.qml188
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/app.qrc15
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons.qrc13
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/folder_closed.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/folder_open.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/generic_file.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/globe.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/info_sign.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/light_bulb.svg43
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/qt_logo.svg26
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/read.svg38
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/icons/resize.svg6
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/About.qml59
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Colors.qml22
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/FileSystemView.qml99
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Icon.qml44
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenu.qml42
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/MyMenuBar.qml135
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/ResizeButton.qml23
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/Sidebar.qml117
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qml/WindowDragHandler.qml16
-rw-r--r--examples/quickcontrols/filesystemexplorer/FileSystemModule/qmldir11
-rw-r--r--examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.rst39
-rw-r--r--examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.webpbin0 -> 83086 bytes
-rw-r--r--examples/quickcontrols/filesystemexplorer/filesystemexplorer.py69
-rw-r--r--examples/quickcontrols/filesystemexplorer/filesystemexplorer.pyproject27
-rw-r--r--examples/quickcontrols/gallery/+Material/ToolBar.qml (renamed from examples/quickcontrols2/gallery/+Material/ToolBar.qml)0
-rw-r--r--examples/quickcontrols/gallery/ToolBar.qml (renamed from examples/quickcontrols2/gallery/ToolBar.qml)0
-rw-r--r--examples/quickcontrols/gallery/doc/gallery.png (renamed from examples/quickcontrols2/gallery/doc/gallery.png)bin23828 -> 23828 bytes
-rw-r--r--examples/quickcontrols/gallery/doc/gallery.rst (renamed from examples/quickcontrols2/gallery/doc/gallery.rst)0
-rw-r--r--examples/quickcontrols/gallery/doc/images/qtquickcontrols2-gallery-drawer.png (renamed from examples/quickcontrols2/gallery/doc/images/qtquickcontrols2-gallery-drawer.png)bin15428 -> 15428 bytes
-rw-r--r--examples/quickcontrols/gallery/doc/images/qtquickcontrols2-gallery-menu.png (renamed from examples/quickcontrols2/gallery/doc/images/qtquickcontrols2-gallery-menu.png)bin15953 -> 15953 bytes
-rw-r--r--examples/quickcontrols/gallery/doc/images/qtquickcontrols2-gallery-welcome.png (renamed from examples/quickcontrols2/gallery/doc/images/qtquickcontrols2-gallery-welcome.png)bin18513 -> 18513 bytes
-rw-r--r--examples/quickcontrols/gallery/gallery.py (renamed from examples/quickcontrols2/gallery/gallery.py)0
-rw-r--r--examples/quickcontrols/gallery/gallery.pyproject36
-rw-r--r--examples/quickcontrols/gallery/gallery.qml318
-rw-r--r--examples/quickcontrols/gallery/gallery.qrc (renamed from examples/quickcontrols2/gallery/gallery.qrc)0
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20/back.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20/back.png)bin146 -> 146 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20/drawer.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20/drawer.png)bin123 -> 123 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20/menu.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20/menu.png)bin123 -> 123 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@2/back.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@2/back.png)bin184 -> 184 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@2/drawer.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@2/drawer.png)bin126 -> 126 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@2/menu.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@2/menu.png)bin158 -> 158 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@3/back.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@3/back.png)bin227 -> 227 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@3/drawer.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@3/drawer.png)bin130 -> 130 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@3/menu.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@3/menu.png)bin193 -> 193 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@4/back.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@4/back.png)bin246 -> 246 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@4/drawer.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@4/drawer.png)bin131 -> 131 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/20x20@4/menu.png (renamed from examples/quickcontrols2/gallery/icons/gallery/20x20@4/menu.png)bin223 -> 223 bytes
-rw-r--r--examples/quickcontrols/gallery/icons/gallery/index.theme (renamed from examples/quickcontrols2/gallery/icons/gallery/index.theme)0
-rw-r--r--examples/quickcontrols/gallery/images/arrow.png (renamed from examples/quickcontrols2/gallery/images/arrow.png)bin319 -> 319 bytes
-rw-r--r--examples/quickcontrols/gallery/images/arrow@2x.png (renamed from examples/quickcontrols2/gallery/images/arrow@2x.png)bin476 -> 476 bytes
-rw-r--r--examples/quickcontrols/gallery/images/arrow@3x.png (renamed from examples/quickcontrols2/gallery/images/arrow@3x.png)bin611 -> 611 bytes
-rw-r--r--examples/quickcontrols/gallery/images/arrow@4x.png (renamed from examples/quickcontrols2/gallery/images/arrow@4x.png)bin758 -> 758 bytes
-rw-r--r--examples/quickcontrols/gallery/images/arrows.png (renamed from examples/quickcontrols2/gallery/images/arrows.png)bin458 -> 458 bytes
-rw-r--r--examples/quickcontrols/gallery/images/arrows@2x.png (renamed from examples/quickcontrols2/gallery/images/arrows@2x.png)bin699 -> 699 bytes
-rw-r--r--examples/quickcontrols/gallery/images/arrows@3x.png (renamed from examples/quickcontrols2/gallery/images/arrows@3x.png)bin942 -> 942 bytes
-rw-r--r--examples/quickcontrols/gallery/images/arrows@4x.png (renamed from examples/quickcontrols2/gallery/images/arrows@4x.png)bin1211 -> 1211 bytes
-rw-r--r--examples/quickcontrols/gallery/images/qt-logo.pngbin0 -> 2849 bytes
-rw-r--r--examples/quickcontrols/gallery/images/qt-logo@2x.pngbin0 -> 4936 bytes
-rw-r--r--examples/quickcontrols/gallery/images/qt-logo@3x.pngbin0 -> 7254 bytes
-rw-r--r--examples/quickcontrols/gallery/images/qt-logo@4x.pngbin0 -> 9505 bytes
-rw-r--r--examples/quickcontrols/gallery/pages/BusyIndicatorPage.qml28
-rw-r--r--examples/quickcontrols/gallery/pages/ButtonPage.qml (renamed from examples/quickcontrols2/gallery/pages/ButtonPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/CheckBoxPage.qml (renamed from examples/quickcontrols2/gallery/pages/CheckBoxPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/ComboBoxPage.qml51
-rw-r--r--examples/quickcontrols/gallery/pages/DelayButtonPage.qml (renamed from examples/quickcontrols2/gallery/pages/DelayButtonPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/DelegatePage.qml201
-rw-r--r--examples/quickcontrols/gallery/pages/DialPage.qml (renamed from examples/quickcontrols2/gallery/pages/DialPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/DialogPage.qml179
-rw-r--r--examples/quickcontrols/gallery/pages/FramePage.qml (renamed from examples/quickcontrols2/gallery/pages/FramePage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/GroupBoxPage.qml (renamed from examples/quickcontrols2/gallery/pages/GroupBoxPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/PageIndicatorPage.qml (renamed from examples/quickcontrols2/gallery/pages/PageIndicatorPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/ProgressBarPage.qml (renamed from examples/quickcontrols2/gallery/pages/ProgressBarPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/RadioButtonPage.qml (renamed from examples/quickcontrols2/gallery/pages/RadioButtonPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/RangeSliderPage.qml (renamed from examples/quickcontrols2/gallery/pages/RangeSliderPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/ScrollBarPage.qml (renamed from examples/quickcontrols2/gallery/pages/ScrollBarPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/ScrollIndicatorPage.qml (renamed from examples/quickcontrols2/gallery/pages/ScrollIndicatorPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/ScrollablePage.qml (renamed from examples/quickcontrols2/gallery/pages/ScrollablePage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/SliderPage.qml (renamed from examples/quickcontrols2/gallery/pages/SliderPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/SpinBoxPage.qml (renamed from examples/quickcontrols2/gallery/pages/SpinBoxPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/StackViewPage.qml58
-rw-r--r--examples/quickcontrols/gallery/pages/SwipeViewPage.qml49
-rw-r--r--examples/quickcontrols/gallery/pages/SwitchPage.qml (renamed from examples/quickcontrols2/gallery/pages/SwitchPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/TabBarPage.qml57
-rw-r--r--examples/quickcontrols/gallery/pages/TextAreaPage.qml29
-rw-r--r--examples/quickcontrols/gallery/pages/TextFieldPage.qml (renamed from examples/quickcontrols2/gallery/pages/TextFieldPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/ToolTipPage.qml (renamed from examples/quickcontrols2/gallery/pages/ToolTipPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/pages/TumblerPage.qml (renamed from examples/quickcontrols2/gallery/pages/TumblerPage.qml)0
-rw-r--r--examples/quickcontrols/gallery/qmldir (renamed from examples/quickcontrols2/gallery/qmldir)0
-rw-r--r--examples/quickcontrols/gallery/qtquickcontrols2.conf (renamed from examples/quickcontrols2/gallery/qtquickcontrols2.conf)0
-rw-r--r--examples/quickcontrols/gallery/rc_gallery.py4410
-rw-r--r--examples/quickcontrols2/gallery/gallery.pyproject37
-rw-r--r--examples/quickcontrols2/gallery/gallery.qml295
-rw-r--r--examples/quickcontrols2/gallery/images/qt-logo.pngbin1301 -> 0 bytes
-rw-r--r--examples/quickcontrols2/gallery/images/qt-logo@2x.pngbin2611 -> 0 bytes
-rw-r--r--examples/quickcontrols2/gallery/images/qt-logo@3x.pngbin4155 -> 0 bytes
-rw-r--r--examples/quickcontrols2/gallery/images/qt-logo@4x.pngbin5916 -> 0 bytes
-rw-r--r--examples/quickcontrols2/gallery/pages/BusyIndicatorPage.qml26
-rw-r--r--examples/quickcontrols2/gallery/pages/ComboBoxPage.qml50
-rw-r--r--examples/quickcontrols2/gallery/pages/DelegatePage.qml170
-rw-r--r--examples/quickcontrols2/gallery/pages/DialogPage.qml179
-rw-r--r--examples/quickcontrols2/gallery/pages/StackViewPage.qml49
-rw-r--r--examples/quickcontrols2/gallery/pages/SwipeViewPage.qml49
-rw-r--r--examples/quickcontrols2/gallery/pages/TabBarPage.qml57
-rw-r--r--examples/quickcontrols2/gallery/pages/TextAreaPage.qml29
-rw-r--r--examples/quickcontrols2/gallery/rc_gallery.py7855
-rw-r--r--examples/samplebinding/CMakeLists.txt31
-rw-r--r--examples/samplebinding/doc/bindings.h.rstinc2
-rw-r--r--examples/samplebinding/doc/bindings.xml.rstinc31
-rw-r--r--examples/samplebinding/doc/samplebinding.pyproject1
-rw-r--r--examples/samplebinding/doc/samplebinding.rst17
-rw-r--r--examples/scriptableapplication/CMakeLists.txt29
-rw-r--r--examples/scriptableapplication/mainwindow.cpp60
-rw-r--r--examples/scriptableapplication/mainwindow.h2
-rw-r--r--examples/scriptableapplication/pythonutils.cpp10
-rw-r--r--examples/scriptableapplication/pythonutils.h2
-rw-r--r--examples/serialbus/can/bitratebox.py55
-rw-r--r--examples/serialbus/can/can.pyproject10
-rw-r--r--examples/serialbus/can/can.qrc8
-rw-r--r--examples/serialbus/can/canbusdeviceinfobox.py49
-rw-r--r--examples/serialbus/can/canbusdeviceinfobox.ui86
-rw-r--r--examples/serialbus/can/canbusdeviceinfodialog.py16
-rw-r--r--examples/serialbus/can/canbusdeviceinfodialog.ui66
-rw-r--r--examples/serialbus/can/connectdialog.py235
-rw-r--r--examples/serialbus/can/connectdialog.ui278
-rw-r--r--examples/serialbus/can/doc/can.rst6
-rw-r--r--examples/serialbus/can/images/application-exit.pngbin0 -> 11200 bytes
-rw-r--r--examples/serialbus/can/images/clear.pngbin0 -> 12543 bytes
-rw-r--r--examples/serialbus/can/images/connect.pngbin0 -> 15374 bytes
-rw-r--r--examples/serialbus/can/images/disconnect.pngbin0 -> 15092 bytes
-rw-r--r--examples/serialbus/can/main.py19
-rw-r--r--examples/serialbus/can/mainwindow.py232
-rw-r--r--examples/serialbus/can/mainwindow.ui207
-rw-r--r--examples/serialbus/can/rc_can.py3453
-rw-r--r--examples/serialbus/can/receivedframesmodel.py142
-rw-r--r--examples/serialbus/can/receivedframesview.py54
-rw-r--r--examples/serialbus/can/sendframebox.py195
-rw-r--r--examples/serialbus/can/sendframebox.ui211
-rw-r--r--examples/serialbus/can/ui_canbusdeviceinfobox.py88
-rw-r--r--examples/serialbus/can/ui_canbusdeviceinfodialog.py64
-rw-r--r--examples/serialbus/can/ui_connectdialog.py252
-rw-r--r--examples/serialbus/can/ui_mainwindow.py172
-rw-r--r--examples/serialbus/can/ui_sendframebox.py188
-rw-r--r--examples/serialbus/modbus/modbusclient/doc/modbusclient.rst9
-rw-r--r--examples/serialbus/modbus/modbusclient/images/application-exit.pngbin0 -> 11200 bytes
-rw-r--r--examples/serialbus/modbus/modbusclient/images/connect.pngbin0 -> 15374 bytes
-rw-r--r--examples/serialbus/modbus/modbusclient/images/disconnect.pngbin0 -> 15092 bytes
-rw-r--r--examples/serialbus/modbus/modbusclient/images/settings.pngbin0 -> 16039 bytes
-rw-r--r--examples/serialbus/modbus/modbusclient/main.py26
-rw-r--r--examples/serialbus/modbus/modbusclient/mainwindow.py331
-rw-r--r--examples/serialbus/modbus/modbusclient/mainwindow.ui520
-rw-r--r--examples/serialbus/modbus/modbusclient/modbusclient.pyproject5
-rw-r--r--examples/serialbus/modbus/modbusclient/modbusclient.qrc8
-rw-r--r--examples/serialbus/modbus/modbusclient/rc_modbusclient.py3672
-rw-r--r--examples/serialbus/modbus/modbusclient/settingsdialog.py52
-rw-r--r--examples/serialbus/modbus/modbusclient/settingsdialog.ui237
-rw-r--r--examples/serialbus/modbus/modbusclient/ui_mainwindow.py377
-rw-r--r--examples/serialbus/modbus/modbusclient/ui_settingsdialog.py173
-rw-r--r--examples/serialbus/modbus/modbusclient/writeregistermodel.py118
-rw-r--r--examples/serialport/terminal/console.py49
-rw-r--r--examples/serialport/terminal/doc/terminal.rst9
-rw-r--r--examples/serialport/terminal/images/application-exit.pngbin0 -> 11200 bytes
-rw-r--r--examples/serialport/terminal/images/clear.pngbin0 -> 12543 bytes
-rw-r--r--examples/serialport/terminal/images/connect.pngbin0 -> 15374 bytes
-rw-r--r--examples/serialport/terminal/images/disconnect.pngbin0 -> 15092 bytes
-rw-r--r--examples/serialport/terminal/images/settings.pngbin0 -> 16039 bytes
-rw-r--r--examples/serialport/terminal/main.py17
-rw-r--r--examples/serialport/terminal/mainwindow.py108
-rw-r--r--examples/serialport/terminal/mainwindow.ui162
-rw-r--r--examples/serialport/terminal/rc_terminal.py4464
-rw-r--r--examples/serialport/terminal/settingsdialog.py179
-rw-r--r--examples/serialport/terminal/settingsdialog.ui177
-rw-r--r--examples/serialport/terminal/terminal.pyproject4
-rw-r--r--examples/serialport/terminal/terminal.qrc9
-rw-r--r--examples/serialport/terminal/ui_mainwindow.py149
-rw-r--r--examples/serialport/terminal/ui_settingsdialog.py181
-rw-r--r--examples/speech/hello_speak/doc/hello_speak.rst16
-rw-r--r--examples/speech/hello_speak/doc/hello_speak.webpbin0 -> 25432 bytes
-rw-r--r--examples/speech/hello_speak/hello_speak.pyproject5
-rw-r--r--examples/speech/hello_speak/main.py20
-rw-r--r--examples/speech/hello_speak/mainwindow.py134
-rw-r--r--examples/speech/hello_speak/mainwindow.ui267
-rw-r--r--examples/speech/hello_speak/ui_mainwindow.py211
-rw-r--r--examples/sql/books/bookdelegate.py2
-rw-r--r--examples/sql/books/bookwindow.py2
-rw-r--r--examples/statemachine/moveblocks/doc/moveblocks.pngbin0 -> 4532 bytes
-rw-r--r--examples/statemachine/moveblocks/doc/moveblocks.rst10
-rw-r--r--examples/statemachine/moveblocks/moveblocks.py222
-rw-r--r--examples/statemachine/moveblocks/moveblocks.pyproject3
-rw-r--r--examples/statemachine/ping_pong/ping_pong.py (renamed from examples/widgets/state-machine/ping_pong/ping_pong.py)0
-rw-r--r--examples/statemachine/ping_pong/ping_pong.pyproject (renamed from examples/widgets/state-machine/ping_pong/ping_pong.pyproject)0
-rw-r--r--examples/statemachine/rogue/rogue.py (renamed from examples/widgets/state-machine/rogue/rogue.py)0
-rw-r--r--examples/statemachine/rogue/rogue.pyproject (renamed from examples/widgets/state-machine/rogue/rogue.pyproject)0
-rw-r--r--examples/statemachine/trafficlight/doc/trafficlight.pngbin0 -> 4786 bytes
-rw-r--r--examples/statemachine/trafficlight/doc/trafficlight.rst (renamed from examples/widgets/state-machine/trafficlight/doc/trafficlight.rst)0
-rw-r--r--examples/statemachine/trafficlight/trafficlight.py (renamed from examples/widgets/state-machine/trafficlight/trafficlight.py)0
-rw-r--r--examples/statemachine/trafficlight/trafficlight.pyproject (renamed from examples/widgets/state-machine/trafficlight/trafficlight.pyproject)0
-rw-r--r--examples/texttospeech/hello_speak/hello_speak.py71
-rw-r--r--examples/texttospeech/hello_speak/hello_speak.pyproject3
-rw-r--r--examples/uitools/uiloader/uiloader.py4
-rw-r--r--examples/utils/pyside_config.py2
-rw-r--r--examples/webenginequick/nanobrowser/ApplicationRoot.qml40
-rw-r--r--examples/webenginequick/nanobrowser/BrowserDialog.qml27
-rw-r--r--examples/webenginequick/nanobrowser/BrowserWindow.qml817
-rw-r--r--examples/webenginequick/nanobrowser/DownloadView.qml127
-rw-r--r--examples/webenginequick/nanobrowser/FindBar.qml95
-rw-r--r--examples/webenginequick/nanobrowser/FullScreenNotification.qml62
-rw-r--r--examples/webenginequick/nanobrowser/browser.qml16
-rw-r--r--examples/webenginequick/nanobrowser/icons/3rdparty/COPYING1
-rw-r--r--examples/webenginequick/nanobrowser/icons/3rdparty/go-next.pngbin0 -> 930 bytes
-rw-r--r--examples/webenginequick/nanobrowser/icons/3rdparty/go-previous.pngbin0 -> 955 bytes
-rw-r--r--examples/webenginequick/nanobrowser/icons/3rdparty/process-stop.pngbin0 -> 1272 bytes
-rw-r--r--examples/webenginequick/nanobrowser/icons/3rdparty/view-refresh.pngbin0 -> 1364 bytes
-rw-r--r--examples/webenginequick/nanobrowser/nanobrowser.pyproject5
-rw-r--r--examples/webenginequick/nanobrowser/quicknanobrowser.py63
-rw-r--r--examples/webenginequick/nanobrowser/rc_resources.py348
-rw-r--r--examples/webenginequick/nanobrowser/resources.qrc8
-rw-r--r--examples/webenginewidgets/markdowneditor/mainwindow.py9
-rw-r--r--examples/webenginewidgets/markdowneditor/markdowneditor.pyproject3
-rw-r--r--examples/webenginewidgets/markdowneditor/resources/index.html2
-rw-r--r--examples/webenginewidgets/simplebrowser/browser.py68
-rw-r--r--examples/webenginewidgets/simplebrowser/browserwindow.py493
-rw-r--r--examples/webenginewidgets/simplebrowser/certificateerrordialog.ui133
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/COPYING1
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/dialog-error.pngbin0 -> 1645 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/edit-clear.pngbin0 -> 1682 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/go-bottom.pngbin0 -> 1263 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/go-next.pngbin0 -> 1219 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/go-previous.pngbin0 -> 1200 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/process-stop.pngbin0 -> 1927 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/qt_attribution.json24
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/text-html.pngbin0 -> 1759 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/3rdparty/view-refresh.pngbin0 -> 2024 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/AppLogoColor.pngbin0 -> 6113 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/ninja.pngbin0 -> 1671 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/data/rc_simplebrowser.py1391
-rw-r--r--examples/webenginewidgets/simplebrowser/data/simplebrowser.qrc16
-rw-r--r--examples/webenginewidgets/simplebrowser/doc/simplebrowser.rst179
-rw-r--r--examples/webenginewidgets/simplebrowser/doc/simplebrowser.webpbin0 -> 67542 bytes
-rw-r--r--examples/webenginewidgets/simplebrowser/downloadmanagerwidget.py51
-rw-r--r--examples/webenginewidgets/simplebrowser/downloadmanagerwidget.ui104
-rw-r--r--examples/webenginewidgets/simplebrowser/downloadwidget.py108
-rw-r--r--examples/webenginewidgets/simplebrowser/downloadwidget.ui78
-rw-r--r--examples/webenginewidgets/simplebrowser/main.py40
-rw-r--r--examples/webenginewidgets/simplebrowser/passworddialog.ui121
-rw-r--r--examples/webenginewidgets/simplebrowser/simplebrowser.py66
-rw-r--r--examples/webenginewidgets/simplebrowser/simplebrowser.pyproject6
-rw-r--r--examples/webenginewidgets/simplebrowser/tabwidget.py241
-rw-r--r--examples/webenginewidgets/simplebrowser/ui_certificateerrordialog.py87
-rw-r--r--examples/webenginewidgets/simplebrowser/ui_downloadmanagerwidget.py76
-rw-r--r--examples/webenginewidgets/simplebrowser/ui_downloadwidget.py86
-rw-r--r--examples/webenginewidgets/simplebrowser/ui_passworddialog.py96
-rw-r--r--examples/webenginewidgets/simplebrowser/webpage.py29
-rw-r--r--examples/webenginewidgets/simplebrowser/webpopupwindow.py53
-rw-r--r--examples/webenginewidgets/simplebrowser/webview.py291
-rw-r--r--examples/webenginewidgets/tabbedbrowser/bookmarkwidget.py239
-rw-r--r--examples/webenginewidgets/tabbedbrowser/browsertabwidget.py207
-rw-r--r--examples/webenginewidgets/tabbedbrowser/doc/tabbedbrowser.pngbin37147 -> 0 bytes
-rw-r--r--examples/webenginewidgets/tabbedbrowser/doc/tabbedbrowser.rst58
-rw-r--r--examples/webenginewidgets/tabbedbrowser/downloadwidget.py111
-rw-r--r--examples/webenginewidgets/tabbedbrowser/findtoolbar.py62
-rw-r--r--examples/webenginewidgets/tabbedbrowser/historywindow.py66
-rw-r--r--examples/webenginewidgets/tabbedbrowser/main.py357
-rw-r--r--examples/webenginewidgets/tabbedbrowser/tabbedbrowser.pyproject5
-rw-r--r--examples/webenginewidgets/tabbedbrowser/webengineview.py55
-rw-r--r--examples/webenginewidgets/widgetsnanobrowser/doc/widgetsnanobrowser.png (renamed from examples/webenginewidgets/simplebrowser/doc/simplebrowser.png)bin51615 -> 51615 bytes
-rw-r--r--examples/webenginewidgets/widgetsnanobrowser/doc/widgetsnanobrowser.rst8
-rw-r--r--examples/webenginewidgets/widgetsnanobrowser/widgetsnanobrowser.py70
-rw-r--r--examples/webenginewidgets/widgetsnanobrowser/widgetsnanobrowser.pyproject3
-rw-r--r--examples/widgetbinding/CMakeLists.txt37
-rw-r--r--examples/widgetbinding/dialog.py3
-rw-r--r--examples/widgetbinding/doc/widgetbinding.md (renamed from examples/widgetbinding/README.md)0
-rw-r--r--examples/widgetbinding/doc/widgetbinding.pyproject10
-rw-r--r--examples/widgetbinding/wigglywidget.cpp12
-rw-r--r--examples/widgets/animation/animatedtiles/animatedtiles.py4
-rw-r--r--examples/widgets/animation/animatedtiles/animatedtiles.pyproject3
-rw-r--r--examples/widgets/animation/appchooser/appchooser.py2
-rw-r--r--examples/widgets/animation/appchooser/appchooser.pyproject2
-rw-r--r--examples/widgets/animation/easing/easing.py31
-rw-r--r--examples/widgets/animation/easing/form.ui105
-rw-r--r--examples/widgets/animation/easing/images/qt-logo.pngbin5149 -> 0 bytes
-rw-r--r--examples/widgets/animation/easing/ui_form.py61
-rw-r--r--examples/widgets/animation/states/states.py2
-rw-r--r--examples/widgets/animation/states/states.pyproject2
-rw-r--r--examples/widgets/codeeditor/codeeditor.py104
-rw-r--r--examples/widgets/codeeditor/codeeditor.pyproject3
-rw-r--r--examples/widgets/codeeditor/doc/codeeditor.pngbin7759 -> 0 bytes
-rw-r--r--examples/widgets/codeeditor/doc/codeeditor.rst9
-rw-r--r--examples/widgets/codeeditor/main.py15
-rw-r--r--examples/widgets/desktop/screenshot/screenshot.py6
-rw-r--r--examples/widgets/desktop/systray/images/bad.png (renamed from examples/widgets/systray/images/bad.png)bin2496 -> 2496 bytes
-rw-r--r--examples/widgets/desktop/systray/images/heart.png (renamed from examples/widgets/systray/images/heart.png)bin25780 -> 25780 bytes
-rw-r--r--examples/widgets/desktop/systray/images/trash.png (renamed from examples/widgets/systray/images/trash.png)bin12128 -> 12128 bytes
-rw-r--r--examples/widgets/desktop/systray/main.py (renamed from examples/widgets/systray/main.py)0
-rw-r--r--examples/widgets/desktop/systray/rc_systray.py (renamed from examples/widgets/systray/rc_systray.py)0
-rw-r--r--examples/widgets/desktop/systray/systray.pyproject (renamed from examples/widgets/systray/systray.pyproject)0
-rw-r--r--examples/widgets/desktop/systray/systray.qrc (renamed from examples/widgets/systray/systray.qrc)0
-rw-r--r--examples/widgets/desktop/systray/window.py (renamed from examples/widgets/systray/window.py)0
-rw-r--r--examples/widgets/dialogs/classwizard/classwizard.py16
-rw-r--r--examples/widgets/dialogs/classwizard/classwizard.pyproject3
-rw-r--r--examples/widgets/dialogs/classwizard/listchooser.py7
-rw-r--r--examples/widgets/dialogs/findfiles/findfiles.py184
-rw-r--r--examples/widgets/dialogs/findfiles/findfiles.pyproject3
-rw-r--r--examples/widgets/dialogs/standarddialogs/standarddialogs.py105
-rw-r--r--examples/widgets/dialogs/trivialwizard/trivialwizard.py2
-rw-r--r--examples/widgets/draganddrop/draggabletext/draggabletext.pyproject3
-rw-r--r--examples/widgets/draganddrop/dropsite/doc/dropsite.rst8
-rw-r--r--examples/widgets/draganddrop/dropsite/droparea.py67
-rw-r--r--examples/widgets/draganddrop/dropsite/dropsite.pyproject3
-rw-r--r--examples/widgets/draganddrop/dropsite/dropsitewindow.py115
-rw-r--r--examples/widgets/draganddrop/dropsite/main.py15
-rw-r--r--examples/widgets/effects/lighting/lighting.py2
-rw-r--r--examples/widgets/gettext/main.py4
-rw-r--r--examples/widgets/graphicsview/anchorlayout/anchorlayout.py2
-rw-r--r--examples/widgets/graphicsview/collidingmice/collidingmice.pyproject2
-rw-r--r--examples/widgets/graphicsview/diagramscene/diagramscene.py30
-rw-r--r--examples/widgets/graphicsview/diagramscene/diagramscene.pyproject2
-rw-r--r--examples/widgets/graphicsview/dragdroprobot/dragdroprobot.py2
-rw-r--r--examples/widgets/graphicsview/dragdroprobot/dragdroprobot.pyproject2
-rw-r--r--examples/widgets/graphicsview/elasticnodes/elasticnodes.py6
-rw-r--r--examples/widgets/imageviewer/imageviewer.py12
-rw-r--r--examples/widgets/itemviews/address_book/address_book.py3
-rw-r--r--examples/widgets/itemviews/address_book/addresswidget.py5
-rw-r--r--examples/widgets/itemviews/basicfiltermodel/basicsortfiltermodel.py5
-rw-r--r--examples/widgets/itemviews/dirview/dirview.py60
-rw-r--r--examples/widgets/itemviews/dirview/dirview.pyproject3
-rw-r--r--examples/widgets/itemviews/dirview/doc/dirview.rst5
-rw-r--r--examples/widgets/itemviews/editabletreemodel/treemodel.py9
-rw-r--r--examples/widgets/itemviews/fetchmore/fetchmore.py5
-rw-r--r--examples/widgets/itemviews/jsonmodel/jsonmodel.py7
-rw-r--r--examples/widgets/itemviews/spinboxdelegate/doc/spinboxdelegate.rst5
-rw-r--r--examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.py78
-rw-r--r--examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.pyproject3
-rw-r--r--examples/widgets/itemviews/spreadsheet/doc/spreadsheet.pngbin0 -> 40187 bytes
-rw-r--r--examples/widgets/itemviews/spreadsheet/doc/spreadsheet.rst10
-rw-r--r--examples/widgets/itemviews/spreadsheet/main.py19
-rw-r--r--examples/widgets/itemviews/spreadsheet/spreadsheet.py544
-rw-r--r--examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.py67
-rw-r--r--examples/widgets/itemviews/spreadsheet/spreadsheetitem.py122
-rw-r--r--examples/widgets/layouts/basiclayouts/basiclayouts.py3
-rw-r--r--examples/widgets/layouts/dynamiclayouts/dynamiclayouts.py2
-rw-r--r--examples/widgets/linguist/main.py7
-rw-r--r--examples/widgets/mainwindows/application/application.py10
-rw-r--r--examples/widgets/mainwindows/application/application.pyproject2
-rw-r--r--examples/widgets/mainwindows/dockwidgets/dockwidgets.py2
-rw-r--r--examples/widgets/mainwindows/dockwidgets/dockwidgets.pyproject2
-rw-r--r--examples/widgets/mainwindows/mdi/images/new.pngbin852 -> 0 bytes
-rw-r--r--examples/widgets/mainwindows/mdi/images/save.pngbin1187 -> 0 bytes
-rw-r--r--examples/widgets/mainwindows/mdi/mdi.py36
-rw-r--r--examples/widgets/mainwindows/mdi/mdi.pyproject2
-rw-r--r--examples/widgets/mainwindows/mdi/mdi.qrc10
-rw-r--r--examples/widgets/mainwindows/mdi/mdi_rc.py608
-rw-r--r--examples/widgets/painting/basicdrawing/basicdrawing.pyproject2
-rw-r--r--examples/widgets/painting/painter/painter.py15
-rw-r--r--examples/widgets/painting/plot/plot.py5
-rw-r--r--examples/widgets/richtext/orderform/orderform.py7
-rw-r--r--examples/widgets/richtext/textedit/main.py1
-rw-r--r--examples/widgets/richtext/textedit/textedit.py14
-rw-r--r--examples/widgets/richtext/textobject/textobject.py6
-rw-r--r--examples/widgets/state-machine/eventtrans/eventtrans.py57
-rw-r--r--examples/widgets/state-machine/eventtrans/eventtrans.pyproject3
-rw-r--r--examples/widgets/state-machine/factstates/factstates.py89
-rw-r--r--examples/widgets/state-machine/factstates/factstates.pyproject3
-rw-r--r--examples/widgets/state-machine/trafficlight/doc/trafficlight.pngbin79 -> 0 bytes
-rw-r--r--examples/widgets/state-machine/twowaybutton/twowaybutton.py33
-rw-r--r--examples/widgets/state-machine/twowaybutton/twowaybutton.pyproject3
-rw-r--r--examples/widgets/tetrix/tetrix.py469
-rw-r--r--examples/widgets/tools/regularexpression/regularexpression.py2
-rw-r--r--examples/widgets/tools/regularexpression/regularexpressiondialog.py11
-rw-r--r--examples/widgets/tutorials/addressbook/part1.py5
-rw-r--r--examples/widgets/tutorials/addressbook/part2.py6
-rw-r--r--examples/widgets/tutorials/addressbook/part3.py8
-rw-r--r--examples/widgets/tutorials/addressbook/part4.py11
-rw-r--r--examples/widgets/tutorials/addressbook/part5.py10
-rw-r--r--examples/widgets/tutorials/addressbook/part6.py9
-rw-r--r--examples/widgets/tutorials/addressbook/part7.py9
-rw-r--r--examples/widgets/tutorials/cannon/t10.py2
-rw-r--r--examples/widgets/tutorials/cannon/t11.py2
-rw-r--r--examples/widgets/tutorials/cannon/t12.py2
-rw-r--r--examples/widgets/tutorials/cannon/t13.py4
-rw-r--r--examples/widgets/tutorials/cannon/t14.py8
-rw-r--r--examples/widgets/tutorials/cannon/t2.py3
-rw-r--r--examples/widgets/tutorials/cannon/t8.py2
-rw-r--r--examples/widgets/tutorials/cannon/t9.py2
-rw-r--r--examples/widgets/tutorials/modelview/1_readonly.py38
-rw-r--r--examples/widgets/tutorials/modelview/2_formatting.py65
-rw-r--r--examples/widgets/tutorials/modelview/3_changingmodel.py52
-rw-r--r--examples/widgets/tutorials/modelview/4_headers.py43
-rw-r--r--examples/widgets/tutorials/modelview/5_edit.py73
-rw-r--r--examples/widgets/tutorials/modelview/6_treeview.py41
-rw-r--r--examples/widgets/tutorials/modelview/7_selections.py70
-rw-r--r--examples/widgets/tutorials/modelview/doc/modelview.rst4
-rw-r--r--examples/widgets/tutorials/modelview/modelview.pyproject9
-rw-r--r--examples/widgets/widgets/charactermap/charactermap.pyproject4
-rw-r--r--examples/widgets/widgets/charactermap/characterwidget.py133
-rw-r--r--examples/widgets/widgets/charactermap/doc/charactermap.rst8
-rw-r--r--examples/widgets/widgets/charactermap/fontinfodialog.py47
-rw-r--r--examples/widgets/widgets/charactermap/main.py17
-rw-r--r--examples/widgets/widgets/charactermap/mainwindow.py167
-rw-r--r--examples/widgets/widgets/digitalclock/digitalclock.py41
-rw-r--r--examples/widgets/widgets/digitalclock/digitalclock.pyproject3
-rw-r--r--examples/widgets/widgets/digitalclock/doc/digitalclock-screenshot.pngbin0 -> 726 bytes
-rw-r--r--examples/widgets/widgets/digitalclock/doc/digitalclock.rst12
-rw-r--r--examples/widgets/widgets/tetrix/doc/tetrix-screenshot.png (renamed from examples/widgets/tetrix/doc/tetrix-screenshot.png)bin5396 -> 5396 bytes
-rw-r--r--examples/widgets/widgets/tetrix/doc/tetrix.rst (renamed from examples/widgets/tetrix/doc/tetrix.rst)0
-rw-r--r--examples/widgets/widgets/tetrix/tetrix.py471
-rw-r--r--examples/widgets/widgets/tetrix/tetrix.pyproject (renamed from examples/widgets/tetrix/tetrix.pyproject)0
-rw-r--r--examples/widgets/widgetsgallery/main.py3
-rw-r--r--examples/widgets/widgetsgallery/widgetgallery.py22
-rw-r--r--examples/xml/dombookmarks/dombookmarks.py39
-rw-r--r--examples/xml/dombookmarks/dombookmarks.pyproject2
-rw-r--r--examples/xml/dombookmarks/frank.xbel230
-rw-r--r--examples/xml/dombookmarks/jennifer.xbel82
1038 files changed, 123269 insertions, 16211 deletions
diff --git a/examples/async/eratosthenes/doc/eratosthenes.rst b/examples/async/eratosthenes/doc/eratosthenes.rst
new file mode 100644
index 000000000..494a94df3
--- /dev/null
+++ b/examples/async/eratosthenes/doc/eratosthenes.rst
@@ -0,0 +1,53 @@
+Async examples
+==============
+
+The Python language provides keywords for asynchronous operations, i.e.,
+"async" to define coroutines or "await" to schedule asynchronous calls in the
+event loop (see `PEP 492 <https://peps.python.org/pep-0492/>`_). It is up to
+packages to implement an event loop, support for these keywords, and more.
+
+The best-known package for this is `asyncio`. Since both an async package and
+Qt itself work with event loops, special care must be taken to ensure that both
+event loops work with each other. asyncio offers a function `stop` that allows
+stopping an event loop without closing it. If it is called while a loop is
+running through `run_forever`, the loop will run the current batch of callbacks
+and then exit. New callbacks wil be scheduled the next time `run_forever` is
+called.
+
+This approach is highly experimental and does not represent the state of the
+art of integrating Qt with asyncio. Instead it should rather be regarded more
+as a proof of concept to contrast asyncio with other async packages such as
+`trio`, which offers a dedicated `low-level API
+<https://trio.readthedocs.io/en/stable/reference-lowlevel.html>`_ for more
+complicated use cases such as this. Specifically, there exists a function
+`start_guest_run` that enables running the Trio event loop as a "guest" inside
+another event loop - Qt's in our case.
+
+Based on this functionality, two examples for async usage with Qt have been
+implemented: `eratosthenes` and `minimal`:
+
+.. image:: eratosthenes.svg
+ :alt: Async example: Eratosthenes
+ :width: 400
+
+* `eratosthenes` is a more extensive example that visualizes the Sieve of
+ Eratosthenes algorithm. This algorithm per se is not one that is particularly
+ suitable for asynchronous operations as it's not I/O-heavy, but synchronizing
+ coroutines to a configurable tick allows for a good visualization.
+* `minimal` is a minimal example featuring a button that triggers an
+ asynchronous coroutine with a sleep. It is designed to highlight which
+ boilerplate code is essential for an async program with Qt and offers a
+ starting point for more complex programs.
+
+Both examples feature:
+
+1. A window class.
+2. An `AsyncHelper` class containing `start_guest_run` plus helpers and
+ callbacks necessary for its invocation. The entry point for the Trio/asyncio
+ guest run is provided as an argument from outside, which can be any async
+ function.
+
+While `eratosthenes` offloads the asynchronous logic that will run in
+trio's/asyncio's event loop into a separate class, `minimal` demonstrates that
+async functions can be integrated into any class, including subclasses of Qt
+classes.
diff --git a/examples/async/eratosthenes/doc/eratosthenes.svg b/examples/async/eratosthenes/doc/eratosthenes.svg
new file mode 100644
index 000000000..eaf53da50
--- /dev/null
+++ b/examples/async/eratosthenes/doc/eratosthenes.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1210.666" height="886.666" viewBox="0 0 908 665" baseProfile="tiny" xmlns:v="https://vecta.io/nano"><style><![CDATA[.B{stroke:none}.C{font-family:.AppleSystemUIFont}.D{font-size:13px}.E{font-weight:700}.F{fill:#000}.G{fill:#548f7c}.H{fill:#bab040}.I{fill:#51a660}.J{fill:#a193b6}.K{fill:#8a4a8f}.L{fill:#45b2a7}.M{fill:#916bbd}.N{fill:#7dad6b}.O{fill:#628f47}.P{fill:#6474c0}.Q{fill:#b182b0}.R{fill:#a94257}.S{fill:#a1794d}.T{fill:#bbac54}.U{fill:#6e4344}.V{fill:#a38b9d}.W{fill:#6c4268}.X{fill:#974c66}.Y{fill:#8163bb}.Z{fill:#bda991}.a{fill:#599c9d}.b{fill:#7ec05e}.c{fill:#b26b51}.d{fill:#434da9}.e{fill:#a46a69}.f{fill:#6a6467}.g{fill:#954ca5}.h{fill:#6f4965}.i{fill:#7a99b1}.j{fill:#786344}.k{fill:#898683}.l{fill:#ac989d}.m{fill:#4f5a7b}.n{fill:#b28c5f}.o{fill:#639747}.p{fill:#aba64a}.q{fill:#916c5e}.r{fill:#b0929e}.s{fill:#909864}.t{fill:#a14faf}]]></style><g fill="none" stroke="#000" fill-rule="evenodd" class="C B"><path d="M0 0h908v665H0z" fill="#ececec" class="D"/><g font-size="14" class="F"><text xml:space="preserve" y="25.781" x="192">🥳</text><text xml:space="preserve" x="214.016" y="25.781"> Congratulations! You found all the prime numbers and solved mathematics. </text><text xml:space="preserve" x="693.031" y="25.781">🥳</text></g><text xml:space="preserve" y="61.906" x="34" class="D F">1</text><text xml:space="preserve" y="61.906" x="67" class="D F">2</text><text xml:space="preserve" y="61.906" x="101" class="D F">3</text><text xml:space="preserve" y="61.906" x="136" class="D E l">4</text><text xml:space="preserve" y="61.906" x="171" class="D F">5</text><text xml:space="preserve" y="61.906" x="206" class="D E U">6</text><text xml:space="preserve" y="61.906" x="241" class="D F">7</text><text xml:space="preserve" y="61.906" x="275" class="D E l">8</text><text xml:space="preserve" y="61.906" x="310" class="D E U">9</text><text xml:space="preserve" y="61.906" x="341" class="D E M">10</text><text xml:space="preserve" y="61.906" x="378" class="D F">11</text><text xml:space="preserve" y="61.906" x="411" class="D E U">12</text><text xml:space="preserve" y="61.906" x="446" class="D F">13</text><text xml:space="preserve" y="61.906" x="480" class="D E G">14</text><text xml:space="preserve" y="61.906" x="516" class="D E M">15</text><text xml:space="preserve" y="61.906" x="550" class="D E l">16</text><text xml:space="preserve" y="61.906" x="586" class="D F">17</text><text xml:space="preserve" y="61.906" x="619" class="D E U">18</text><text xml:space="preserve" y="61.906" x="655" class="D F">19</text><text xml:space="preserve" y="61.906" x="689" class="D E M">20</text><text xml:space="preserve" y="61.906" x="725" class="D E G">21</text><text xml:space="preserve" y="61.906" x="758" class="D E H">22</text><text xml:space="preserve" y="61.906" x="793" class="D F">23</text><text xml:space="preserve" y="61.906" x="827" class="D E U">24</text><text xml:space="preserve" y="61.906" x="862" class="D E M">25</text><text xml:space="preserve" y="85.906" x="28" class="D E I">26</text><text xml:space="preserve" y="85.906" x="63" class="D E U">27</text><text xml:space="preserve" y="85.906" x="96" class="D E G">28</text><text xml:space="preserve" y="85.906" x="132" class="D F">29</text><text xml:space="preserve" y="85.906" x="166" class="D E M">30</text><text xml:space="preserve" y="85.906" x="203" class="D F">31</text><text xml:space="preserve" y="85.906" x="236" class="D E K">32</text><text xml:space="preserve" y="85.906" x="270" class="D E H">33</text><text xml:space="preserve" y="85.906" x="305" class="D E J">34</text><text xml:space="preserve" y="85.906" x="341" class="D E G">35</text><text xml:space="preserve" y="85.906" x="375" class="D E U">36</text><text xml:space="preserve" y="85.906" x="411" class="D F">37</text><text xml:space="preserve" y="85.906" x="444" class="D E L">38</text><text xml:space="preserve" y="85.906" x="479" class="D E I">39</text><text xml:space="preserve" y="85.906" x="514" class="D E M">40</text><text xml:space="preserve" y="85.906" x="551" class="D F">41</text><text xml:space="preserve" y="85.906" x="584" class="D E G">42</text><text xml:space="preserve" y="85.906" x="619" class="D F">43</text><text xml:space="preserve" y="85.906" x="653" class="D E H">44</text><text xml:space="preserve" y="85.906" x="688" class="D E M">45</text><text xml:space="preserve" y="85.906" x="723" class="D E N">46</text><text xml:space="preserve" y="85.906" x="759" class="D F">47</text><text xml:space="preserve" y="85.906" x="792" class="D E K">48</text><text xml:space="preserve" y="85.906" x="827" class="D E G">49</text><text xml:space="preserve" y="85.906" x="861" class="D E M">50</text><text xml:space="preserve" y="109.906" x="29" class="D E J">51</text><text xml:space="preserve" y="109.906" x="62" class="D E I">52</text><text xml:space="preserve" y="109.906" x="97" class="D F">53</text><text xml:space="preserve" y="109.906" x="131" class="D E U">54</text><text xml:space="preserve" y="109.906" x="167" class="D E H">55</text><text xml:space="preserve" y="109.906" x="201" class="D E G">56</text><text xml:space="preserve" y="109.906" x="236" class="D E L">57</text><text xml:space="preserve" y="109.906" x="270" class="D E O">58</text><text xml:space="preserve" y="109.906" x="306" class="D F">59</text><text xml:space="preserve" y="109.906" x="340" class="D E M">60</text><text xml:space="preserve" y="109.906" x="377" class="D F">61</text><text xml:space="preserve" y="109.906" x="410" class="D E P">62</text><text xml:space="preserve" y="109.906" x="444" class="D E G">63</text><text xml:space="preserve" y="109.906" x="479" class="D E K">64</text><text xml:space="preserve" y="109.906" x="514" class="D E I">65</text><text xml:space="preserve" y="109.906" x="549" class="D E H">66</text><text xml:space="preserve" y="109.906" x="585" class="D F">67</text><text xml:space="preserve" y="109.906" x="618" class="D E J">68</text><text xml:space="preserve" y="109.906" x="653" class="D E N">69</text><text xml:space="preserve" y="109.906" x="689" class="D E G">70</text><text xml:space="preserve" y="109.906" x="725" class="D F">71</text><text xml:space="preserve" y="109.906" x="759" class="D E U">72</text><text xml:space="preserve" y="109.906" x="793" class="D F">73</text><text xml:space="preserve" y="109.906" x="828" class="D E Q">74</text><text xml:space="preserve" y="109.906" x="862" class="D E M">75</text><text xml:space="preserve" y="133.906" x="28" class="D E L">76</text><text xml:space="preserve" y="133.906" x="63" class="D E H">77</text><text xml:space="preserve" y="133.906" x="97" class="D E I">78</text><text xml:space="preserve" y="133.906" x="132" class="D F">79</text><text xml:space="preserve" y="133.906" x="166" class="D E K">80</text><text xml:space="preserve" y="133.906" x="202" class="D E U">81</text><text xml:space="preserve" y="133.906" x="236" class="D E R">82</text><text xml:space="preserve" y="133.906" x="271" class="D F">83</text><text xml:space="preserve" y="133.906" x="305" class="D E G">84</text><text xml:space="preserve" y="133.906" x="340" class="D E J">85</text><text xml:space="preserve" y="133.906" x="375" class="D E S">86</text><text xml:space="preserve" y="133.906" x="410" class="D E O">87</text><text xml:space="preserve" y="133.906" x="444" class="D E H">88</text><text xml:space="preserve" y="133.906" x="480" class="D F">89</text><text xml:space="preserve" y="133.906" x="514" class="D E M">90</text><text xml:space="preserve" y="133.906" x="550" class="D E I">91</text><text xml:space="preserve" y="133.906" x="584" class="D E N">92</text><text xml:space="preserve" y="133.906" x="618" class="D E P">93</text><text xml:space="preserve" y="133.906" x="653" class="D E T">94</text><text xml:space="preserve" y="133.906" x="688" class="D E L">95</text><text xml:space="preserve" y="133.906" x="723" class="D E K">96</text><text xml:space="preserve" y="133.906" x="759" class="D F">97</text><text xml:space="preserve" y="133.906" x="792" class="D E G">98</text><text xml:space="preserve" y="133.906" x="827" class="D E H">99</text><text xml:space="preserve" y="133.906" x="858" class="D E M">100</text><text xml:space="preserve" y="157.906" x="27" class="D F">101</text><text xml:space="preserve" y="157.906" x="59" class="D E J">102</text><text xml:space="preserve" y="157.906" x="94" class="D F">103</text><text xml:space="preserve" y="157.906" x="128" class="D E I">104</text><text xml:space="preserve" y="157.906" x="163" class="D E G">105</text><text xml:space="preserve" y="157.906" x="198" class="D E V">106</text><text xml:space="preserve" y="157.906" x="234" class="D F">107</text><text xml:space="preserve" y="157.906" x="267" class="D E W">108</text><text xml:space="preserve" y="157.906" x="303" class="D F">109</text><text xml:space="preserve" y="157.906" x="338" class="D E H">110</text><text xml:space="preserve" y="157.906" x="374" class="D E Q">111</text><text xml:space="preserve" y="157.906" x="408" class="D E K">112</text><text xml:space="preserve" y="157.906" x="443" class="D F">113</text><text xml:space="preserve" y="157.906" x="477" class="D E L">114</text><text xml:space="preserve" y="157.906" x="512" class="D E N">115</text><text xml:space="preserve" y="157.906" x="547" class="D E O">116</text><text xml:space="preserve" y="157.906" x="582" class="D E I">117</text><text xml:space="preserve" y="157.906" x="616" class="D E X">118</text><text xml:space="preserve" y="157.906" x="651" class="D E J">119</text><text xml:space="preserve" y="157.906" x="685" class="D E M">120</text><text xml:space="preserve" y="157.906" x="721" class="D E H">121</text><text xml:space="preserve" y="157.906" x="755" class="D E Y">122</text><text xml:space="preserve" y="157.906" x="789" class="D E R">123</text><text xml:space="preserve" y="157.906" x="824" class="D E P">124</text><text xml:space="preserve" y="157.906" x="858" class="D E M">125</text><text xml:space="preserve" y="181.906" x="25" class="D E G">126</text><text xml:space="preserve" y="181.906" x="60" class="D F">127</text><text xml:space="preserve" y="181.906" x="93" class="D E K">128</text><text xml:space="preserve" y="181.906" x="128" class="D E S">129</text><text xml:space="preserve" y="181.906" x="163" class="D E I">130</text><text xml:space="preserve" y="181.906" x="200" class="D F">131</text><text xml:space="preserve" y="181.906" x="233" class="D E H">132</text><text xml:space="preserve" y="181.906" x="267" class="D E L">133</text><text xml:space="preserve" y="181.906" x="302" class="D E Z">134</text><text xml:space="preserve" y="181.906" x="337" class="D E M">135</text><text xml:space="preserve" y="181.906" x="372" class="D E J">136</text><text xml:space="preserve" y="181.906" x="408" class="D F">137</text><text xml:space="preserve" y="181.906" x="441" class="D E N">138</text><text xml:space="preserve" y="181.906" x="477" class="D F">139</text><text xml:space="preserve" y="181.906" x="511" class="D E G">140</text><text xml:space="preserve" y="181.906" x="547" class="D E T">141</text><text xml:space="preserve" y="181.906" x="581" class="D E a">142</text><text xml:space="preserve" y="181.906" x="615" class="D E I">143</text><text xml:space="preserve" y="181.906" x="650" class="D E K">144</text><text xml:space="preserve" y="181.906" x="685" class="D E O">145</text><text xml:space="preserve" y="181.906" x="720" class="D E b">146</text><text xml:space="preserve" y="181.906" x="755" class="D E G">147</text><text xml:space="preserve" y="181.906" x="789" class="D E Q">148</text><text xml:space="preserve" y="181.906" x="824" class="D F">149</text><text xml:space="preserve" y="181.906" x="858" class="D E M">150</text><text xml:space="preserve" y="205.906" x="27" class="D F">151</text><text xml:space="preserve" y="205.906" x="59" class="D E L">152</text><text xml:space="preserve" y="205.906" x="93" class="D E J">153</text><text xml:space="preserve" y="205.906" x="128" class="D E H">154</text><text xml:space="preserve" y="205.906" x="163" class="D E P">155</text><text xml:space="preserve" y="205.906" x="198" class="D E I">156</text><text xml:space="preserve" y="205.906" x="234" class="D F">157</text><text xml:space="preserve" y="205.906" x="267" class="D E c">158</text><text xml:space="preserve" y="205.906" x="302" class="D E V">159</text><text xml:space="preserve" y="205.906" x="337" class="D E K">160</text><text xml:space="preserve" y="205.906" x="373" class="D E N">161</text><text xml:space="preserve" y="205.906" x="407" class="D E W">162</text><text xml:space="preserve" y="205.906" x="442" class="D F">163</text><text xml:space="preserve" y="205.906" x="476" class="D E R">164</text><text xml:space="preserve" y="205.906" x="511" class="D E H">165</text><text xml:space="preserve" y="205.906" x="546" class="D E d">166</text><text xml:space="preserve" y="205.906" x="582" class="D F">167</text><text xml:space="preserve" y="205.906" x="615" class="D E G">168</text><text xml:space="preserve" y="205.906" x="650" class="D E I">169</text><text xml:space="preserve" y="205.906" x="686" class="D E J">170</text><text xml:space="preserve" y="205.906" x="722" class="D E L">171</text><text xml:space="preserve" y="205.906" x="755" class="D E S">172</text><text xml:space="preserve" y="205.906" x="790" class="D F">173</text><text xml:space="preserve" y="205.906" x="824" class="D E O">174</text><text xml:space="preserve" y="205.906" x="859" class="D E G">175</text><text xml:space="preserve" y="229.906" x="25" class="D E K">176</text><text xml:space="preserve" y="229.906" x="60" class="D E X">177</text><text xml:space="preserve" y="229.906" x="93" class="D E e">178</text><text xml:space="preserve" y="229.906" x="129" class="D F">179</text><text xml:space="preserve" y="229.906" x="163" class="D E M">180</text><text xml:space="preserve" y="229.906" x="200" class="D F">181</text><text xml:space="preserve" y="229.906" x="233" class="D E I">182</text><text xml:space="preserve" y="229.906" x="267" class="D E Y">183</text><text xml:space="preserve" y="229.906" x="302" class="D E N">184</text><text xml:space="preserve" y="229.906" x="337" class="D E Q">185</text><text xml:space="preserve" y="229.906" x="372" class="D E P">186</text><text xml:space="preserve" y="229.906" x="407" class="D E J">187</text><text xml:space="preserve" y="229.906" x="441" class="D E T">188</text><text xml:space="preserve" y="229.906" x="476" class="D E G">189</text><text xml:space="preserve" y="229.906" x="511" class="D E L">190</text><text xml:space="preserve" y="229.906" x="548" class="D F">191</text><text xml:space="preserve" y="229.906" x="581" class="D E K">192</text><text xml:space="preserve" y="229.906" x="616" class="D F">193</text><text xml:space="preserve" y="229.906" x="650" class="D E f">194</text><text xml:space="preserve" y="229.906" x="685" class="D E I">195</text><text xml:space="preserve" y="229.906" x="720" class="D E G">196</text><text xml:space="preserve" y="229.906" x="756" class="D F">197</text><text xml:space="preserve" y="229.906" x="789" class="D E H">198</text><text xml:space="preserve" y="229.906" x="824" class="D F">199</text><text xml:space="preserve" y="229.906" x="857" class="D E M">200</text><text xml:space="preserve" y="253.906" x="25" class="D E Z">201</text><text xml:space="preserve" y="253.906" x="58" class="D E g">202</text><text xml:space="preserve" y="253.906" x="92" class="D E O">203</text><text xml:space="preserve" y="253.906" x="127" class="D E J">204</text><text xml:space="preserve" y="253.906" x="162" class="D E R">205</text><text xml:space="preserve" y="253.906" x="197" class="D E h">206</text><text xml:space="preserve" y="253.906" x="232" class="D E N">207</text><text xml:space="preserve" y="253.906" x="266" class="D E K">208</text><text xml:space="preserve" y="253.906" x="301" class="D E L">209</text><text xml:space="preserve" y="253.906" x="337" class="D E G">210</text><text xml:space="preserve" y="253.906" x="374" class="D F">211</text><text xml:space="preserve" y="253.906" x="407" class="D E V">212</text><text xml:space="preserve" y="253.906" x="441" class="D E a">213</text><text xml:space="preserve" y="253.906" x="476" class="D E i">214</text><text xml:space="preserve" y="253.906" x="511" class="D E S">215</text><text xml:space="preserve" y="253.906" x="546" class="D E W">216</text><text xml:space="preserve" y="253.906" x="581" class="D E P">217</text><text xml:space="preserve" y="253.906" x="615" class="D E j">218</text><text xml:space="preserve" y="253.906" x="650" class="D E b">219</text><text xml:space="preserve" y="253.906" x="684" class="D E H">220</text><text xml:space="preserve" y="253.906" x="721" class="D E J">221</text><text xml:space="preserve" y="253.906" x="754" class="D E Q">222</text><text xml:space="preserve" y="253.906" x="789" class="D F">223</text><text xml:space="preserve" y="253.906" x="823" class="D E K">224</text><text xml:space="preserve" y="253.906" x="858" class="D E M">225</text><text xml:space="preserve" y="277.906" x="24" class="D E k">226</text><text xml:space="preserve" y="277.906" x="59" class="D F">227</text><text xml:space="preserve" y="277.906" x="92" class="D E L">228</text><text xml:space="preserve" y="277.906" x="128" class="D F">229</text><text xml:space="preserve" y="277.906" x="162" class="D E N">230</text><text xml:space="preserve" y="277.906" x="198" class="D E H">231</text><text xml:space="preserve" y="277.906" x="232" class="D E O">232</text><text xml:space="preserve" y="277.906" x="267" class="D F">233</text><text xml:space="preserve" y="277.906" x="301" class="D E I">234</text><text xml:space="preserve" y="277.906" x="336" class="D E T">235</text><text xml:space="preserve" y="277.906" x="371" class="D E X">236</text><text xml:space="preserve" y="277.906" x="406" class="D E c">237</text><text xml:space="preserve" y="277.906" x="440" class="D E J">238</text><text xml:space="preserve" y="277.906" x="476" class="D F">239</text><text xml:space="preserve" y="277.906" x="510" class="D E K">240</text><text xml:space="preserve" y="277.906" x="547" class="D F">241</text><text xml:space="preserve" y="277.906" x="580" class="D E H">242</text><text xml:space="preserve" y="277.906" x="614" class="D E m">243</text><text xml:space="preserve" y="277.906" x="649" class="D E Y">244</text><text xml:space="preserve" y="277.906" x="684" class="D E G">245</text><text xml:space="preserve" y="277.906" x="719" class="D E R">246</text><text xml:space="preserve" y="277.906" x="754" class="D E L">247</text><text xml:space="preserve" y="277.906" x="788" class="D E P">248</text><text xml:space="preserve" y="277.906" x="823" class="D E d">249</text><text xml:space="preserve" y="277.906" x="857" class="D E M">250</text><text xml:space="preserve" y="301.906" x="26" class="D F">251</text><text xml:space="preserve" y="301.906" x="58" class="D E G">252</text><text xml:space="preserve" y="301.906" x="92" class="D E N">253</text><text xml:space="preserve" y="301.906" x="127" class="D E n">254</text><text xml:space="preserve" y="301.906" x="162" class="D E J">255</text><text xml:space="preserve" y="301.906" x="197" class="D E K">256</text><text xml:space="preserve" y="301.906" x="233" class="D F">257</text><text xml:space="preserve" y="301.906" x="266" class="D E S">258</text><text xml:space="preserve" y="301.906" x="301" class="D E Q">259</text><text xml:space="preserve" y="301.906" x="336" class="D E I">260</text><text xml:space="preserve" y="301.906" x="372" class="D E O">261</text><text xml:space="preserve" y="301.906" x="406" class="D E o">262</text><text xml:space="preserve" y="301.906" x="441" class="D F">263</text><text xml:space="preserve" y="301.906" x="475" class="D E H">264</text><text xml:space="preserve" y="301.906" x="510" class="D E V">265</text><text xml:space="preserve" y="301.906" x="545" class="D E L">266</text><text xml:space="preserve" y="301.906" x="580" class="D E e">267</text><text xml:space="preserve" y="301.906" x="614" class="D E Z">268</text><text xml:space="preserve" y="301.906" x="650" class="D F">269</text><text xml:space="preserve" y="301.906" x="685" class="D E W">270</text><text xml:space="preserve" y="301.906" x="722" class="D F">271</text><text xml:space="preserve" y="301.906" x="754" class="D E K">272</text><text xml:space="preserve" y="301.906" x="789" class="D E I">273</text><text xml:space="preserve" y="301.906" x="824" class="D E p">274</text><text xml:space="preserve" y="301.906" x="858" class="D E H">275</text><text xml:space="preserve" y="325.906" x="24" class="D E N">276</text><text xml:space="preserve" y="325.906" x="59" class="D F">277</text><text xml:space="preserve" y="325.906" x="93" class="D E q">278</text><text xml:space="preserve" y="325.906" x="128" class="D E P">279</text><text xml:space="preserve" y="325.906" x="162" class="D E G">280</text><text xml:space="preserve" y="325.906" x="199" class="D F">281</text><text xml:space="preserve" y="325.906" x="232" class="D E T">282</text><text xml:space="preserve" y="325.906" x="267" class="D F">283</text><text xml:space="preserve" y="325.906" x="301" class="D E a">284</text><text xml:space="preserve" y="325.906" x="336" class="D E L">285</text><text xml:space="preserve" y="325.906" x="371" class="D E I">286</text><text xml:space="preserve" y="325.906" x="406" class="D E R">287</text><text xml:space="preserve" y="325.906" x="440" class="D E K">288</text><text xml:space="preserve" y="325.906" x="475" class="D E J">289</text><text xml:space="preserve" y="325.906" x="510" class="D E O">290</text><text xml:space="preserve" y="325.906" x="546" class="D E f">291</text><text xml:space="preserve" y="325.906" x="580" class="D E b">292</text><text xml:space="preserve" y="325.906" x="615" class="D F">293</text><text xml:space="preserve" y="325.906" x="649" class="D E G">294</text><text xml:space="preserve" y="325.906" x="684" class="D E X">295</text><text xml:space="preserve" y="325.906" x="719" class="D E Q">296</text><text xml:space="preserve" y="325.906" x="754" class="D E H">297</text><text xml:space="preserve" y="325.906" x="788" class="D E r">298</text><text xml:space="preserve" y="325.906" x="823" class="D E N">299</text><text xml:space="preserve" y="325.906" x="857" class="D E M">300</text><text xml:space="preserve" y="349.906" x="25" class="D E S">301</text><text xml:space="preserve" y="349.906" x="58" class="D E s">302</text><text xml:space="preserve" y="349.906" x="92" class="D E g">303</text><text xml:space="preserve" y="349.906" x="127" class="D E L">304</text><text xml:space="preserve" y="349.906" x="162" class="D E Y">305</text><text xml:space="preserve" y="349.906" x="197" class="D E J">306</text><text xml:space="preserve" y="349.906" x="233" class="D F">307</text><text xml:space="preserve" y="349.906" x="266" class="D E H">308</text><text xml:space="preserve" y="349.906" x="301" class="D E h">309</text><text xml:space="preserve" y="349.906" x="337" class="D E P">310</text><text xml:space="preserve" y="349.906" x="374" class="D F">311</text><text xml:space="preserve" y="349.906" x="407" class="D E I">312</text><text xml:space="preserve" y="349.906" x="442" class="D F">313</text><text fill="#72a694" xml:space="preserve" y="349.906" x="476" class="D E">314</text><text xml:space="preserve" y="349.906" x="511" class="D E G">315</text><text xml:space="preserve" y="349.906" x="546" class="D E c">316</text><text xml:space="preserve" y="349.906" x="582" class="D F">317</text><text xml:space="preserve" y="349.906" x="615" class="D E V">318</text><text xml:space="preserve" y="349.906" x="650" class="D E O">319</text><text xml:space="preserve" y="349.906" x="684" class="D E K">320</text><text xml:space="preserve" y="349.906" x="720" class="D E i">321</text><text xml:space="preserve" y="349.906" x="754" class="D E N">322</text><text xml:space="preserve" y="349.906" x="788" class="D E L">323</text><text xml:space="preserve" y="349.906" x="823" class="D E W">324</text><text xml:space="preserve" y="349.906" x="857" class="D E I">325</text><text fill="#aa85a2" xml:space="preserve" y="373.906" x="24" class="D E">326</text><text xml:space="preserve" y="373.906" x="58" class="D E j">327</text><text xml:space="preserve" y="373.906" x="92" class="D E R">328</text><text xml:space="preserve" y="373.906" x="127" class="D E T">329</text><text xml:space="preserve" y="373.906" x="162" class="D E H">330</text><text xml:space="preserve" y="373.906" x="199" class="D F">331</text><text xml:space="preserve" y="373.906" x="232" class="D E d">332</text><text xml:space="preserve" y="373.906" x="266" class="D E Q">333</text><text fill="#bca451" xml:space="preserve" y="373.906" x="301" class="D E">334</text><text xml:space="preserve" y="373.906" x="336" class="D E Z">335</text><text xml:space="preserve" y="373.906" x="371" class="D E K">336</text><text xml:space="preserve" y="373.906" x="407" class="D F">337</text><text xml:space="preserve" y="373.906" x="440" class="D E I">338</text><text xml:space="preserve" y="373.906" x="475" class="D E k">339</text><text xml:space="preserve" y="373.906" x="510" class="D E J">340</text><text xml:space="preserve" y="373.906" x="546" class="D E P">341</text><text xml:space="preserve" y="373.906" x="580" class="D E L">342</text><text xml:space="preserve" y="373.906" x="614" class="D E G">343</text><text xml:space="preserve" y="373.906" x="649" class="D E S">344</text><text xml:space="preserve" y="373.906" x="684" class="D E N">345</text><text fill="#8e8dba" xml:space="preserve" y="373.906" x="719" class="D E">346</text><text xml:space="preserve" y="373.906" x="755" class="D F">347</text><text xml:space="preserve" y="373.906" x="788" class="D E O">348</text><text xml:space="preserve" y="373.906" x="823" class="D F">349</text><text xml:space="preserve" y="373.906" x="857" class="D E G">350</text><text xml:space="preserve" y="397.906" x="25" class="D E I">351</text><text xml:space="preserve" y="397.906" x="58" class="D E K">352</text><text xml:space="preserve" y="397.906" x="93" class="D F">353</text><text xml:space="preserve" y="397.906" x="127" class="D E X">354</text><text xml:space="preserve" y="397.906" x="162" class="D E a">355</text><text xml:space="preserve" y="397.906" x="197" class="D E e">356</text><text xml:space="preserve" y="397.906" x="232" class="D E J">357</text><text fill="#79a79e" xml:space="preserve" y="397.906" x="266" class="D E">358</text><text xml:space="preserve" y="397.906" x="302" class="D F">359</text><text xml:space="preserve" y="397.906" x="336" class="D E t">360</text><text xml:space="preserve" y="397.906" x="372" class="D E L">361</text><text fill="#656186" xml:space="preserve" y="397.906" x="406" class="D E">362</text><text xml:space="preserve" y="397.906" x="440" class="D E H">363</text><text xml:space="preserve" y="397.906" x="475" class="D E I">364</text><text xml:space="preserve" y="397.906" x="510" class="D E b">365</text><text xml:space="preserve" y="397.906" x="545" class="D E Y">366</text><text xml:space="preserve" y="397.906" x="581" class="D F">367</text><text xml:space="preserve" y="397.906" x="614" class="D E N">368</text><text xml:space="preserve" y="397.906" x="649" class="D E R">369</text><text xml:space="preserve" y="397.906" x="685" class="D E Q">370</text><text xml:space="preserve" y="397.906" x="721" class="D E V">371</text><text xml:space="preserve" y="397.906" x="754" class="D E P">372</text><text xml:space="preserve" y="397.906" x="789" class="D F">373</text><text xml:space="preserve" y="397.906" x="823" class="D E J">374</text><text xml:space="preserve" y="397.906" x="858" class="D E M">375</text><text xml:space="preserve" y="421.906" x="24" class="D E T">376</text><text xml:space="preserve" y="421.906" x="59" class="D E O">377</text><text xml:space="preserve" y="421.906" x="92" class="D E W">378</text><text xml:space="preserve" y="421.906" x="128" class="D F">379</text><text xml:space="preserve" y="421.906" x="162" class="D E L">380</text><text xml:space="preserve" y="421.906" x="198" class="D E n">381</text><text fill="#94894b" xml:space="preserve" y="421.906" x="232" class="D E">382</text><text xml:space="preserve" y="421.906" x="267" class="D F">383</text><text xml:space="preserve" y="421.906" x="301" class="D E K">384</text><text xml:space="preserve" y="421.906" x="336" class="D E H">385</text><text fill="#87759e" xml:space="preserve" y="421.906" x="371" class="D E">386</text><text xml:space="preserve" y="421.906" x="406" class="D E S">387</text><text xml:space="preserve" y="421.906" x="440" class="D E f">388</text><text xml:space="preserve" y="421.906" x="476" class="D F">389</text><text xml:space="preserve" y="421.906" x="510" class="D E I">390</text><text xml:space="preserve" y="421.906" x="546" class="D E N">391</text><text xml:space="preserve" y="421.906" x="580" class="D E G">392</text><text xml:space="preserve" y="421.906" x="614" class="D E o">393</text><text fill="#9f6db4" xml:space="preserve" y="421.906" x="649" class="D E">394</text><text xml:space="preserve" y="421.906" x="684" class="D E c">395</text><text xml:space="preserve" y="421.906" x="719" class="D E H">396</text><text xml:space="preserve" y="421.906" x="755" class="D F">397</text><text fill="#aebba6" xml:space="preserve" y="421.906" x="788" class="D E">398</text><text xml:space="preserve" y="421.906" x="823" class="D E L">399</text><text xml:space="preserve" y="421.906" x="857" class="D E K">400</text><text xml:space="preserve" y="445.906" x="25" class="D F">401</text><text xml:space="preserve" y="445.906" x="58" class="D E Z">402</text><text xml:space="preserve" y="445.906" x="92" class="D E P">403</text><text xml:space="preserve" y="445.906" x="127" class="D E g">404</text><text xml:space="preserve" y="445.906" x="162" class="D E m">405</text><text xml:space="preserve" y="445.906" x="197" class="D E O">406</text><text xml:space="preserve" y="445.906" x="232" class="D E Q">407</text><text xml:space="preserve" y="445.906" x="266" class="D E J">408</text><text xml:space="preserve" y="445.906" x="302" class="D F">409</text><text xml:space="preserve" y="445.906" x="337" class="D E R">410</text><text xml:space="preserve" y="445.906" x="373" class="D E p">411</text><text xml:space="preserve" y="445.906" x="407" class="D E h">412</text><text xml:space="preserve" y="445.906" x="441" class="D E X">413</text><text xml:space="preserve" y="445.906" x="476" class="D E N">414</text><text xml:space="preserve" y="445.906" x="511" class="D E d">415</text><text xml:space="preserve" y="445.906" x="546" class="D E K">416</text><text xml:space="preserve" y="445.906" x="581" class="D E q">417</text><text xml:space="preserve" y="445.906" x="615" class="D E L">418</text><text xml:space="preserve" y="445.906" x="651" class="D F">419</text><text xml:space="preserve" y="445.906" x="684" class="D E G">420</text><text xml:space="preserve" y="445.906" x="721" class="D F">421</text><text fill="#61a569" xml:space="preserve" y="445.906" x="754" class="D E">422</text><text xml:space="preserve" y="445.906" x="788" class="D E T">423</text><text xml:space="preserve" y="445.906" x="823" class="D E V">424</text><text xml:space="preserve" y="445.906" x="857" class="D E J">425</text><text xml:space="preserve" y="469.906" x="24" class="D E a">426</text><text xml:space="preserve" y="469.906" x="58" class="D E Y">427</text><text xml:space="preserve" y="469.906" x="92" class="D E i">428</text><text xml:space="preserve" y="469.906" x="127" class="D E I">429</text><text xml:space="preserve" y="469.906" x="162" class="D E S">430</text><text xml:space="preserve" y="469.906" x="199" class="D F">431</text><text xml:space="preserve" y="469.906" x="232" class="D E W">432</text><text xml:space="preserve" y="469.906" x="267" class="D F">433</text><text xml:space="preserve" y="469.906" x="301" class="D E P">434</text><text xml:space="preserve" y="469.906" x="336" class="D E O">435</text><text xml:space="preserve" y="469.906" x="371" class="D E j">436</text><text xml:space="preserve" y="469.906" x="406" class="D E N">437</text><text xml:space="preserve" y="469.906" x="440" class="D E b">438</text><text xml:space="preserve" y="469.906" x="476" class="D F">439</text><text xml:space="preserve" y="469.906" x="510" class="D E H">440</text><text xml:space="preserve" y="469.906" x="546" class="D E G">441</text><text xml:space="preserve" y="469.906" x="580" class="D E J">442</text><text xml:space="preserve" y="469.906" x="615" class="D F">443</text><text xml:space="preserve" y="469.906" x="649" class="D E Q">444</text><text xml:space="preserve" y="469.906" x="684" class="D E e">445</text><text fill="#7e6bb4" xml:space="preserve" y="469.906" x="719" class="D E">446</text><text xml:space="preserve" y="469.906" x="754" class="D E r">447</text><text xml:space="preserve" y="469.906" x="788" class="D E K">448</text><text xml:space="preserve" y="469.906" x="823" class="D F">449</text><text xml:space="preserve" y="469.906" x="857" class="D E M">450</text><text xml:space="preserve" y="493.906" x="25" class="D E R">451</text><text xml:space="preserve" y="493.906" x="58" class="D E k">452</text><text xml:space="preserve" y="493.906" x="92" class="D E s">453</text><text fill="#a2569d" xml:space="preserve" y="493.906" x="127" class="D E">454</text><text xml:space="preserve" y="493.906" x="162" class="D E I">455</text><text xml:space="preserve" y="493.906" x="197" class="D E L">456</text><text xml:space="preserve" y="493.906" x="233" class="D F">457</text><text fill="#67ab80" xml:space="preserve" y="493.906" x="266" class="D E">458</text><text xml:space="preserve" y="493.906" x="301" class="D E J">459</text><text xml:space="preserve" y="493.906" x="336" class="D E N">460</text><text xml:space="preserve" y="493.906" x="373" class="D F">461</text><text xml:space="preserve" y="493.906" x="406" class="D E H">462</text><text xml:space="preserve" y="493.906" x="441" class="D F">463</text><text xml:space="preserve" y="493.906" x="475" class="D E O">464</text><text xml:space="preserve" y="493.906" x="510" class="D E P">465</text><text fill="#5db273" xml:space="preserve" y="493.906" x="545" class="D E">466</text><text xml:space="preserve" y="493.906" x="581" class="D F">467</text><text xml:space="preserve" y="493.906" x="614" class="D E I">468</text><text xml:space="preserve" y="493.906" x="649" class="D E Z">469</text><text xml:space="preserve" y="493.906" x="685" class="D E T">470</text><text fill="#72a694" xml:space="preserve" y="493.906" x="721" class="D E">471</text><text xml:space="preserve" y="493.906" x="754" class="D E X">472</text><text xml:space="preserve" y="493.906" x="789" class="D E S">473</text><text xml:space="preserve" y="493.906" x="823" class="D E c">474</text><text xml:space="preserve" y="493.906" x="858" class="D E L">475</text><text xml:space="preserve" y="517.906" x="24" class="D E J">476</text><text xml:space="preserve" y="517.906" x="58" class="D E V">477</text><text fill="#5c989e" xml:space="preserve" y="517.906" x="92" class="D E">478</text><text xml:space="preserve" y="517.906" x="128" class="D F">479</text><text xml:space="preserve" y="517.906" x="162" class="D E K">480</text><text xml:space="preserve" y="517.906" x="198" class="D E Q">481</text><text fill="#bb54ab" xml:space="preserve" y="517.906" x="232" class="D E">482</text><text xml:space="preserve" y="517.906" x="266" class="D E N">483</text><text xml:space="preserve" y="517.906" x="301" class="D E H">484</text><text xml:space="preserve" y="517.906" x="336" class="D E f">485</text><text xml:space="preserve" y="517.906" x="371" class="D E W">486</text><text xml:space="preserve" y="517.906" x="407" class="D F">487</text><text xml:space="preserve" y="517.906" x="440" class="D E Y">488</text><text fill="#aa85a2" xml:space="preserve" y="517.906" x="475" class="D E">489</text><text xml:space="preserve" y="517.906" x="510" class="D E G">490</text><text xml:space="preserve" y="517.906" x="547" class="D F">491</text><text xml:space="preserve" y="517.906" x="580" class="D E R">492</text><text xml:space="preserve" y="517.906" x="614" class="D E O">493</text><text xml:space="preserve" y="517.906" x="649" class="D E L">494</text><text xml:space="preserve" y="517.906" x="684" class="D E H">495</text><text xml:space="preserve" y="517.906" x="719" class="D E P">496</text><text xml:space="preserve" y="517.906" x="754" class="D E a">497</text><text xml:space="preserve" y="517.906" x="788" class="D E d">498</text><text xml:space="preserve" y="517.906" x="823" class="D F">499</text><text xml:space="preserve" y="517.906" x="857" class="D E M">500</text><text fill="#bca451" xml:space="preserve" y="541.906" x="25" class="D E">501</text><text fill="#40448a" xml:space="preserve" y="541.906" x="58" class="D E">502</text><text xml:space="preserve" y="541.906" x="93" class="D F">503</text><text xml:space="preserve" y="541.906" x="127" class="D E t">504</text><text xml:space="preserve" y="541.906" x="162" class="D E g">505</text><text xml:space="preserve" y="541.906" x="197" class="D E N">506</text><text xml:space="preserve" y="541.906" x="232" class="D E I">507</text><text xml:space="preserve" y="541.906" x="266" class="D E n">508</text><text xml:space="preserve" y="541.906" x="302" class="D F">509</text><text xml:space="preserve" y="541.906" x="337" class="D E J">510</text><text xml:space="preserve" y="541.906" x="373" class="D E b">511</text><text xml:space="preserve" y="541.906" x="407" class="D E K">512</text><text xml:space="preserve" y="541.906" x="441" class="D E L">513</text><text fill="#b9a185" xml:space="preserve" y="541.906" x="476" class="D E">514</text><text xml:space="preserve" y="541.906" x="511" class="D E h">515</text><text xml:space="preserve" y="541.906" x="546" class="D E S">516</text><text xml:space="preserve" y="541.906" x="581" class="D E T">517</text><text xml:space="preserve" y="541.906" x="615" class="D E Q">518</text><text fill="#8e8dba" xml:space="preserve" y="541.906" x="650" class="D E">519</text><text xml:space="preserve" y="541.906" x="684" class="D E I">520</text><text xml:space="preserve" y="541.906" x="721" class="D F">521</text><text xml:space="preserve" y="541.906" x="754" class="D E O">522</text><text xml:space="preserve" y="541.906" x="789" class="D F">523</text><text xml:space="preserve" y="541.906" x="823" class="D E o">524</text><text xml:space="preserve" y="541.906" x="857" class="D E G">525</text><text fill="#5f5fb5" xml:space="preserve" y="565.906" x="24" class="D E">526</text><text xml:space="preserve" y="565.906" x="58" class="D E P">527</text><text xml:space="preserve" y="565.906" x="92" class="D E K">528</text><text xml:space="preserve" y="565.906" x="127" class="D E N">529</text><text xml:space="preserve" y="565.906" x="162" class="D E V">530</text><text xml:space="preserve" y="565.906" x="198" class="D E X">531</text><text xml:space="preserve" y="565.906" x="232" class="D E L">532</text><text xml:space="preserve" y="565.906" x="266" class="D E R">533</text><text xml:space="preserve" y="565.906" x="301" class="D E e">534</text><text xml:space="preserve" y="565.906" x="336" class="D E i">535</text><text xml:space="preserve" y="565.906" x="371" class="D E Z">536</text><text fill="#79a79e" xml:space="preserve" y="565.906" x="406" class="D E">537</text><text fill="#a8b675" xml:space="preserve" y="565.906" x="440" class="D E">538</text><text xml:space="preserve" y="565.906" x="475" class="D E H">539</text><text xml:space="preserve" y="565.906" x="510" class="D E W">540</text><text xml:space="preserve" y="565.906" x="547" class="D F">541</text><text fill="#4c7c9f" xml:space="preserve" y="565.906" x="580" class="D E">542</text><text fill="#656186" xml:space="preserve" y="565.906" x="614" class="D E">543</text><text xml:space="preserve" y="565.906" x="649" class="D E J">544</text><text xml:space="preserve" y="565.906" x="684" class="D E j">545</text><text xml:space="preserve" y="565.906" x="719" class="D E I">546</text><text xml:space="preserve" y="565.906" x="755" class="D F">547</text><text xml:space="preserve" y="565.906" x="788" class="D E p">548</text><text xml:space="preserve" y="565.906" x="823" class="D E Y">549</text><text xml:space="preserve" y="565.906" x="857" class="D E H">550</text><text xml:space="preserve" y="589.906" x="25" class="D E O">551</text><text xml:space="preserve" y="589.906" x="58" class="D E N">552</text><text xml:space="preserve" y="589.906" x="92" class="D E c">553</text><text fill="#5d90b0" xml:space="preserve" y="589.906" x="127" class="D E">554</text><text xml:space="preserve" y="589.906" x="162" class="D E Q">555</text><text xml:space="preserve" y="589.906" x="197" class="D E q">556</text><text xml:space="preserve" y="589.906" x="233" class="D F">557</text><text xml:space="preserve" y="589.906" x="266" class="D E P">558</text><text xml:space="preserve" y="589.906" x="301" class="D E S">559</text><text xml:space="preserve" y="589.906" x="336" class="D E K">560</text><text xml:space="preserve" y="589.906" x="372" class="D E J">561</text><text fill="#578f8b" xml:space="preserve" y="589.906" x="406" class="D E">562</text><text xml:space="preserve" y="589.906" x="441" class="D F">563</text><text xml:space="preserve" y="589.906" x="475" class="D E T">564</text><text xml:space="preserve" y="589.906" x="510" class="D E k">565</text><text fill="#8985af" xml:space="preserve" y="589.906" x="545" class="D E">566</text><text xml:space="preserve" y="589.906" x="580" class="D E m">567</text><text xml:space="preserve" y="589.906" x="614" class="D E a">568</text><text xml:space="preserve" y="589.906" x="650" class="D F">569</text><text xml:space="preserve" y="589.906" x="685" class="D E L">570</text><text xml:space="preserve" y="589.906" x="721" class="D F">571</text><text xml:space="preserve" y="589.906" x="754" class="D E I">572</text><text fill="#94894b" xml:space="preserve" y="589.906" x="789" class="D E">573</text><text xml:space="preserve" y="589.906" x="823" class="D E R">574</text><text xml:space="preserve" y="589.906" x="858" class="D E N">575</text><text xml:space="preserve" y="613.906" x="24" class="D E t">576</text><text xml:space="preserve" y="613.906" x="59" class="D F">577</text><text xml:space="preserve" y="613.906" x="92" class="D E J">578</text><text fill="#87759e" xml:space="preserve" y="613.906" x="128" class="D E">579</text><text xml:space="preserve" y="613.906" x="162" class="D E O">580</text><text xml:space="preserve" y="613.906" x="198" class="D E d">581</text><text xml:space="preserve" y="613.906" x="232" class="D E f">582</text><text xml:space="preserve" y="613.906" x="266" class="D E V">583</text><text xml:space="preserve" y="613.906" x="301" class="D E b">584</text><text xml:space="preserve" y="613.906" x="336" class="D E I">585</text><text fill="#ab6984" xml:space="preserve" y="613.906" x="371" class="D E">586</text><text xml:space="preserve" y="613.906" x="407" class="D F">587</text><text xml:space="preserve" y="613.906" x="440" class="D E G">588</text><text xml:space="preserve" y="613.906" x="475" class="D E P">589</text><text xml:space="preserve" y="613.906" x="510" class="D E X">590</text><text fill="#9f6db4" xml:space="preserve" y="613.906" x="546" class="D E">591</text><text xml:space="preserve" y="613.906" x="580" class="D E Q">592</text><text xml:space="preserve" y="613.906" x="615" class="D F">593</text><text xml:space="preserve" y="613.906" x="649" class="D E W">594</text><text xml:space="preserve" y="613.906" x="684" class="D E J">595</text><text xml:space="preserve" y="613.906" x="719" class="D E r">596</text><text fill="#aebba6" xml:space="preserve" y="613.906" x="754" class="D E">597</text><text xml:space="preserve" y="613.906" x="788" class="D E N">598</text><text xml:space="preserve" y="613.906" x="823" class="D F">599</text><text xml:space="preserve" y="613.906" x="857" class="D E M">600</text><text xml:space="preserve" y="637.906" x="26" class="D F">601</text><text xml:space="preserve" y="637.906" x="58" class="D E S">602</text><text xml:space="preserve" y="637.906" x="92" class="D E Z">603</text><text xml:space="preserve" y="637.906" x="127" class="D E s">604</text><text xml:space="preserve" y="637.906" x="162" class="D E H">605</text><text xml:space="preserve" y="637.906" x="197" class="D E g">606</text><text xml:space="preserve" y="637.906" x="233" class="D F">607</text><text xml:space="preserve" y="637.906" x="266" class="D E L">608</text><text xml:space="preserve" y="637.906" x="301" class="D E O">609</text><text xml:space="preserve" y="637.906" x="337" class="D E Y">610</text><text xml:space="preserve" y="637.906" x="373" class="D E T">611</text><text xml:space="preserve" y="637.906" x="407" class="D E J">612</text><text xml:space="preserve" y="637.906" x="442" class="D F">613</text><text fill="#9b6f5d" xml:space="preserve" y="637.906" x="476" class="D E">614</text><text xml:space="preserve" y="637.906" x="511" class="D E R">615</text><text xml:space="preserve" y="637.906" x="546" class="D E H">616</text><text xml:space="preserve" y="637.906" x="582" class="D F">617</text><text xml:space="preserve" y="637.906" x="615" class="D E h">618</text><text xml:space="preserve" y="637.906" x="651" class="D F">619</text><text xml:space="preserve" y="637.906" x="684" class="D E P">620</text><text xml:space="preserve" y="637.906" x="720" class="D E N">621</text><text fill="#be7384" xml:space="preserve" y="637.906" x="754" class="D E">622</text><text xml:space="preserve" y="637.906" x="788" class="D E e">623</text><text xml:space="preserve" y="637.906" x="823" class="D E K">624</text><text xml:space="preserve" y="637.906" x="857" class="D E M">625</text></g></svg>
diff --git a/examples/async/eratosthenes/eratosthenes.pyproject b/examples/async/eratosthenes/eratosthenes.pyproject
new file mode 100644
index 000000000..f0de627ef
--- /dev/null
+++ b/examples/async/eratosthenes/eratosthenes.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["eratosthenes_trio.py", "eratosthenes_asyncio.py"]
+}
diff --git a/examples/async/eratosthenes/eratosthenes_asyncio.py b/examples/async/eratosthenes/eratosthenes_asyncio.py
new file mode 100644
index 000000000..a5177a94b
--- /dev/null
+++ b/examples/async/eratosthenes/eratosthenes_asyncio.py
@@ -0,0 +1,220 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import (Qt, QEvent, QObject, QTimer, Signal, Slot)
+from PySide6.QtGui import (QColor, QFont, QPalette)
+from PySide6.QtWidgets import (QApplication, QGridLayout, QLabel, QMainWindow, QVBoxLayout, QWidget)
+
+import asyncio
+import signal
+import sys
+from random import randint
+
+
+class MainWindow(QMainWindow):
+
+ set_num = Signal(int, QColor)
+
+ def __init__(self, rows, cols):
+ super().__init__()
+
+ self.rows = rows
+ self.cols = cols
+
+ widget_central = QWidget()
+ self.setCentralWidget(widget_central)
+
+ layout_outer = QVBoxLayout(widget_central)
+
+ self.widget_outer_text = QLabel()
+ font = QFont()
+ font.setPointSize(14)
+ self.widget_outer_text.setFont(font)
+ layout_outer.addWidget(self.widget_outer_text, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ widget_inner_grid = QWidget()
+ layout_outer.addWidget(widget_inner_grid, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ self.layout_inner_grid = QGridLayout(widget_inner_grid)
+ k = 1
+ for i in range(self.rows):
+ for j in range(self.cols):
+ box = QLabel(f"{k}")
+ self.layout_inner_grid.addWidget(box, i, j, Qt.AlignmentFlag.AlignCenter)
+ k += 1
+
+ self.set_num.connect(self.set_num_handler)
+
+ @Slot(int, QColor)
+ def set_num_handler(self, i, color):
+ row = int((i - 1) / self.cols)
+ col = (i - 1) - (row * self.cols)
+ widget = self.layout_inner_grid.itemAtPosition(row, col).widget()
+
+ font = QFont()
+ font.setWeight(QFont.Bold)
+ palette = QPalette()
+ palette.setColor(QPalette.WindowText, color)
+ widget.setFont(font)
+ widget.setPalette(palette)
+
+
+class Eratosthenes(QObject):
+
+ done_signal = Signal()
+
+ """ This Sieve of Eratosthenes runs on a configurable tick (default
+ 0.1 seconds). At each tick, a new subroutine will be created
+ that will check multiples of the next prime number. Each of
+ these subroutines also operates on the same tick. """
+
+ def __init__(self, num, window, tick=0.1):
+ super().__init__()
+ self.num = num
+ self.sieve = [True] * self.num
+ self.base = 0
+ self.window = window
+ self.tick = tick
+ self.coroutines = []
+ self.done = False
+ self.loop = None
+
+ def get_tick(self):
+ return self.loop.time() + self.tick
+
+ async def start(self):
+ self.loop = asyncio.get_event_loop()
+ asyncio.create_task(self.update_text())
+ while self.base <= self.num / 2:
+ await asyncio.sleep(self.tick)
+ for i in range(self.base + 1, self.num):
+ if self.sieve[i]:
+ self.base = i
+ break
+ asyncio.create_task(self.mark_number(self.base + 1))
+ while sum(self.coroutines) > 0:
+ await asyncio.sleep(self.tick)
+ self.done = True
+
+ async def mark_number(self, base):
+ id = len(self.coroutines)
+ self.coroutines.append(1)
+ color = QColor(randint(64, 192), randint(64, 192), randint(64, 192))
+ for i in range(2 * base, self.num + 1, base):
+ if self.sieve[i - 1]:
+ self.sieve[i - 1] = False
+ self.window.set_num.emit(i, color)
+ await asyncio.sleep(self.tick)
+ self.coroutines[id] = 0
+
+ async def update_text(self):
+ while not self.done:
+ await asyncio.sleep(self.tick)
+ if int(self.loop.time() + self.tick) % 2:
+ text = "⚙️ ...Calculating prime numbers... ⚙️"
+ else:
+ text = "👩‍💻 ...Hacking the universe... 👩‍💻"
+ self.window.widget_outer_text.setText(text)
+
+ self.window.widget_outer_text.setText(
+ "🥳 Congratulations! You found all the prime numbers and solved mathematics. 🥳"
+ )
+
+ # This signals to the guest run when there are no more asyncio tasks
+ # left so its event loop can finish.
+ self.done_signal.emit()
+
+
+class AsyncHelper(QObject):
+
+ class ReenterQtObject(QObject):
+ """ This is a QObject to which an event will be posted, allowing
+ asyncio to resume when the event is handled. event.fn() is
+ the next entry point of the asyncio event loop. """
+ def event(self, event):
+ if event.type() == QEvent.Type.User + 1:
+ event.fn()
+ return True
+ return False
+
+ class ReenterQtEvent(QEvent):
+ """ This is the QEvent that will be handled by the ReenterQtObject.
+ self.fn is the next entry point of the asyncio event loop. """
+ def __init__(self, fn):
+ super().__init__(QEvent.Type(QEvent.Type.User + 1))
+ self.fn = fn
+
+ def __init__(self, worker, entry):
+ super().__init__()
+ self.reenter_qt = self.ReenterQtObject()
+ self.entry = entry
+ self.loop = asyncio.new_event_loop()
+ self.done = False
+
+ self.worker = worker
+ if hasattr(self.worker, "start_signal") and isinstance(self.worker.start_signal, Signal):
+ self.worker.start_signal.connect(self.on_worker_started)
+ if hasattr(self.worker, "done_signal") and isinstance(self.worker.done_signal, Signal):
+ self.worker.done_signal.connect(self.on_worker_done)
+
+ @Slot()
+ def on_worker_started(self):
+ """ To use asyncio and Qt together, one must run the asyncio
+ event loop as a "guest" inside the Qt "host" event loop. """
+ if not self.entry:
+ raise Exception("No entry point for the asyncio event loop was set.")
+ asyncio.set_event_loop(self.loop)
+ self.loop.create_task(self.entry())
+ self.loop.call_soon(self.next_guest_run_schedule)
+ self.done = False # Set this explicitly as we might want to restart the guest run.
+ self.loop.run_forever()
+
+ @Slot()
+ def on_worker_done(self):
+ """ When all our current asyncio tasks are finished, we must end
+ the "guest run" lest we enter a quasi idle loop of switching
+ back and forth between the asyncio and Qt loops. We can
+ launch a new guest run by calling launch_guest_run() again. """
+ self.done = True
+
+ def continue_loop(self):
+ """ This function is called by an event posted to the Qt event
+ loop to continue the asyncio event loop. """
+ if not self.done:
+ self.loop.call_soon(self.next_guest_run_schedule)
+ self.loop.run_forever()
+
+ def next_guest_run_schedule(self):
+ """ This function serves to pause and re-schedule the guest
+ (asyncio) event loop inside the host (Qt) event loop. It is
+ registered in asyncio as a callback to be called at the next
+ iteration of the event loop. When this function runs, it
+ first stops the asyncio event loop, then by posting an event
+ on the Qt event loop, it both relinquishes to Qt's event
+ loop and also schedules the asyncio event loop to run again.
+ Upon handling this event, a function will be called that
+ resumes the asyncio event loop. """
+ self.loop.stop()
+ QApplication.postEvent(self.reenter_qt, self.ReenterQtEvent(self.continue_loop))
+
+
+if __name__ == "__main__":
+ rows = 40
+ cols = 40
+ num = rows * cols
+
+ app = QApplication(sys.argv)
+ main_window = MainWindow(rows, cols)
+ eratosthenes = Eratosthenes(num, main_window)
+ async_helper = AsyncHelper(eratosthenes, eratosthenes.start)
+
+ # This establishes the entry point for the asyncio guest run. It
+ # varies depending on how and when its event loop is to be
+ # triggered, e.g., from the beginning (as here) or rather at a
+ # specific moment like a button press.
+ QTimer.singleShot(0, async_helper.on_worker_started)
+
+ main_window.show()
+
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ app.exec()
diff --git a/examples/async/eratosthenes/eratosthenes_trio.py b/examples/async/eratosthenes/eratosthenes_trio.py
new file mode 100644
index 000000000..89d3d1ce6
--- /dev/null
+++ b/examples/async/eratosthenes/eratosthenes_trio.py
@@ -0,0 +1,197 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import (Qt, QEvent, QObject, QTimer, Signal, Slot)
+from PySide6.QtGui import (QColor, QFont, QPalette)
+from PySide6.QtWidgets import (QApplication, QGridLayout, QLabel, QMainWindow, QVBoxLayout, QWidget)
+
+import outcome
+import signal
+import sys
+import traceback
+import trio
+from random import randint
+
+
+class MainWindow(QMainWindow):
+
+ set_num = Signal(int, QColor)
+
+ def __init__(self, rows, cols):
+ super().__init__()
+
+ self.rows = rows
+ self.cols = cols
+
+ widget_central = QWidget()
+ self.setCentralWidget(widget_central)
+
+ layout_outer = QVBoxLayout(widget_central)
+
+ self.widget_outer_text = QLabel()
+ font = QFont()
+ font.setPointSize(14)
+ self.widget_outer_text.setFont(font)
+ layout_outer.addWidget(self.widget_outer_text, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ widget_inner_grid = QWidget()
+ layout_outer.addWidget(widget_inner_grid, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ self.layout_inner_grid = QGridLayout(widget_inner_grid)
+ k = 1
+ for i in range(self.rows):
+ for j in range(self.cols):
+ box = QLabel(f"{k}")
+ self.layout_inner_grid.addWidget(box, i, j, Qt.AlignmentFlag.AlignCenter)
+ k += 1
+
+ self.set_num.connect(self.set_num_handler)
+
+ @Slot(int, QColor)
+ def set_num_handler(self, i, color):
+ row = int((i - 1) / self.cols)
+ col = (i - 1) - (row * self.cols)
+ widget = self.layout_inner_grid.itemAtPosition(row, col).widget()
+
+ font = QFont()
+ font.setWeight(QFont.Bold)
+ palette = QPalette()
+ palette.setColor(QPalette.WindowText, color)
+ widget.setFont(font)
+ widget.setPalette(palette)
+
+
+class Eratosthenes(QObject):
+
+ """ This Sieve of Eratosthenes runs on a configurable tick (default
+ 0.1 seconds). At each tick, a new subroutine will be created
+ that will check multiples of the next prime number. Each of
+ these subroutines also operates on the same tick. """
+
+ def __init__(self, num, window, tick=0.1):
+ super().__init__()
+ self.num = num
+ self.sieve = [True] * self.num
+ self.base = 0
+ self.window = window
+ self.tick = tick
+ self.coroutines = []
+ self.done = False
+ self.nursery = None
+
+ async def start(self):
+ async with trio.open_nursery() as self.nursery:
+ self.nursery.start_soon(self.update_text)
+ while self.base <= self.num / 2:
+ await trio.sleep(self.tick)
+ for i in range(self.base + 1, self.num):
+ if self.sieve[i]:
+ self.base = i
+ break
+ self.nursery.start_soon(self.mark_number, self.base + 1)
+ while sum(self.coroutines) > 0:
+ await trio.sleep(self.tick)
+ self.done = True
+
+ async def mark_number(self, base):
+ id = len(self.coroutines)
+ self.coroutines.append(1)
+ color = QColor(randint(64, 192), randint(64, 192), randint(64, 192))
+ for i in range(2 * base, self.num + 1, base):
+ if self.sieve[i - 1]:
+ self.sieve[i - 1] = False
+ self.window.set_num.emit(i, color)
+ await trio.sleep(self.tick)
+ self.coroutines[id] = 0
+
+ async def update_text(self):
+ while not self.done:
+ await trio.sleep(self.tick)
+ if int(trio.lowlevel.current_clock().current_time() + self.tick) % 2:
+ text = "⚙️ ...Calculating prime numbers... ⚙️"
+ else:
+ text = "👩‍💻 ...Hacking the universe... 👩‍💻"
+ self.window.widget_outer_text.setText(text)
+
+ self.window.widget_outer_text.setText(
+ "🥳 Congratulations! You found all the prime numbers and solved mathematics. 🥳"
+ )
+
+
+class AsyncHelper(QObject):
+
+ class ReenterQtObject(QObject):
+ """ This is a QObject to which an event will be posted, allowing
+ Trio to resume when the event is handled. event.fn() is the
+ next entry point of the Trio event loop. """
+ def event(self, event):
+ if event.type() == QEvent.Type.User + 1:
+ event.fn()
+ return True
+ return False
+
+ class ReenterQtEvent(QEvent):
+ """ This is the QEvent that will be handled by the ReenterQtObject.
+ self.fn is the next entry point of the Trio event loop. """
+ def __init__(self, fn):
+ super().__init__(QEvent.Type(QEvent.Type.User + 1))
+ self.fn = fn
+
+ def __init__(self, worker, entry):
+ super().__init__()
+ self.reenter_qt = self.ReenterQtObject()
+ self.entry = entry
+
+ self.worker = worker
+ if hasattr(self.worker, "start_signal") and isinstance(self.worker.start_signal, Signal):
+ self.worker.start_signal.connect(self.launch_guest_run)
+
+ @Slot()
+ def launch_guest_run(self):
+ """ To use Trio and Qt together, one must run the Trio event
+ loop as a "guest" inside the Qt "host" event loop. """
+ if not self.entry:
+ raise Exception("No entry point for the Trio guest run was set.")
+ trio.lowlevel.start_guest_run(
+ self.entry,
+ run_sync_soon_threadsafe=self.next_guest_run_schedule,
+ done_callback=self.trio_done_callback,
+ )
+
+ def next_guest_run_schedule(self, fn):
+ """ This function serves to re-schedule the guest (Trio) event
+ loop inside the host (Qt) event loop. It is called by Trio
+ at the end of an event loop run in order to relinquish back
+ to Qt's event loop. By posting an event on the Qt event loop
+ that contains Trio's next entry point, it ensures that Trio's
+ event loop will be scheduled again by Qt. """
+ QApplication.postEvent(self.reenter_qt, self.ReenterQtEvent(fn))
+
+ def trio_done_callback(self, outcome_):
+ """ This function is called by Trio when its event loop has
+ finished. """
+ if isinstance(outcome_, outcome.Error):
+ error = outcome_.error
+ traceback.print_exception(type(error), error, error.__traceback__)
+
+
+if __name__ == "__main__":
+ rows = 40
+ cols = 40
+ num = rows * cols
+
+ app = QApplication(sys.argv)
+ main_window = MainWindow(rows, cols)
+ eratosthenes = Eratosthenes(num, main_window)
+ async_helper = AsyncHelper(eratosthenes, eratosthenes.start)
+
+ # This establishes the entry point for the Trio guest run. It varies
+ # depending on how and when its event loop is to be triggered, e.g.,
+ # from the beginning (as here) or rather at a specific moment like
+ # a button press.
+ QTimer.singleShot(0, async_helper.launch_guest_run)
+
+ main_window.show()
+
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ app.exec()
diff --git a/examples/async/eratosthenes/requirements_trio.txt b/examples/async/eratosthenes/requirements_trio.txt
new file mode 100644
index 000000000..e2cc10204
--- /dev/null
+++ b/examples/async/eratosthenes/requirements_trio.txt
@@ -0,0 +1,2 @@
+trio
+outcome
diff --git a/examples/async/minimal/doc/minimal.png b/examples/async/minimal/doc/minimal.png
new file mode 100644
index 000000000..b8a18963f
--- /dev/null
+++ b/examples/async/minimal/doc/minimal.png
Binary files differ
diff --git a/examples/async/minimal/doc/minimal.rst b/examples/async/minimal/doc/minimal.rst
new file mode 100644
index 000000000..5a1cf8544
--- /dev/null
+++ b/examples/async/minimal/doc/minimal.rst
@@ -0,0 +1,52 @@
+Async examples
+==============
+
+The Python language provides keywords for asynchronous operations, i.e.,
+"async" to define coroutines or "await" to schedule asynchronous calls in the
+event loop (see `PEP 492 <https://peps.python.org/pep-0492/>`_). It is up to
+packages to implement an event loop, support for these keywords, and more.
+
+The best-known package for this is `asyncio`. Since both an async package and
+Qt itself work with event loops, special care must be taken to ensure that both
+event loops work with each other. asyncio offers a function `stop` that allows
+stopping an event loop without closing it. If it is called while a loop is
+running through `run_forever`, the loop will run the current batch of callbacks
+and then exit. New callbacks wil be scheduled the next time `run_forever` is
+called.
+
+This approach is highly experimental and does not represent the state of the
+art of integrating Qt with asyncio. Instead it should rather be regarded more
+as a proof of concept to contrast asyncio with other async packages such as
+`trio`, which offers a dedicated `low-level API
+<https://trio.readthedocs.io/en/stable/reference-lowlevel.html>`_ for more
+complicated use cases such as this. Specifically, there exists a function
+`start_guest_run` that enables running the Trio event loop as a "guest" inside
+another event loop - Qt's in our case.
+
+Based on this functionality, two examples for async usage with Qt have been
+implemented: `eratosthenes` and `minimal`:
+
+.. image:: minimal.png
+ :alt: Async example: Minimal
+
+* `eratosthenes` is a more extensive example that visualizes the Sieve of
+ Eratosthenes algorithm. This algorithm per se is not one that is particularly
+ suitable for asynchronous operations as it's not I/O-heavy, but synchronizing
+ coroutines to a configurable tick allows for a good visualization.
+* `minimal` is a minimal example featuring a button that triggers an
+ asynchronous coroutine with a sleep. It is designed to highlight which
+ boilerplate code is essential for an async program with Qt and offers a
+ starting point for more complex programs.
+
+Both examples feature:
+
+1. A window class.
+2. An `AsyncHelper` class containing `start_guest_run` plus helpers and
+ callbacks necessary for its invocation. The entry point for the Trio/asyncio
+ guest run is provided as an argument from outside, which can be any async
+ function.
+
+While `eratosthenes` offloads the asynchronous logic that will run in
+trio's/asyncio's event loop into a separate class, `minimal` demonstrates that
+async functions can be integrated into any class, including subclasses of Qt
+classes.
diff --git a/examples/async/minimal/minimal.pyproject b/examples/async/minimal/minimal.pyproject
new file mode 100644
index 000000000..b4b1a3f80
--- /dev/null
+++ b/examples/async/minimal/minimal.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["minimal_trio.py", "minimal_asyncio.py"]
+}
diff --git a/examples/async/minimal/minimal_asyncio.py b/examples/async/minimal/minimal_asyncio.py
new file mode 100644
index 000000000..80c81da3b
--- /dev/null
+++ b/examples/async/minimal/minimal_asyncio.py
@@ -0,0 +1,123 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import (Qt, QEvent, QObject, Signal, Slot)
+from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
+
+import asyncio
+import signal
+import sys
+
+
+class MainWindow(QMainWindow):
+
+ start_signal = Signal()
+ done_signal = Signal()
+
+ def __init__(self):
+ super().__init__()
+
+ widget = QWidget()
+ self.setCentralWidget(widget)
+
+ layout = QVBoxLayout(widget)
+
+ self.text = QLabel("The answer is 42.")
+ layout.addWidget(self.text, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ async_trigger = QPushButton(text="What is the question?")
+ async_trigger.clicked.connect(self.async_start)
+ layout.addWidget(async_trigger, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ @Slot()
+ def async_start(self):
+ self.start_signal.emit()
+
+ async def set_text(self):
+ await asyncio.sleep(1)
+ self.text.setText("What do you get if you multiply six by nine?")
+ self.done_signal.emit()
+
+
+class AsyncHelper(QObject):
+
+ class ReenterQtObject(QObject):
+ """ This is a QObject to which an event will be posted, allowing
+ asyncio to resume when the event is handled. event.fn() is
+ the next entry point of the asyncio event loop. """
+ def event(self, event):
+ if event.type() == QEvent.Type.User + 1:
+ event.fn()
+ return True
+ return False
+
+ class ReenterQtEvent(QEvent):
+ """ This is the QEvent that will be handled by the ReenterQtObject.
+ self.fn is the next entry point of the asyncio event loop. """
+ def __init__(self, fn):
+ super().__init__(QEvent.Type(QEvent.Type.User + 1))
+ self.fn = fn
+
+ def __init__(self, worker, entry):
+ super().__init__()
+ self.reenter_qt = self.ReenterQtObject()
+ self.entry = entry
+ self.loop = asyncio.new_event_loop()
+ self.done = False
+
+ self.worker = worker
+ if hasattr(self.worker, "start_signal") and isinstance(self.worker.start_signal, Signal):
+ self.worker.start_signal.connect(self.on_worker_started)
+ if hasattr(self.worker, "done_signal") and isinstance(self.worker.done_signal, Signal):
+ self.worker.done_signal.connect(self.on_worker_done)
+
+ @Slot()
+ def on_worker_started(self):
+ """ To use asyncio and Qt together, one must run the asyncio
+ event loop as a "guest" inside the Qt "host" event loop. """
+ if not self.entry:
+ raise Exception("No entry point for the asyncio event loop was set.")
+ asyncio.set_event_loop(self.loop)
+ self.loop.create_task(self.entry())
+ self.loop.call_soon(self.next_guest_run_schedule)
+ self.done = False # Set this explicitly as we might want to restart the guest run.
+ self.loop.run_forever()
+
+ @Slot()
+ def on_worker_done(self):
+ """ When all our current asyncio tasks are finished, we must end
+ the "guest run" lest we enter a quasi idle loop of switching
+ back and forth between the asyncio and Qt loops. We can
+ launch a new guest run by calling launch_guest_run() again. """
+ self.done = True
+
+ def continue_loop(self):
+ """ This function is called by an event posted to the Qt event
+ loop to continue the asyncio event loop. """
+ if not self.done:
+ self.loop.call_soon(self.next_guest_run_schedule)
+ self.loop.run_forever()
+
+ def next_guest_run_schedule(self):
+ """ This function serves to pause and re-schedule the guest
+ (asyncio) event loop inside the host (Qt) event loop. It is
+ registered in asyncio as a callback to be called at the next
+ iteration of the event loop. When this function runs, it
+ first stops the asyncio event loop, then by posting an event
+ on the Qt event loop, it both relinquishes to Qt's event
+ loop and also schedules the asyncio event loop to run again.
+ Upon handling this event, a function will be called that
+ resumes the asyncio event loop. """
+ self.loop.stop()
+ QApplication.postEvent(self.reenter_qt, self.ReenterQtEvent(self.continue_loop))
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ main_window = MainWindow()
+ async_helper = AsyncHelper(main_window, main_window.set_text)
+
+ main_window.show()
+
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ app.exec()
diff --git a/examples/async/minimal/minimal_trio.py b/examples/async/minimal/minimal_trio.py
new file mode 100644
index 000000000..5ae19fa36
--- /dev/null
+++ b/examples/async/minimal/minimal_trio.py
@@ -0,0 +1,107 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import (Qt, QEvent, QObject, Signal, Slot)
+from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
+
+import outcome
+import signal
+import sys
+import traceback
+import trio
+
+
+class MainWindow(QMainWindow):
+
+ start_signal = Signal()
+
+ def __init__(self):
+ super().__init__()
+
+ widget = QWidget()
+ self.setCentralWidget(widget)
+
+ layout = QVBoxLayout(widget)
+
+ self.text = QLabel("The answer is 42.")
+ layout.addWidget(self.text, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ async_trigger = QPushButton(text="What is the question?")
+ async_trigger.clicked.connect(self.async_start)
+ layout.addWidget(async_trigger, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ @Slot()
+ def async_start(self):
+ self.start_signal.emit()
+
+ async def set_text(self):
+ await trio.sleep(1)
+ self.text.setText("What do you get if you multiply six by nine?")
+
+
+class AsyncHelper(QObject):
+
+ class ReenterQtObject(QObject):
+ """ This is a QObject to which an event will be posted, allowing
+ Trio to resume when the event is handled. event.fn() is the
+ next entry point of the Trio event loop. """
+ def event(self, event):
+ if event.type() == QEvent.Type.User + 1:
+ event.fn()
+ return True
+ return False
+
+ class ReenterQtEvent(QEvent):
+ """ This is the QEvent that will be handled by the ReenterQtObject.
+ self.fn is the next entry point of the Trio event loop. """
+ def __init__(self, fn):
+ super().__init__(QEvent.Type(QEvent.Type.User + 1))
+ self.fn = fn
+
+ def __init__(self, worker, entry):
+ super().__init__()
+ self.reenter_qt = self.ReenterQtObject()
+ self.entry = entry
+
+ self.worker = worker
+ if hasattr(self.worker, "start_signal") and isinstance(self.worker.start_signal, Signal):
+ self.worker.start_signal.connect(self.launch_guest_run)
+
+ @Slot()
+ def launch_guest_run(self):
+ """ To use Trio and Qt together, one must run the Trio event
+ loop as a "guest" inside the Qt "host" event loop. """
+ if not self.entry:
+ raise Exception("No entry point for the Trio guest run was set.")
+ trio.lowlevel.start_guest_run(
+ self.entry,
+ run_sync_soon_threadsafe=self.next_guest_run_schedule,
+ done_callback=self.trio_done_callback,
+ )
+
+ def next_guest_run_schedule(self, fn):
+ """ This function serves to re-schedule the guest (Trio) event
+ loop inside the host (Qt) event loop. It is called by Trio
+ at the end of an event loop run in order to relinquish back
+ to Qt's event loop. By posting an event on the Qt event loop
+ that contains Trio's next entry point, it ensures that Trio's
+ event loop will be scheduled again by Qt. """
+ QApplication.postEvent(self.reenter_qt, self.ReenterQtEvent(fn))
+
+ def trio_done_callback(self, outcome_):
+ """ This function is called by Trio when its event loop has
+ finished. """
+ if isinstance(outcome_, outcome.Error):
+ error = outcome_.error
+ traceback.print_exception(type(error), error, error.__traceback__)
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ main_window = MainWindow()
+ async_helper = AsyncHelper(main_window, main_window.set_text)
+
+ main_window.show()
+
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ app.exec()
diff --git a/examples/async/minimal/requirements_trio.txt b/examples/async/minimal/requirements_trio.txt
new file mode 100644
index 000000000..ae0d704f0
--- /dev/null
+++ b/examples/async/minimal/requirements_trio.txt
@@ -0,0 +1 @@
+trio
diff --git a/examples/bluetooth/btscanner/device.py b/examples/bluetooth/btscanner/device.py
index daf0ab456..e51160b84 100644
--- a/examples/bluetooth/btscanner/device.py
+++ b/examples/bluetooth/btscanner/device.py
@@ -3,7 +3,7 @@
from PySide6.QtCore import QPoint, Qt, Slot
from PySide6.QtGui import QColor
-from PySide6.QtWidgets import QDialog, QListWidgetItem, QListWidget, QMenu
+from PySide6.QtWidgets import QDialog, QListWidgetItem, QMenu
from PySide6.QtBluetooth import (QBluetoothAddress, QBluetoothDeviceDiscoveryAgent,
QBluetoothDeviceInfo, QBluetoothLocalDevice)
@@ -88,7 +88,7 @@ class DeviceDiscoveryDialog(QDialog):
else:
self._local_device.setHostMode(QBluetoothLocalDevice.HostPoweredOff)
- @Slot(QBluetoothLocalDevice.HostMode)
+ @Slot("QBluetoothLocalDevice::HostMode")
def host_mode_state_changed(self, mode):
self._ui.power.setChecked(mode != QBluetoothLocalDevice.HostPoweredOff)
self._ui.discoverable.setChecked(mode == QBluetoothLocalDevice.HostDiscoverable)
@@ -118,7 +118,7 @@ class DeviceDiscoveryDialog(QDialog):
elif chosen_action == remove_pair_action:
self._local_device.requestPairing(address, QBluetoothLocalDevice.Unpaired)
- @Slot(QBluetoothAddress, QBluetoothLocalDevice.Pairing)
+ @Slot(QBluetoothAddress, "QBluetoothLocalDevice::Pairing")
def pairing_done(self, address, pairing):
items = self._ui.list.findItems(address.toString(), Qt.MatchContains)
diff --git a/examples/bluetooth/btscanner/main.py b/examples/bluetooth/btscanner/main.py
index bf8f596a3..a54a862a2 100644
--- a/examples/bluetooth/btscanner/main.py
+++ b/examples/bluetooth/btscanner/main.py
@@ -5,8 +5,7 @@
import sys
-from PySide6.QtCore import Qt
-from PySide6.QtWidgets import QApplication, QWidget
+from PySide6.QtWidgets import QApplication
from device import DeviceDiscoveryDialog
diff --git a/examples/bluetooth/btscanner/service.py b/examples/bluetooth/btscanner/service.py
index 73e174b82..31df8a9ea 100644
--- a/examples/bluetooth/btscanner/service.py
+++ b/examples/bluetooth/btscanner/service.py
@@ -1,7 +1,7 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-from PySide6.QtCore import Qt, Slot
+from PySide6.QtCore import Slot
from PySide6.QtWidgets import QDialog
from PySide6.QtBluetooth import (QBluetoothAddress, QBluetoothServiceInfo,
QBluetoothServiceDiscoveryAgent, QBluetoothLocalDevice)
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/App.qml b/examples/bluetooth/heartrate_game/HeartRateGame/App.qml
new file mode 100644
index 000000000..db6aa7145
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/App.qml
@@ -0,0 +1,99 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import HeartRateGame
+
+Item {
+ id: app
+
+ required property ConnectionHandler connectionHandler
+ required property DeviceFinder deviceFinder
+ required property DeviceHandler deviceHandler
+
+ anchors.fill: parent
+ opacity: 0.0
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: 500
+ }
+ }
+
+ property int __currentIndex: 0
+
+ TitleBar {
+ id: titleBar
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ currentIndex: app.__currentIndex
+
+ onTitleClicked: (index) => {
+ if (index < app.__currentIndex)
+ app.__currentIndex = index
+ }
+ }
+
+ StackLayout {
+ id: pageStack
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: titleBar.bottom
+ anchors.bottom: parent.bottom
+ currentIndex: app.__currentIndex
+
+ Connect {
+ connectionHandler: app.connectionHandler
+ deviceFinder: app.deviceFinder
+ deviceHandler: app.deviceHandler
+
+ onShowMeasurePage: app.__currentIndex = 1
+ }
+ Measure {
+ id: measurePage
+ deviceHandler: app.deviceHandler
+
+ onShowStatsPage: app.__currentIndex = 2
+ }
+ Stats {
+ deviceHandler: app.deviceHandler
+ }
+
+ onCurrentIndexChanged: {
+ if (currentIndex === 0)
+ measurePage.close()
+ }
+ }
+
+ BluetoothAlarmDialog {
+ id: btAlarmDialog
+ anchors.fill: parent
+ visible: !app.connectionHandler.alive || permissionError
+ permissionError: !app.connectionHandler.hasPermission
+ }
+
+ Keys.onReleased: (event) => {
+ switch (event.key) {
+ case Qt.Key_Escape:
+ case Qt.Key_Back:
+ {
+ if (app.__currentIndex > 0) {
+ app.__currentIndex = app.__currentIndex - 1
+ event.accepted = true
+ } else {
+ Qt.quit()
+ }
+ break
+ }
+ default:
+ break
+ }
+ }
+
+ Component.onCompleted: {
+ forceActiveFocus()
+ app.opacity = 1.0
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/BluetoothAlarmDialog.qml b/examples/bluetooth/heartrate_game/HeartRateGame/BluetoothAlarmDialog.qml
new file mode 100644
index 000000000..3687b1331
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/BluetoothAlarmDialog.qml
@@ -0,0 +1,79 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: root
+
+ property bool permissionError: false
+
+ anchors.fill: parent
+
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
+ opacity: 0.9
+ }
+
+ MouseArea {
+ id: eventEater
+ }
+
+ Rectangle {
+ id: dialogFrame
+
+ anchors.centerIn: parent
+ width: parent.width * 0.8
+ height: parent.height * 0.6
+ border.color: "#454545"
+ color: GameSettings.backgroundColor
+ radius: width * 0.05
+
+ Item {
+ id: dialogContainer
+ anchors.fill: parent
+ anchors.margins: parent.width*0.05
+
+ Image {
+ id: offOnImage
+ anchors.left: quitButton.left
+ anchors.right: quitButton.right
+ anchors.top: parent.top
+ height: GameSettings.heightForWidth(width, sourceSize)
+ source: "images/bt_off_to_on.png"
+ }
+
+ Text {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: offOnImage.bottom
+ anchors.bottom: quitButton.top
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.WordWrap
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
+ text: root.permissionError
+ ? qsTr("Bluetooth permissions are not granted. Please grant the permissions in the system settings.")
+ : qsTr("This application cannot be used without Bluetooth. Please switch Bluetooth ON to continue.")
+ }
+
+ GameButton {
+ id: quitButton
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: dialogContainer.width * 0.6
+ height: GameSettings.buttonHeight
+ onClicked: Qt.quit()
+
+ Text {
+ anchors.centerIn: parent
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.bigFontSize
+ text: qsTr("Quit")
+ }
+ }
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/BottomLine.qml b/examples/bluetooth/heartrate_game/HeartRateGame/BottomLine.qml
new file mode 100644
index 000000000..caebc307e
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/BottomLine.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ width: parent.width * 0.85
+ height: parent.height * 0.05
+ radius: height*0.5
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/Connect.qml b/examples/bluetooth/heartrate_game/HeartRateGame/Connect.qml
new file mode 100644
index 000000000..ca8ef2923
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/Connect.qml
@@ -0,0 +1,159 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+import QtQuick
+import HeartRateGame
+
+GamePage {
+ id: connectPage
+
+ required property ConnectionHandler connectionHandler
+ required property DeviceFinder deviceFinder
+ required property DeviceHandler deviceHandler
+
+ signal showMeasurePage
+
+ errorMessage: deviceFinder.error
+ infoMessage: deviceFinder.info
+
+ Rectangle {
+ id: viewContainer
+ anchors.top: parent.top
+ // only BlueZ platform has address type selection
+ anchors.bottom: connectPage.connectionHandler.requiresAddressType ? addressTypeButton.top
+ : searchButton.top
+ anchors.topMargin: GameSettings.fieldMargin + connectPage.messageHeight
+ anchors.bottomMargin: GameSettings.fieldMargin
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width - GameSettings.fieldMargin * 2
+ color: GameSettings.viewColor
+ radius: GameSettings.buttonRadius
+
+ Text {
+ id: title
+ width: parent.width
+ height: GameSettings.fieldHeight
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.mediumFontSize
+ text: qsTr("FOUND DEVICES")
+
+ BottomLine {
+ height: 1
+ width: parent.width
+ color: "#898989"
+ }
+ }
+
+ ListView {
+ id: devices
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.top: title.bottom
+ model: connectPage.deviceFinder.devices
+ clip: true
+
+ delegate: Rectangle {
+ id: box
+
+ required property int index
+ required property var modelData
+
+ height: GameSettings.fieldHeight * 1.2
+ width: devices.width
+ color: index % 2 === 0 ? GameSettings.delegate1Color : GameSettings.delegate2Color
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ connectPage.deviceFinder.connectToService(box.modelData.deviceAddress)
+ connectPage.showMeasurePage()
+ }
+ }
+
+ Text {
+ id: device
+ font.pixelSize: GameSettings.smallFontSize
+ text: box.modelData.deviceName
+ anchors.top: parent.top
+ anchors.topMargin: parent.height * 0.1
+ anchors.leftMargin: parent.height * 0.1
+ anchors.left: parent.left
+ color: GameSettings.textColor
+ }
+
+ Text {
+ id: deviceAddress
+ font.pixelSize: GameSettings.smallFontSize
+ text: box.modelData.deviceAddress
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: parent.height * 0.1
+ anchors.rightMargin: parent.height * 0.1
+ anchors.right: parent.right
+ color: Qt.darker(GameSettings.textColor)
+ }
+ }
+ }
+ }
+
+ GameButton {
+ id: addressTypeButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: searchButton.top
+ anchors.bottomMargin: GameSettings.fieldMargin * 0.5
+ width: viewContainer.width
+ height: GameSettings.fieldHeight
+ visible: connectPage.connectionHandler.requiresAddressType // only required on BlueZ
+ state: "public"
+ onClicked: state === "public" ? state = "random" : state = "public"
+
+ states: [
+ State {
+ name: "public"
+ PropertyChanges {
+ addressTypeText.text: qsTr("Public Address")
+ }
+ PropertyChanges {
+ connectPage.deviceHandler.addressType: DeviceHandler.PUBLIC_ADDRESS
+ }
+ },
+ State {
+ name: "random"
+ PropertyChanges {
+ addressTypeText.text: qsTr("Random Address")
+ }
+ PropertyChanges {
+ connectPage.deviceHandler.addressType: DeviceHandler.RANDOM_ADDRESS
+ }
+ }
+ ]
+
+ Text {
+ id: addressTypeText
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ color: GameSettings.textColor
+ }
+ }
+
+ GameButton {
+ id: searchButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: GameSettings.fieldMargin
+ width: viewContainer.width
+ height: GameSettings.fieldHeight
+ enabled: !connectPage.deviceFinder.scanning
+ onClicked: connectPage.deviceFinder.startSearch()
+
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ text: qsTr("START SEARCH")
+ color: searchButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/GameButton.qml b/examples/bluetooth/heartrate_game/HeartRateGame/GameButton.qml
new file mode 100644
index 000000000..8e8760102
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/GameButton.qml
@@ -0,0 +1,39 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: button
+ color: baseColor
+ onEnabledChanged: checkColor()
+ radius: GameSettings.buttonRadius
+
+ property color baseColor: GameSettings.buttonColor
+ property color pressedColor: GameSettings.buttonPressedColor
+ property color disabledColor: GameSettings.disabledButtonColor
+
+ signal clicked
+
+ function checkColor() {
+ if (!button.enabled) {
+ button.color = disabledColor
+ } else {
+ if (mouseArea.containsPress)
+ button.color = pressedColor
+ else
+ button.color = baseColor
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onPressed: button.checkColor()
+ onReleased: button.checkColor()
+ onClicked: {
+ button.checkColor()
+ button.clicked()
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/GamePage.qml b/examples/bluetooth/heartrate_game/HeartRateGame/GamePage.qml
new file mode 100644
index 000000000..249f94186
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/GamePage.qml
@@ -0,0 +1,36 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ id: page
+
+ property string errorMessage: ""
+ property string infoMessage: ""
+ property real messageHeight: msg.height
+ property bool hasError: errorMessage != ""
+ property bool hasInfo: infoMessage != ""
+
+ Rectangle {
+ id: msg
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: GameSettings.fieldHeight
+ color: page.hasError ? GameSettings.errorColor : GameSettings.infoColor
+ visible: page.hasError || page.hasInfo
+
+ Text {
+ id: error
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ minimumPixelSize: 5
+ font.pixelSize: GameSettings.smallFontSize
+ fontSizeMode: Text.Fit
+ color: GameSettings.textColor
+ text: page.hasError ? page.errorMessage : page.infoMessage
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/GameSettings.qml b/examples/bluetooth/heartrate_game/HeartRateGame/GameSettings.qml
new file mode 100644
index 000000000..0fe854609
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/GameSettings.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma Singleton
+import QtQuick
+
+Item {
+ property int wHeight
+ property int wWidth
+
+ // Colors
+ readonly property color backgroundColor: "#2d3037"
+ readonly property color buttonColor: "#202227"
+ readonly property color buttonPressedColor: "#6ccaf2"
+ readonly property color disabledButtonColor: "#555555"
+ readonly property color viewColor: "#202227"
+ readonly property color delegate1Color: Qt.darker(viewColor, 1.2)
+ readonly property color delegate2Color: Qt.lighter(viewColor, 1.2)
+ readonly property color textColor: "#ffffff"
+ readonly property color textDarkColor: "#232323"
+ readonly property color disabledTextColor: "#777777"
+ readonly property color sliderColor: "#6ccaf2"
+ readonly property color errorColor: "#ba3f62"
+ readonly property color infoColor: "#3fba62"
+
+ // Font sizes
+ property real microFontSize: hugeFontSize * 0.2
+ property real tinyFontSize: hugeFontSize * 0.4
+ property real smallTinyFontSize: hugeFontSize * 0.5
+ property real smallFontSize: hugeFontSize * 0.6
+ property real mediumFontSize: hugeFontSize * 0.7
+ property real bigFontSize: hugeFontSize * 0.8
+ property real largeFontSize: hugeFontSize * 0.9
+ property real hugeFontSize: (wWidth + wHeight) * 0.03
+ property real giganticFontSize: (wWidth + wHeight) * 0.04
+
+ // Some other values
+ property real fieldHeight: wHeight * 0.08
+ property real fieldMargin: fieldHeight * 0.5
+ property real buttonHeight: wHeight * 0.08
+ property real buttonRadius: buttonHeight * 0.1
+
+ // Some help functions
+ function widthForHeight(h, ss) {
+ return h / ss.height * ss.width
+ }
+
+ function heightForWidth(w, ss) {
+ return w / ss.width * ss.height
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/Main.qml b/examples/bluetooth/heartrate_game/HeartRateGame/Main.qml
new file mode 100644
index 000000000..e26f9b004
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/Main.qml
@@ -0,0 +1,71 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+import QtQuick
+import QtQuick.Window
+import HeartRateGame
+
+Window {
+ id: wroot
+ visible: true
+ width: 720 * .7
+ height: 1240 * .7
+ title: qsTr("HeartRateGame")
+ color: GameSettings.backgroundColor
+
+ required property ConnectionHandler connectionHandler
+ required property DeviceFinder deviceFinder
+ required property DeviceHandler deviceHandler
+
+ Component.onCompleted: {
+ GameSettings.wWidth = Qt.binding(function () {
+ return width
+ })
+ GameSettings.wHeight = Qt.binding(function () {
+ return height
+ })
+ }
+
+ Loader {
+ id: splashLoader
+ anchors.fill: parent
+ asynchronous: false
+ visible: true
+
+ sourceComponent: SplashScreen {
+ appIsReady: appLoader.status === Loader.Ready
+ onReadyChanged: {
+ if (ready) {
+ appLoader.visible = true
+ splashLoader.visible = false
+ splashLoader.active = false
+ }
+ }
+ }
+
+ onStatusChanged: {
+ if (status === Loader.Ready)
+ appLoader.active = true
+ }
+ }
+
+ Loader {
+ id: appLoader
+ anchors.fill: parent
+ active: false
+ asynchronous: true
+ visible: false
+
+ sourceComponent: App {
+ connectionHandler: wroot.connectionHandler
+ deviceFinder: wroot.deviceFinder
+ deviceHandler: wroot.deviceHandler
+ }
+
+ onStatusChanged: {
+ if (status === Loader.Error)
+ Qt.quit()
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/Measure.qml b/examples/bluetooth/heartrate_game/HeartRateGame/Measure.qml
new file mode 100644
index 000000000..48e84e762
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/Measure.qml
@@ -0,0 +1,212 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import HeartRateGame
+
+GamePage {
+ id: measurePage
+
+ required property DeviceHandler deviceHandler
+
+ errorMessage: deviceHandler.error
+ infoMessage: deviceHandler.info
+
+ property real __timeCounter: 0
+ property real __maxTimeCount: 60
+ property string relaxText: qsTr("Relax!\nWhen you are ready, press Start. You have %1s time to increase heartrate so much as possible.\nGood luck!").arg(__maxTimeCount)
+
+ signal showStatsPage
+
+ function close() {
+ deviceHandler.stopMeasurement()
+ deviceHandler.disconnectService()
+ }
+
+ function start() {
+ if (!deviceHandler.measuring) {
+ __timeCounter = 0
+ deviceHandler.startMeasurement()
+ }
+ }
+
+ function stop() {
+ if (deviceHandler.measuring)
+ deviceHandler.stopMeasurement()
+
+ measurePage.showStatsPage()
+ }
+
+ Timer {
+ id: measureTimer
+ interval: 1000
+ running: measurePage.deviceHandler.measuring
+ repeat: true
+ onTriggered: {
+ measurePage.__timeCounter++
+ if (measurePage.__timeCounter >= measurePage.__maxTimeCount)
+ measurePage.stop()
+ }
+ }
+
+ Column {
+ anchors.centerIn: parent
+ spacing: GameSettings.fieldHeight * 0.5
+
+ Rectangle {
+ id: circle
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: Math.min(measurePage.width, measurePage.height - GameSettings.fieldHeight * 4)
+ - 2 * GameSettings.fieldMargin
+ height: width
+ radius: width * 0.5
+ color: GameSettings.viewColor
+
+ Text {
+ id: hintText
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: -parent.height * 0.1
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ width: parent.width * 0.8
+ height: parent.height * 0.6
+ wrapMode: Text.WordWrap
+ text: measurePage.relaxText
+ visible: !measurePage.deviceHandler.measuring
+ color: GameSettings.textColor
+ fontSizeMode: Text.Fit
+ minimumPixelSize: 10
+ font.pixelSize: GameSettings.mediumFontSize
+ }
+
+ Text {
+ id: text
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: -parent.height * 0.15
+ font.pixelSize: parent.width * 0.45
+ text: measurePage.deviceHandler.hr
+ visible: measurePage.deviceHandler.measuring
+ color: GameSettings.textColor
+ }
+
+ Item {
+ id: minMaxContainer
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width * 0.7
+ height: parent.height * 0.15
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: parent.height * 0.16
+ visible: measurePage.deviceHandler.measuring
+
+ Text {
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ text: measurePage.deviceHandler.minHR
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.hugeFontSize
+
+ Text {
+ anchors.left: parent.left
+ anchors.bottom: parent.top
+ font.pixelSize: parent.font.pixelSize * 0.8
+ color: parent.color
+ text: "MIN"
+ }
+ }
+
+ Text {
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ text: measurePage.deviceHandler.maxHR
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.hugeFontSize
+
+ Text {
+ anchors.right: parent.right
+ anchors.bottom: parent.top
+ font.pixelSize: parent.font.pixelSize * 0.8
+ color: parent.color
+ text: "MAX"
+ }
+ }
+ }
+
+ Image {
+ id: heart
+ anchors.horizontalCenter: minMaxContainer.horizontalCenter
+ anchors.verticalCenter: minMaxContainer.bottom
+ width: parent.width * 0.2
+ height: width
+ source: "images/heart.png"
+ smooth: true
+ antialiasing: true
+
+ SequentialAnimation {
+ id: heartAnim
+ running: measurePage.deviceHandler.alive
+ loops: Animation.Infinite
+ alwaysRunToEnd: true
+ PropertyAnimation {
+ target: heart
+ property: "scale"
+ to: 1.2
+ duration: 500
+ easing.type: Easing.InQuad
+ }
+ PropertyAnimation {
+ target: heart
+ property: "scale"
+ to: 1.0
+ duration: 500
+ easing.type: Easing.OutQuad
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: timeSlider
+ color: GameSettings.viewColor
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: circle.width
+ height: GameSettings.fieldHeight
+ radius: GameSettings.buttonRadius
+
+ Rectangle {
+ height: parent.height
+ radius: parent.radius
+ color: GameSettings.sliderColor
+ width: Math.min(
+ 1.0,
+ measurePage.__timeCounter / measurePage.__maxTimeCount) * parent.width
+ }
+
+ Text {
+ anchors.centerIn: parent
+ color: "gray"
+ text: (measurePage.__maxTimeCount - measurePage.__timeCounter).toFixed(0) + " s"
+ font.pixelSize: GameSettings.bigFontSize
+ }
+ }
+ }
+
+ GameButton {
+ id: startButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: GameSettings.fieldMargin
+ width: circle.width
+ height: GameSettings.fieldHeight
+ enabled: !measurePage.deviceHandler.measuring
+ radius: GameSettings.buttonRadius
+
+ onClicked: measurePage.start()
+
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ text: qsTr("START")
+ color: startButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/SplashScreen.qml b/examples/bluetooth/heartrate_game/HeartRateGame/SplashScreen.qml
new file mode 100644
index 000000000..2f9ac1b3f
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/SplashScreen.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import HeartRateGame
+
+Item {
+ id: root
+
+ property bool appIsReady: false
+ property bool splashIsReady: false
+ property bool ready: appIsReady && splashIsReady
+
+ anchors.fill: parent
+
+ Image {
+ anchors.centerIn: parent
+ width: Math.min(parent.height, parent.width) * 0.6
+ height: GameSettings.heightForWidth(width, sourceSize)
+ source: "images/logo.png"
+ }
+
+ Timer {
+ id: splashTimer
+ interval: 1000
+ onTriggered: splashIsReady = true
+ }
+
+ Component.onCompleted: splashTimer.start()
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/Stats.qml b/examples/bluetooth/heartrate_game/HeartRateGame/Stats.qml
new file mode 100644
index 000000000..22cdd5365
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/Stats.qml
@@ -0,0 +1,55 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import HeartRateGame
+
+GamePage {
+ id: statsPage
+
+ required property DeviceHandler deviceHandler
+
+ Column {
+ anchors.centerIn: parent
+ width: parent.width
+
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ font.pixelSize: GameSettings.hugeFontSize
+ color: GameSettings.textColor
+ text: qsTr("RESULT")
+ }
+
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ font.pixelSize: GameSettings.giganticFontSize * 3
+ color: GameSettings.textColor
+ text: (statsPage.deviceHandler.maxHR - statsPage.deviceHandler.minHR).toFixed(0)
+ }
+
+ Item {
+ height: GameSettings.fieldHeight
+ width: 1
+ }
+
+ StatsLabel {
+ title: qsTr("MIN")
+ value: statsPage.deviceHandler.minHR.toFixed(0)
+ }
+
+ StatsLabel {
+ title: qsTr("MAX")
+ value: statsPage.deviceHandler.maxHR.toFixed(0)
+ }
+
+ StatsLabel {
+ title: qsTr("AVG")
+ value: statsPage.deviceHandler.average.toFixed(1)
+ }
+
+ StatsLabel {
+ title: qsTr("CALORIES")
+ value: statsPage.deviceHandler.calories.toFixed(3)
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/StatsLabel.qml b/examples/bluetooth/heartrate_game/HeartRateGame/StatsLabel.qml
new file mode 100644
index 000000000..0ea4249a7
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/StatsLabel.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ height: GameSettings.fieldHeight
+ width: parent.width
+
+ property alias title: leftText.text
+ property alias value: rightText.text
+
+ Text {
+ id: leftText
+ anchors.left: parent.left
+ height: parent.height
+ width: parent.width * 0.45
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
+ }
+
+ Text {
+ id: rightText
+ anchors.right: parent.right
+ height: parent.height
+ width: parent.width * 0.45
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/TitleBar.qml b/examples/bluetooth/heartrate_game/HeartRateGame/TitleBar.qml
new file mode 100644
index 000000000..016a44358
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/TitleBar.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+import QtQuick
+
+Rectangle {
+ id: titleBar
+
+ property var __titles: ["CONNECT", "MEASURE", "STATS"]
+ property int currentIndex: 0
+
+ signal titleClicked(int index)
+
+ height: GameSettings.fieldHeight
+ color: GameSettings.viewColor
+
+ Repeater {
+ model: 3
+ Text {
+ id: caption
+ required property int index
+ width: titleBar.width / 3
+ height: titleBar.height
+ x: index * width
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: titleBar.__titles[index]
+ font.pixelSize: GameSettings.tinyFontSize
+ color: titleBar.currentIndex === index ? GameSettings.textColor
+ : GameSettings.disabledTextColor
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: titleBar.titleClicked(caption.index)
+ }
+ }
+ }
+
+ Item {
+ anchors.bottom: parent.bottom
+ width: parent.width / 3
+ height: parent.height
+ x: titleBar.currentIndex * width
+
+ BottomLine {}
+
+ Behavior on x {
+ NumberAnimation {
+ duration: 200
+ }
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/images/bt_off_to_on.png b/examples/bluetooth/heartrate_game/HeartRateGame/images/bt_off_to_on.png
new file mode 100644
index 000000000..5ea1f3f06
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/images/bt_off_to_on.png
Binary files differ
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/images/heart.png b/examples/bluetooth/heartrate_game/HeartRateGame/images/heart.png
new file mode 100644
index 000000000..f2b3c0a3e
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/images/heart.png
Binary files differ
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/images/logo.png b/examples/bluetooth/heartrate_game/HeartRateGame/images/logo.png
new file mode 100644
index 000000000..ea0af7e00
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/images/logo.png
Binary files differ
diff --git a/examples/bluetooth/heartrate_game/HeartRateGame/qmldir b/examples/bluetooth/heartrate_game/HeartRateGame/qmldir
new file mode 100644
index 000000000..2baa74a92
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/HeartRateGame/qmldir
@@ -0,0 +1,14 @@
+module HeartRateGame
+App 1.0 App.qml
+BluetoothAlarmDialog 1.0 BluetoothAlarmDialog.qml
+BottomLine 1.0 BottomLine.qml
+Connect 1.0 Connect.qml
+GameButton 1.0 GameButton.qml
+GamePage 1.0 GamePage.qml
+singleton GameSettings 1.0 GameSettings.qml
+Measure 1.0 Measure.qml
+SplashScreen 1.0 SplashScreen.qml
+Stats 1.0 Stats.qml
+StatsLabel 1.0 StatsLabel.qml
+TitleBar 1.0 TitleBar.qml
+Main 1.0 Main.qml
diff --git a/examples/bluetooth/heartrate_game/bluetoothbaseclass.py b/examples/bluetooth/heartrate_game/bluetoothbaseclass.py
new file mode 100644
index 000000000..cc5c9dbd0
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/bluetoothbaseclass.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal, Slot
+
+
+class BluetoothBaseClass(QObject):
+
+ errorChanged = Signal()
+ infoChanged = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_error = ""
+ self.m_info = ""
+
+ @Property(str, notify=errorChanged)
+ def error(self):
+ return self.m_error
+
+ @error.setter
+ def error(self, e):
+ if self.m_error != e:
+ self.m_error = e
+ self.errorChanged.emit()
+
+ @Property(str, notify=infoChanged)
+ def info(self):
+ return self.m_info
+
+ @info.setter
+ def info(self, i):
+ if self.m_info != i:
+ self.m_info = i
+ self.infoChanged.emit()
+
+ @Slot()
+ def clearMessages(self):
+ self.info = ""
+ self.error = ""
diff --git a/examples/bluetooth/heartrate_game/connectionhandler.py b/examples/bluetooth/heartrate_game/connectionhandler.py
new file mode 100644
index 000000000..79f2720e7
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/connectionhandler.py
@@ -0,0 +1,76 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from PySide6.QtBluetooth import QBluetoothLocalDevice
+from PySide6.QtQml import QmlElement
+from PySide6.QtCore import QObject, Property, Signal, Slot, Qt, QCoreApplication
+
+from heartrate_global import simulator, is_android
+
+if is_android:
+ from PySide6.QtCore import QBluetoothPermission
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "HeartRateGame"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class ConnectionHandler(QObject):
+
+ deviceChanged = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_hasPermission = False
+ self.initLocalDevice()
+
+ @Property(bool, notify=deviceChanged)
+ def alive(self):
+ if sys.platform == "darwin":
+ return True
+ if simulator():
+ return True
+ return (self.m_localDevice.isValid()
+ and self.m_localDevice.hostMode() != QBluetoothLocalDevice.HostPoweredOff)
+
+ @Property(bool, constant=True)
+ def requiresAddressType(self):
+ return sys.platform == "linux" # QT_CONFIG(bluez)?
+
+ @Property(str, notify=deviceChanged)
+ def name(self):
+ return self.m_localDevice.name()
+
+ @Property(str, notify=deviceChanged)
+ def address(self):
+ return self.m_localDevice.address().toString()
+
+ @Property(bool, notify=deviceChanged)
+ def hasPermission(self):
+ return self.m_hasPermission
+
+ @Slot(QBluetoothLocalDevice.HostMode)
+ def hostModeChanged(self, mode):
+ self.deviceChanged.emit()
+
+ def initLocalDevice(self):
+ if is_android:
+ permission = QBluetoothPermission()
+ permission.setCommunicationModes(QBluetoothPermission.Access)
+ permission_status = qApp.checkPermission(permission)
+ if permission_status == Qt.PermissionStatus.Undetermined:
+ qApp.requestPermission(permission, self, self.initLocalDevice)
+ return
+ if permission_status == Qt.PermissionStatus.Denied:
+ return
+ elif permission_status == Qt.PermissionStatus.Granted:
+ print("[HeartRateGame] Bluetooth Permission Granted")
+
+ self.m_localDevice = QBluetoothLocalDevice()
+ self.m_localDevice.hostModeStateChanged.connect(self.hostModeChanged)
+ self.m_hasPermission = True
+ self.deviceChanged.emit()
diff --git a/examples/bluetooth/heartrate_game/devicefinder.py b/examples/bluetooth/heartrate_game/devicefinder.py
new file mode 100644
index 000000000..db42c2fa4
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/devicefinder.py
@@ -0,0 +1,137 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent,
+ QBluetoothDeviceInfo)
+from PySide6.QtQml import QmlElement
+from PySide6.QtCore import QTimer, Property, Signal, Slot, Qt, QCoreApplication
+
+from bluetoothbaseclass import BluetoothBaseClass
+from deviceinfo import DeviceInfo
+from heartrate_global import simulator, is_android
+
+if is_android:
+ from PySide6.QtCore import QBluetoothPermission
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "HeartRateGame"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class DeviceFinder(BluetoothBaseClass):
+
+ scanningChanged = Signal()
+ devicesChanged = Signal()
+
+ def __init__(self, handler, parent=None):
+ super().__init__(parent)
+ self.m_deviceHandler = handler
+ self.m_devices = []
+ self.m_demoTimer = QTimer()
+#! [devicediscovery-1]
+ self.m_deviceDiscoveryAgent = QBluetoothDeviceDiscoveryAgent(self)
+ self.m_deviceDiscoveryAgent.setLowEnergyDiscoveryTimeout(15000)
+ self.m_deviceDiscoveryAgent.deviceDiscovered.connect(self.addDevice)
+ self.m_deviceDiscoveryAgent.errorOccurred.connect(self.scanError)
+
+ self.m_deviceDiscoveryAgent.finished.connect(self.scanFinished)
+ self.m_deviceDiscoveryAgent.canceled.connect(self.scanFinished)
+#! [devicediscovery-1]
+ if simulator():
+ self.m_demoTimer.setSingleShot(True)
+ self.m_demoTimer.setInterval(2000)
+ self.m_demoTimer.timeout.connect(self.scanFinished)
+
+ @Slot()
+ def startSearch(self):
+ if is_android:
+ permission = QBluetoothPermission()
+ permission.setCommunicationModes(QBluetoothPermission.Access)
+ permission_status = qApp.checkPermission(permission)
+ if permission_status == Qt.PermissionStatus.Undetermined:
+ qApp.requestPermission(permission, self, self.startSearch)
+ return
+ elif permission_status == Qt.PermissionStatus.Denied:
+ return
+ elif permission_status == Qt.PermissionStatus.Granted:
+ print("[HeartRateGame] Bluetooth Permission Granted")
+
+ self.clearMessages()
+ self.m_deviceHandler.setDevice(None)
+ self.m_devices.clear()
+
+ self.devicesChanged.emit()
+
+ if simulator():
+ self.m_demoTimer.start()
+ else:
+#! [devicediscovery-2]
+ self.m_deviceDiscoveryAgent.start(QBluetoothDeviceDiscoveryAgent.LowEnergyMethod)
+#! [devicediscovery-2]
+ self.scanningChanged.emit()
+ self.info = "Scanning for devices..."
+
+#! [devicediscovery-3]
+ @Slot(QBluetoothDeviceInfo)
+ def addDevice(self, device):
+ # If device is LowEnergy-device, add it to the list
+ if device.coreConfigurations() & QBluetoothDeviceInfo.LowEnergyCoreConfiguration:
+ self.m_devices.append(DeviceInfo(device))
+ self.info = "Low Energy device found. Scanning more..."
+#! [devicediscovery-3]
+ self.devicesChanged.emit()
+#! [devicediscovery-4]
+ #...
+#! [devicediscovery-4]
+
+ @Slot(QBluetoothDeviceDiscoveryAgent.Error)
+ def scanError(self, error):
+ if error == QBluetoothDeviceDiscoveryAgent.PoweredOffError:
+ self.error = "The Bluetooth adaptor is powered off."
+ elif error == QBluetoothDeviceDiscoveryAgent.InputOutputError:
+ self.error = "Writing or reading from the device resulted in an error."
+ else:
+ self.error = "An unknown error has occurred."
+
+ @Slot()
+ def scanFinished(self):
+ if simulator():
+ # Only for testing
+ for i in range(5):
+ self.m_devices.append(DeviceInfo(QBluetoothDeviceInfo()))
+
+ if self.m_devices:
+ self.info = "Scanning done."
+ else:
+ self.error = "No Low Energy devices found."
+
+ self.scanningChanged.emit()
+ self.devicesChanged.emit()
+
+ @Slot(str)
+ def connectToService(self, address):
+ self.m_deviceDiscoveryAgent.stop()
+
+ currentDevice = None
+ for entry in self.m_devices:
+ device = entry
+ if device and device.deviceAddress == address:
+ currentDevice = device
+ break
+
+ if currentDevice:
+ self.m_deviceHandler.setDevice(currentDevice)
+
+ self.clearMessages()
+
+ @Property(bool, notify=scanningChanged)
+ def scanning(self):
+ if simulator():
+ return self.m_demoTimer.isActive()
+ return self.m_deviceDiscoveryAgent.isActive()
+
+ @Property("QVariant", notify=devicesChanged)
+ def devices(self):
+ return self.m_devices
diff --git a/examples/bluetooth/heartrate_game/devicehandler.py b/examples/bluetooth/heartrate_game/devicehandler.py
new file mode 100644
index 000000000..85d160c4d
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/devicehandler.py
@@ -0,0 +1,306 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import struct
+
+from enum import IntEnum
+
+from PySide6.QtBluetooth import (QLowEnergyCharacteristic,
+ QLowEnergyController,
+ QLowEnergyDescriptor,
+ QLowEnergyService,
+ QBluetoothUuid)
+from PySide6.QtQml import QmlElement
+from PySide6.QtCore import (QByteArray, QDateTime, QRandomGenerator, QTimer,
+ Property, Signal, Slot, QEnum)
+
+from bluetoothbaseclass import BluetoothBaseClass
+from heartrate_global import simulator
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "HeartRateGame"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class DeviceHandler(BluetoothBaseClass):
+
+ @QEnum
+ class AddressType(IntEnum):
+ PUBLIC_ADDRESS = 1
+ RANDOM_ADDRESS = 2
+
+ measuringChanged = Signal()
+ aliveChanged = Signal()
+ statsChanged = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self.m_control = None
+ self.m_service = None
+ self.m_notificationDesc = QLowEnergyDescriptor()
+ self.m_currentDevice = None
+
+ self.m_foundHeartRateService = False
+ self.m_measuring = False
+ self.m_currentValue = 0
+ self.m_min = 0
+ self.m_max = 0
+ self.m_sum = 0
+ self.m_avg = 0.0
+ self.m_calories = 0.0
+
+ self.m_start = QDateTime()
+ self.m_stop = QDateTime()
+
+ self.m_measurements = []
+ self.m_addressType = QLowEnergyController.PublicAddress
+
+ self.m_demoTimer = QTimer()
+
+ if simulator():
+ self.m_demoTimer.setSingleShot(False)
+ self.m_demoTimer.setInterval(2000)
+ self.m_demoTimer.timeout.connect(self.updateDemoHR)
+ self.m_demoTimer.start()
+ self.updateDemoHR()
+
+ @Property(int)
+ def addressType(self):
+ if self.m_addressType == QLowEnergyController.RandomAddress:
+ return DeviceHandler.AddressType.RANDOM_ADDRESS
+ return DeviceHandler.AddressType.PUBLIC_ADDRESS
+
+ @addressType.setter
+ def addressType(self, type):
+ if type == DeviceHandler.AddressType.PUBLIC_ADDRESS:
+ self.m_addressType = QLowEnergyController.PublicAddress
+ elif type == DeviceHandler.AddressType.RANDOM_ADDRESS:
+ self.m_addressType = QLowEnergyController.RandomAddress
+
+ @Slot(QLowEnergyController.Error)
+ def controllerErrorOccurred(self, device):
+ self.error = "Cannot connect to remote device."
+
+ @Slot()
+ def controllerConnected(self):
+ self.info = "Controller connected. Search services..."
+ self.m_control.discoverServices()
+
+ @Slot()
+ def controllerDisconnected(self):
+ self.error = "LowEnergy controller disconnected"
+
+ def setDevice(self, device):
+ self.clearMessages()
+ self.m_currentDevice = device
+
+ if simulator():
+ self.info = "Demo device connected."
+ return
+
+ # Disconnect and delete old connection
+ if self.m_control:
+ self.m_control.disconnectFromDevice()
+ m_control = None
+
+ # Create new controller and connect it if device available
+ if self.m_currentDevice:
+
+ # Make connections
+#! [Connect-Signals-1]
+ self.m_control = QLowEnergyController.createCentral(self.m_currentDevice.device(), self)
+#! [Connect-Signals-1]
+ self.m_control.setRemoteAddressType(self.m_addressType)
+#! [Connect-Signals-2]
+
+ self.m_control.serviceDiscovered.connect(self.serviceDiscovered)
+ self.m_control.discoveryFinished.connect(self.serviceScanDone)
+
+ self.m_control.errorOccurred.connect(self.controllerErrorOccurred)
+ self.m_control.connected.connect(self.controllerConnected)
+ self.m_control.disconnected.connect(self.controllerDisconnected)
+
+ # Connect
+ self.m_control.connectToDevice()
+#! [Connect-Signals-2]
+
+ @Slot()
+ def startMeasurement(self):
+ if self.alive:
+ self.m_start = QDateTime.currentDateTime()
+ self.m_min = 0
+ self.m_max = 0
+ self.m_avg = 0
+ self.m_sum = 0
+ self.m_calories = 0.0
+ self.m_measuring = True
+ self.m_measurements.clear()
+ self.measuringChanged.emit()
+
+ @Slot()
+ def stopMeasurement(self):
+ self.m_measuring = False
+ self.measuringChanged.emit()
+
+#! [Filter HeartRate service 1]
+ @Slot(QBluetoothUuid)
+ def serviceDiscovered(self, gatt):
+ if gatt == QBluetoothUuid(QBluetoothUuid.ServiceClassUuid.HeartRate):
+ self.info = "Heart Rate service discovered. Waiting for service scan to be done..."
+ self.m_foundHeartRateService = True
+
+#! [Filter HeartRate service 1]
+
+ @Slot()
+ def serviceScanDone(self):
+ self.info = "Service scan done."
+
+ # Delete old service if available
+ if self.m_service:
+ self.m_service = None
+
+#! [Filter HeartRate service 2]
+ # If heartRateService found, create new service
+ if self.m_foundHeartRateService:
+ self.m_service = self.m_control.createServiceObject(QBluetoothUuid(QBluetoothUuid.ServiceClassUuid.HeartRate), self)
+
+ if self.m_service:
+ self.m_service.stateChanged.connect(self.serviceStateChanged)
+ self.m_service.characteristicChanged.connect(self.updateHeartRateValue)
+ self.m_service.descriptorWritten.connect(self.confirmedDescriptorWrite)
+ self.m_service.discoverDetails()
+ else:
+ self.error = "Heart Rate Service not found."
+#! [Filter HeartRate service 2]
+
+# Service functions
+#! [Find HRM characteristic]
+ @Slot(QLowEnergyService.ServiceState)
+ def serviceStateChanged(self, switch):
+ if switch == QLowEnergyService.RemoteServiceDiscovering:
+ self.info = "Discovering services..."
+ elif switch == QLowEnergyService.RemoteServiceDiscovered:
+ self.info = "Service discovered."
+ hrChar = self.m_service.characteristic(QBluetoothUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement))
+ if hrChar.isValid():
+ self.m_notificationDesc = hrChar.descriptor(QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration)
+ if self.m_notificationDesc.isValid():
+ self.m_service.writeDescriptor(self.m_notificationDesc,
+ QByteArray.fromHex(b"0100"))
+ else:
+ self.error = "HR Data not found."
+ self.aliveChanged.emit()
+#! [Find HRM characteristic]
+
+#! [Reading value]
+ @Slot(QLowEnergyCharacteristic, QByteArray)
+ def updateHeartRateValue(self, c, value):
+ # ignore any other characteristic change. Shouldn't really happen though
+ if c.uuid() != QBluetoothUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement):
+ return
+
+ data = value.data()
+ flags = int(data[0])
+ # Heart Rate
+ hrvalue = 0
+ if flags & 0x1: # HR 16 bit little endian? otherwise 8 bit
+ hrvalue = struct.unpack("<H", data[1:3])[0]
+ else:
+ hrvalue = struct.unpack("B", data[1:2])[0]
+
+ self.addMeasurement(hrvalue)
+
+#! [Reading value]
+ @Slot()
+ def updateDemoHR(self):
+ randomValue = 0
+ if self.m_currentValue < 30: # Initial value
+ randomValue = 55 + QRandomGenerator.global_().bounded(30)
+ elif not self.m_measuring: # Value when relax
+ random = QRandomGenerator.global_().bounded(5)
+ randomValue = self.m_currentValue - 2 + random
+ randomValue = max(min(randomValue, 55), 75)
+ else: # Measuring
+ random = QRandomGenerator.global_().bounded(10)
+ randomValue = self.m_currentValue + random - 2
+
+ self.addMeasurement(randomValue)
+
+ @Slot(QLowEnergyCharacteristic, QByteArray)
+ def confirmedDescriptorWrite(self, d, value):
+ if (d.isValid() and d == self.m_notificationDesc
+ and value == QByteArray.fromHex(b"0000")):
+ # disabled notifications . assume disconnect intent
+ self.m_control.disconnectFromDevice()
+ self.m_service = None
+
+ @Slot()
+ def disconnectService(self):
+ self.m_foundHeartRateService = False
+
+ # disable notifications
+ if (self.m_notificationDesc.isValid() and self.m_service
+ and self.m_notificationDesc.value() == QByteArray.fromHex(b"0100")):
+ self.m_service.writeDescriptor(self.m_notificationDesc,
+ QByteArray.fromHex(b"0000"))
+ else:
+ if self.m_control:
+ self.m_control.disconnectFromDevice()
+ self.m_service = None
+
+ @Property(bool, notify=measuringChanged)
+ def measuring(self):
+ return self.m_measuring
+
+ @Property(bool, notify=aliveChanged)
+ def alive(self):
+ if simulator():
+ return True
+ if self.m_service:
+ return self.m_service.state() == QLowEnergyService.RemoteServiceDiscovered
+ return False
+
+ @Property(int, notify=statsChanged)
+ def hr(self):
+ return self.m_currentValue
+
+ @Property(int, notify=statsChanged)
+ def time(self):
+ return self.m_start.secsTo(self.m_stop)
+
+ @Property(int, notify=statsChanged)
+ def maxHR(self):
+ return self.m_max
+
+ @Property(int, notify=statsChanged)
+ def minHR(self):
+ return self.m_min
+
+ @Property(float, notify=statsChanged)
+ def average(self):
+ return self.m_avg
+
+ @Property(float, notify=statsChanged)
+ def calories(self):
+ return self.m_calories
+
+ def addMeasurement(self, value):
+ self.m_currentValue = value
+
+ # If measuring and value is appropriate
+ if self.m_measuring and value > 30 and value < 250:
+ self.m_stop = QDateTime.currentDateTime()
+ self.m_measurements.append(value)
+
+ self.m_min = value if self.m_min == 0 else min(value, self.m_min)
+ self.m_max = max(value, self.m_max)
+ self.m_sum += value
+ self.m_avg = float(self.m_sum) / len(self.m_measurements)
+ self.m_calories = ((-55.0969 + (0.6309 * self.m_avg) + (0.1988 * 94)
+ + (0.2017 * 24)) / 4.184) * 60 * self.time / 3600
+
+ self.statsChanged.emit()
diff --git a/examples/bluetooth/heartrate_game/deviceinfo.py b/examples/bluetooth/heartrate_game/deviceinfo.py
new file mode 100644
index 000000000..5fd5c3270
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/deviceinfo.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from PySide6.QtCore import QObject, Property, Signal
+
+from heartrate_global import simulator
+
+
+class DeviceInfo(QObject):
+
+ deviceChanged = Signal()
+
+ def __init__(self, device):
+ super().__init__()
+ self.m_device = device
+
+ def device(self):
+ return self.m_device
+
+ def setDevice(self, device):
+ self.m_device = device
+ self.deviceChanged.emit()
+
+ @Property(str, notify=deviceChanged)
+ def deviceName(self):
+ if simulator():
+ return "Demo device"
+ return self.m_device.name()
+
+ @Property(str, notify=deviceChanged)
+ def deviceAddress(self):
+ if simulator():
+ return "00:11:22:33:44:55"
+ if sys.platform == "Darwin": # workaround for Core Bluetooth:
+ return self.m_device.deviceUuid().toString()
+ return self.m_device.address().toString()
diff --git a/examples/bluetooth/heartrate_game/doc/heartrate_game.rst b/examples/bluetooth/heartrate_game/doc/heartrate_game.rst
new file mode 100644
index 000000000..0a0938cad
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/doc/heartrate_game.rst
@@ -0,0 +1,9 @@
+Bluetooth Low Energy Heart Rate Game
+====================================
+
+The Bluetooth Low Energy Heart Rate Game shows how to develop a
+Bluetooth Low Energy application using the Qt Bluetooth API. The
+application covers the scanning for Bluetooth Low Energy devices,
+connecting to a Heart Rate service on the device, writing
+characteristics and descriptors, and receiving updates from the device
+once the heart rate has changed.
diff --git a/examples/bluetooth/heartrate_game/heartrate_game.pyproject b/examples/bluetooth/heartrate_game/heartrate_game.pyproject
new file mode 100644
index 000000000..94b7e3978
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/heartrate_game.pyproject
@@ -0,0 +1,23 @@
+{
+ "files": ["main.py",
+ "bluetoothbaseclass.py",
+ "connectionhandler.py",
+ "devicefinder.py",
+ "devicehandler.py",
+ "deviceinfo.py",
+ "heartrate_global.py",
+ "HeartRateGame/qmldir",
+ "HeartRateGame/Main.qml",
+ "HeartRateGame/App.qml",
+ "HeartRateGame/BluetoothAlarmDialog.qml",
+ "HeartRateGame/BottomLine.qml",
+ "HeartRateGame/Connect.qml",
+ "HeartRateGame/GameButton.qml",
+ "HeartRateGame/GamePage.qml",
+ "HeartRateGame/GameSettings.qml",
+ "HeartRateGame/Measure.qml",
+ "HeartRateGame/SplashScreen.qml",
+ "HeartRateGame/Stats.qml",
+ "HeartRateGame/StatsLabel.qml",
+ "HeartRateGame/TitleBar.qml"]
+}
diff --git a/examples/bluetooth/heartrate_game/heartrate_global.py b/examples/bluetooth/heartrate_game/heartrate_global.py
new file mode 100644
index 000000000..dec6f83a3
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/heartrate_global.py
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import os
+import sys
+
+
+_simulator = False
+
+
+def simulator():
+ global _simulator
+ return _simulator
+
+
+def set_simulator(s):
+ global _simulator
+ _simulator = s
+
+is_android = os.environ.get('ANDROID_ARGUMENT')
diff --git a/examples/bluetooth/heartrate_game/main.py b/examples/bluetooth/heartrate_game/main.py
new file mode 100644
index 000000000..3cb4f0672
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/main.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the bluetooth/heartrate-game example from Qt v6.x"""
+
+from pathlib import Path
+import sys
+from argparse import ArgumentParser, RawDescriptionHelpFormatter
+
+from PySide6.QtQml import QQmlApplicationEngine
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import QCoreApplication, QLoggingCategory
+
+from connectionhandler import ConnectionHandler
+from devicefinder import DeviceFinder
+from devicehandler import DeviceHandler
+from heartrate_global import set_simulator
+
+
+if __name__ == '__main__':
+ parser = ArgumentParser(prog="heartrate-game",
+ formatter_class=RawDescriptionHelpFormatter)
+
+ parser.add_argument("-v", "--verbose", action="store_true",
+ help="Generate more output")
+ parser.add_argument("-s", "--simulator", action="store_true",
+ help="Use Simulator")
+ options = parser.parse_args()
+ set_simulator(options.simulator)
+ if options.verbose:
+ QLoggingCategory.setFilterRules("qt.bluetooth* = true")
+
+ app = QGuiApplication(sys.argv)
+
+ connectionHandler = ConnectionHandler()
+ deviceHandler = DeviceHandler()
+ deviceFinder = DeviceFinder(deviceHandler)
+
+ engine = QQmlApplicationEngine()
+ engine.setInitialProperties({
+ "connectionHandler": connectionHandler,
+ "deviceFinder": deviceFinder,
+ "deviceHandler": deviceHandler})
+
+ engine.addImportPath(Path(__file__).parent)
+ engine.loadFromModule("HeartRateGame", "Main")
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ ex = QCoreApplication.exec()
+ del engine
+ sys.exit(ex)
diff --git a/examples/bluetooth/heartrate_server/doc/heartrate_server.rst b/examples/bluetooth/heartrate_server/doc/heartrate_server.rst
new file mode 100644
index 000000000..aaa1a0988
--- /dev/null
+++ b/examples/bluetooth/heartrate_server/doc/heartrate_server.rst
@@ -0,0 +1,8 @@
+Bluetooth Low Energy Heart Rate Server
+======================================
+
+The Bluetooth Low Energy Heart Rate Server is a command-line
+application that shows how to develop a Bluetooth GATT server using
+the Qt Bluetooth API. The application covers setting up a GATT
+service, advertising it and notifying clients about changes to
+characteristic values.
diff --git a/examples/bluetooth/heartrate_server/heartrate_server.py b/examples/bluetooth/heartrate_server/heartrate_server.py
new file mode 100644
index 000000000..f98cc6fe8
--- /dev/null
+++ b/examples/bluetooth/heartrate_server/heartrate_server.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the bluetooth/heartrate-server example from Qt v6.x"""
+
+import sys
+from enum import Enum
+
+from PySide6.QtBluetooth import (QBluetoothUuid, QLowEnergyAdvertisingData,
+ QLowEnergyAdvertisingParameters,
+ QLowEnergyCharacteristic,
+ QLowEnergyCharacteristicData,
+ QLowEnergyController,
+ QLowEnergyDescriptorData,
+ QLowEnergyServiceData)
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import QByteArray, QTimer, QLoggingCategory
+
+
+class ValueChange(Enum):
+ VALUE_UP = 1
+ VALUE_DOWN = 2
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ QLoggingCategory.setFilterRules("qt.bluetooth* = true")
+
+#! [Advertising Data]
+ advertising_data = QLowEnergyAdvertisingData()
+ advertising_data.setDiscoverability(QLowEnergyAdvertisingData.DiscoverabilityGeneral)
+ advertising_data.setIncludePowerLevel(True)
+ advertising_data.setLocalName("HeartRateServer")
+ advertising_data.setServices([QBluetoothUuid.ServiceClassUuid.HeartRate])
+#! [Advertising Data]
+
+#! [Service Data]
+ char_data = QLowEnergyCharacteristicData()
+ char_data.setUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
+ char_data.setValue(QByteArray(2, 0))
+ char_data.setProperties(QLowEnergyCharacteristic.Notify)
+ client_config = QLowEnergyDescriptorData(QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration,
+ QByteArray(2, 0))
+ char_data.addDescriptor(client_config)
+
+ service_data = QLowEnergyServiceData()
+ service_data.setType(QLowEnergyServiceData.ServiceTypePrimary)
+ service_data.setUuid(QBluetoothUuid.ServiceClassUuid.HeartRate)
+ service_data.addCharacteristic(char_data)
+#! [Service Data]
+
+#! [Start Advertising]
+ le_controller = QLowEnergyController.createPeripheral()
+ service = le_controller.addService(service_data)
+ le_controller.startAdvertising(QLowEnergyAdvertisingParameters(),
+ advertising_data, advertising_data)
+#! [Start Advertising]
+
+#! [Provide Heartbeat]
+ value_change = ValueChange.VALUE_UP
+ heartbeat_timer = QTimer()
+ current_heart_rate = 60
+
+ def heartbeat_provider():
+ global current_heart_rate, value_change, current_heart_rate
+ value = QByteArray()
+ value.append(chr(0)) # Flags that specify the format of the value.
+ value.append(chr(current_heart_rate)) # Actual value.
+ characteristic = service.characteristic(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
+ assert(characteristic.isValid())
+ # Potentially causes notification.
+ service.writeCharacteristic(characteristic, value)
+ if current_heart_rate == 60:
+ value_change = ValueChange.VALUE_UP
+ elif current_heart_rate == 100:
+ value_change = ValueChange.VALUE_DOWN
+ if value_change == ValueChange.VALUE_UP:
+ current_heart_rate += 1
+ else:
+ current_heart_rate -= 1
+
+ heartbeat_timer.timeout.connect(heartbeat_provider)
+ heartbeat_timer.start(1000)
+#! [Provide Heartbeat]
+
+ def reconnect():
+ service = le_controller.addService(service_data)
+ if not service.isNull():
+ le_controller.startAdvertising(QLowEnergyAdvertisingParameters(),
+ advertising_data, advertising_data)
+
+ le_controller.disconnected.connect(reconnect)
+
+ sys.exit(app.exec())
diff --git a/examples/bluetooth/heartrate_server/heartrate_server.pyproject b/examples/bluetooth/heartrate_server/heartrate_server.pyproject
new file mode 100644
index 000000000..de1fd14a0
--- /dev/null
+++ b/examples/bluetooth/heartrate_server/heartrate_server.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["heartrate_server.py"]
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/Characteristics.qml b/examples/bluetooth/lowenergyscanner/Scanner/Characteristics.qml
new file mode 100644
index 000000000..bd3ccbfcb
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/Characteristics.qml
@@ -0,0 +1,121 @@
+// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+import QtQuick
+
+Rectangle {
+ id: characteristicsPage
+
+ signal showServices
+ signal showDevices
+
+ width: 300
+ height: 600
+
+ Header {
+ id: header
+ anchors.top: parent.top
+ headerText: "Characteristics list"
+ }
+
+ Dialog {
+ id: info
+ anchors.centerIn: parent
+ visible: true
+ dialogText: "Scanning for characteristics..."
+ }
+
+ Connections {
+ target: Device
+ function oncharacteristics_pdated() {
+ menu.menuText = "Back"
+ if (characteristicview.count === 0) {
+ info.dialogText = "No characteristic found"
+ info.busyImage = false
+ } else {
+ info.visible = false
+ info.busyImage = true
+ }
+ }
+
+ function onDisconnected() {
+ characteristicsPage.showDevices()
+ }
+ }
+
+ ListView {
+ id: characteristicview
+ width: parent.width
+ clip: true
+
+ anchors.top: header.bottom
+ anchors.bottom: menu.top
+ model: Device.characteristicList
+
+ delegate: Rectangle {
+ required property var modelData
+ id: box
+ height: 300
+ width: characteristicview.width
+ color: "lightsteelblue"
+ border.width: 2
+ border.color: "black"
+ radius: 5
+
+ Label {
+ id: characteristicName
+ textContent: box.modelData.characteristic_name
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: characteristicUuid
+ font.pointSize: characteristicName.font.pointSize * 0.7
+ textContent: box.modelData.characteristic_uuid
+ anchors.top: characteristicName.bottom
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: characteristicValue
+ font.pointSize: characteristicName.font.pointSize * 0.7
+ textContent: ("Value: " + box.modelData.characteristic_value)
+ anchors.bottom: characteristicHandle.top
+ horizontalAlignment: Text.AlignHCenter
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: characteristicHandle
+ font.pointSize: characteristicName.font.pointSize * 0.7
+ textContent: ("Handlers: " + box.modelData.characteristic_handle)
+ anchors.bottom: characteristicPermission.top
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: characteristicPermission
+ font.pointSize: characteristicName.font.pointSize * 0.7
+ textContent: box.modelData.characteristic_permission
+ anchors.bottom: parent.bottom
+ anchors.topMargin: 5
+ anchors.bottomMargin: 5
+ }
+ }
+ }
+
+ Menu {
+ id: menu
+ anchors.bottom: parent.bottom
+ menuWidth: parent.width
+ menuText: Device.update
+ menuHeight: (parent.height / 6)
+ onButtonClick: {
+ characteristicsPage.showServices()
+ Device.update = "Back"
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/Devices.qml b/examples/bluetooth/lowenergyscanner/Scanner/Devices.qml
new file mode 100644
index 000000000..6e5e85a52
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/Devices.qml
@@ -0,0 +1,121 @@
+// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+import QtQuick
+
+Rectangle {
+ id: devicesPage
+
+ property bool deviceState: Device.state
+ signal showServices
+
+ width: 300
+ height: 600
+
+ onDeviceStateChanged: {
+ if (!Device.state)
+ info.visible = false
+ }
+
+ Header {
+ id: header
+ anchors.top: parent.top
+ headerText: {
+ if (Device.state)
+ return "Discovering"
+
+ if (Device.devices_list.length > 0)
+ return "Select a device"
+
+ return "Start Discovery"
+ }
+ }
+
+ Dialog {
+ id: info
+ anchors.centerIn: parent
+ visible: false
+ }
+
+ ListView {
+ id: theListView
+ width: parent.width
+ clip: true
+
+ anchors.top: header.bottom
+ anchors.bottom: connectToggle.top
+ model: Device.devices_list
+
+ delegate: Rectangle {
+ required property var modelData
+ id: box
+ height: 100
+ width: theListView.width
+ color: "lightsteelblue"
+ border.width: 2
+ border.color: "black"
+ radius: 5
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ Device.scan_services(box.modelData.device_address)
+ showServices()
+ }
+ }
+
+ Label {
+ id: deviceName
+ textContent: box.modelData.device_name
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: deviceAddress
+ textContent: box.modelData.device_address
+ font.pointSize: deviceName.font.pointSize * 0.7
+ anchors.bottom: box.bottom
+ anchors.bottomMargin: 5
+ }
+ }
+ }
+
+ Menu {
+ id: connectToggle
+
+ menuWidth: parent.width
+ anchors.bottom: menu.top
+ menuText: {
+ visible = Device.devices_list.length > 0
+ if (Device.use_random_address)
+ return "Address type: Random"
+ else
+ return "Address type: Public"
+ }
+
+ onButtonClick: Device.use_random_address = !Device.use_random_address
+ }
+
+ Menu {
+ id: menu
+ anchors.bottom: parent.bottom
+ menuWidth: parent.width
+ menuHeight: (parent.height / 6)
+ menuText: Device.update
+ onButtonClick: {
+ if (!Device.state) {
+ Device.start_device_discovery()
+ // if start_device_discovery() failed Device.state is not set
+ if (Device.state) {
+ info.dialogText = "Searching..."
+ info.visible = true
+ }
+ } else {
+ Device.stop_device_discovery()
+ }
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/Dialog.qml b/examples/bluetooth/lowenergyscanner/Scanner/Dialog.qml
new file mode 100644
index 000000000..75e82642a
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/Dialog.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: dialog
+ width: parent.width / 3 * 2
+ height: dialogTextId.height + background.height + 20
+ z: 50
+ property string dialogText: ""
+ property bool busyImage: true
+ border.width: 1
+ border.color: "#363636"
+ radius: 10
+
+ Text {
+ id: dialogTextId
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ anchors.topMargin: 10
+
+ elide: Text.ElideMiddle
+ text: dialog.dialogText
+ color: "#363636"
+ wrapMode: Text.Wrap
+ }
+
+ Image {
+ id: background
+
+ width: 20
+ height: 20
+ anchors.top: dialogTextId.bottom
+ anchors.horizontalCenter: dialogTextId.horizontalCenter
+ visible: parent.busyImage
+ source: "assets/busy_dark.png"
+ fillMode: Image.PreserveAspectFit
+ NumberAnimation on rotation {
+ duration: 3000
+ from: 0
+ to: 360
+ loops: Animation.Infinite
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/Header.qml b/examples/bluetooth/lowenergyscanner/Scanner/Header.qml
new file mode 100644
index 000000000..c95385dd3
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/Header.qml
@@ -0,0 +1,25 @@
+// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: header
+ width: parent.width
+ height: 70
+ border.width: 1
+ border.color: "#363636"
+ radius: 5
+ property string headerText: ""
+
+ Text {
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ anchors.fill: parent
+ text: header.headerText
+ font.bold: true
+ font.pointSize: 20
+ elide: Text.ElideMiddle
+ color: "#363636"
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/Label.qml b/examples/bluetooth/lowenergyscanner/Scanner/Label.qml
new file mode 100644
index 000000000..e31156740
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/Label.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Text {
+ property string textContent: ""
+ font.pointSize: 20
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#363636"
+ horizontalAlignment: Text.AlignHCenter
+ elide: Text.ElideMiddle
+ width: parent.width
+ wrapMode: Text.Wrap
+ text: textContent
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/Main.qml b/examples/bluetooth/lowenergyscanner/Scanner/Main.qml
new file mode 100644
index 000000000..88600bace
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/Main.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+
+Window {
+ id: main
+
+ width: 300
+ height: 600
+ visible: true
+
+ StackLayout {
+ id: pagesLayout
+ anchors.fill: parent
+ currentIndex: 0
+
+ Devices {
+ onShowServices: pagesLayout.currentIndex = 1
+ }
+ Services {
+ onShowDevices: pagesLayout.currentIndex = 0
+ onShowCharacteristics: pagesLayout.currentIndex = 2
+ }
+ Characteristics {
+ onShowDevices: pagesLayout.currentIndex = 0
+ onShowServices: pagesLayout.currentIndex = 1
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/Menu.qml b/examples/bluetooth/lowenergyscanner/Scanner/Menu.qml
new file mode 100644
index 000000000..ef69c895e
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/Menu.qml
@@ -0,0 +1,55 @@
+// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: menu
+
+ property real menuWidth: 100
+ property real menuHeight: 50
+ property string menuText: "Search"
+ signal buttonClick
+
+ height: menuHeight
+ width: menuWidth
+
+ Rectangle {
+ id: search
+ width: parent.width
+ height: parent.height
+ anchors.centerIn: parent
+ color: "#363636"
+ border.width: 1
+ border.color: "#E3E3E3"
+ radius: 5
+ Text {
+ id: searchText
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ anchors.fill: parent
+ text: menu.menuText
+ elide: Text.ElideMiddle
+ color: "#E3E3E3"
+ wrapMode: Text.WordWrap
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onPressed: {
+ search.width = search.width - 7
+ search.height = search.height - 5
+ }
+
+ onReleased: {
+ search.width = search.width + 7
+ search.height = search.height + 5
+ }
+
+ onClicked: {
+ menu.buttonClick()
+ }
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/Services.qml b/examples/bluetooth/lowenergyscanner/Scanner/Services.qml
new file mode 100644
index 000000000..70326242e
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/Services.qml
@@ -0,0 +1,115 @@
+// Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+import QtQuick
+
+Rectangle {
+ id: servicesPage
+
+ signal showCharacteristics
+ signal showDevices
+
+ width: 300
+ height: 600
+
+ Component.onCompleted: {
+ // Loading this page may take longer than QLEController
+ // stopping with an error, go back and readjust this view
+ // based on controller errors
+ if (Device.controller_error) {
+ info.visible = false
+ menu.menuText = Device.update
+ }
+ }
+
+ Header {
+ id: header
+ anchors.top: parent.top
+ headerText: "Services list"
+ }
+
+ Dialog {
+ id: info
+ anchors.centerIn: parent
+ visible: true
+ dialogText: "Scanning for services..."
+ }
+
+ Connections {
+ target: Device
+ function onservices_updated() {
+ if (servicesview.count === 0)
+ info.dialogText = "No services found"
+ else
+ info.visible = false
+ }
+
+ function ondisconnected() {
+ servicesPage.showDevices()
+ }
+ }
+
+ ListView {
+ id: servicesview
+ width: parent.width
+ anchors.top: header.bottom
+ anchors.bottom: menu.top
+ model: Device.servicesList
+ clip: true
+
+ delegate: Rectangle {
+ required property var modelData
+ id: box
+ height: 100
+ color: "lightsteelblue"
+ border.width: 2
+ border.color: "black"
+ radius: 5
+ width: servicesview.width
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ Device.connectToService(box.modelData.service_uuid)
+ servicesPage.showCharacteristics()
+ }
+ }
+
+ Label {
+ id: serviceName
+ textContent: box.modelData.service_name
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ }
+
+ Label {
+ textContent: box.modelData.service_type
+ font.pointSize: serviceName.font.pointSize * 0.5
+ anchors.top: serviceName.bottom
+ }
+
+ Label {
+ id: serviceUuid
+ font.pointSize: serviceName.font.pointSize * 0.5
+ textContent: box.modelData.service_uuid
+ anchors.bottom: box.bottom
+ anchors.bottomMargin: 5
+ }
+ }
+ }
+
+ Menu {
+ id: menu
+ anchors.bottom: parent.bottom
+ menuWidth: parent.width
+ menuText: Device.update
+ menuHeight: (parent.height / 6)
+ onButtonClick: {
+ Device.disconnect_from_device()
+ servicesPage.showDevices()
+ Device.update = "Search"
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/assets/busy_dark.png b/examples/bluetooth/lowenergyscanner/Scanner/assets/busy_dark.png
new file mode 100644
index 000000000..3a1059531
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/assets/busy_dark.png
Binary files differ
diff --git a/examples/bluetooth/lowenergyscanner/Scanner/qmldir b/examples/bluetooth/lowenergyscanner/Scanner/qmldir
new file mode 100644
index 000000000..0adf6fb19
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/Scanner/qmldir
@@ -0,0 +1,10 @@
+module Scanner
+typeinfo scanner.qmltypes
+Characteristics 1.0 Characteristics.qml
+Devices 1.0 Devices.qml
+Dialog 1.0 Dialog.qml
+Header 1.0 Header.qml
+Label 1.0 Label.qml
+Main 1.0 Main.qml
+Menu 1.0 Menu.qml
+Services 1.0 Services.qml
diff --git a/examples/bluetooth/lowenergyscanner/characteristicinfo.py b/examples/bluetooth/lowenergyscanner/characteristicinfo.py
new file mode 100644
index 000000000..a0e9df77e
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/characteristicinfo.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtBluetooth import QLowEnergyCharacteristic, QBluetoothUuid
+
+
+class CharacteristicInfo(QObject):
+
+ characteristic_changed = Signal()
+
+ def __init__(self, characteristic=None) -> None:
+ super().__init__()
+ self._characteristic = characteristic
+
+ @Property(str, notify=characteristic_changed)
+ def characteristic_name(self):
+ if not self.characteristic:
+ raise Exception("characteristic unset")
+ name = self.characteristic.name()
+ if name:
+ return name
+
+ for descriptor in self.characteristic.descriptors():
+ if descriptor.type() == QBluetoothUuid.DescriptorType.CharacteristicUserDescription:
+ name = descriptor.value()
+ break
+
+ if not name:
+ name = "Unknown"
+
+ return name
+
+ @Property(str, notify=characteristic_changed)
+ def characteristic_uuid(self):
+ uuid = self.characteristic.uuid()
+ result16, success16 = uuid.toUInt16()
+ if success16:
+ return f"0x{result16:x}"
+
+ result32, sucess32 = uuid.toUInt32()
+ if sucess32:
+ return f"0x{result32:x}"
+
+ return uuid.toString().replace('{', '').replace('}', '')
+
+ @Property(str, notify=characteristic_changed)
+ def characteristic_value(self):
+ # Show raw string first and hex value below
+ a = self.characteristic.value()
+ if not a:
+ return "<none>"
+
+ result = f"{str(a)}\n{str(a.toHex())}"
+ return result
+
+ @Property(str, notify=characteristic_changed)
+ def characteristic_permission(self):
+ properties = "( "
+ permission = self.characteristic.properties()
+ if (permission & QLowEnergyCharacteristic.Read):
+ properties += " Read"
+ if (permission & QLowEnergyCharacteristic.Write):
+ properties += " Write"
+ if (permission & QLowEnergyCharacteristic.Notify):
+ properties += " Notify"
+ if (permission & QLowEnergyCharacteristic.Indicate):
+ properties += " Indicate"
+ if (permission & QLowEnergyCharacteristic.ExtendedProperty):
+ properties += " ExtendedProperty"
+ if (permission & QLowEnergyCharacteristic.Broadcasting):
+ properties += " Broadcast"
+ if (permission & QLowEnergyCharacteristic.WriteNoResponse):
+ properties += " WriteNoResp"
+ if (permission & QLowEnergyCharacteristic.WriteSigned):
+ properties += " WriteSigned"
+ properties += " )"
+ return properties
+
+ @property
+ def characteristic(self):
+ return self._characteristic
+
+ @characteristic.setter
+ def characteristic(self, characteristic):
+ self._characteristic = characteristic
+ self.characteristic_changed.emit()
+
diff --git a/examples/bluetooth/lowenergyscanner/device.py b/examples/bluetooth/lowenergyscanner/device.py
new file mode 100644
index 000000000..e69a8450e
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/device.py
@@ -0,0 +1,279 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import warnings
+from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent, QLowEnergyController,
+ QBluetoothDeviceInfo, QBluetoothUuid, QLowEnergyService)
+from PySide6.QtCore import QObject, Property, Signal, Slot, QTimer, QMetaObject, Qt
+from PySide6.QtQml import QmlElement, QmlSingleton
+
+from deviceinfo import DeviceInfo
+from serviceinfo import ServiceInfo
+from characteristicinfo import CharacteristicInfo
+
+QML_IMPORT_NAME = "Scanner"
+QML_IMPORT_MAJOR_VERSION = 1
+
+@QmlElement
+@QmlSingleton
+class Device(QObject):
+
+ devices_updated = Signal()
+ services_updated = Signal()
+ characteristic_updated = Signal()
+ update_changed = Signal()
+ state_changed = Signal()
+ disconnected = Signal()
+ random_address_changed = Signal()
+
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent)
+ self.devices = []
+ self._services = []
+ self._characteristics = []
+ self._previousAddress = ""
+ self._message = ""
+ self.currentDevice = DeviceInfo()
+ self.connected = False
+ self.controller: QLowEnergyController = None
+ self._deviceScanState = False
+ self.random_address = False
+ self.discovery_agent = QBluetoothDeviceDiscoveryAgent()
+ self.discovery_agent.setLowEnergyDiscoveryTimeout(25000)
+ self.discovery_agent.deviceDiscovered.connect(self.add_device)
+ self.discovery_agent.errorOccurred.connect(self.device_scan_error)
+ self.discovery_agent.finished.connect(self.device_scan_finished)
+ self.update = "Search"
+
+ @Property("QVariant", notify=devices_updated)
+ def devices_list(self):
+ return self.devices
+
+ @Property("QVariant", notify=services_updated)
+ def services_list(self):
+ return self._services
+
+ @Property("QVariant", notify=characteristic_updated)
+ def characteristic_list(self):
+ return self._characteristics
+
+ @Property(str, notify=update_changed)
+ def update(self):
+ return self._message
+
+ @update.setter
+ def update(self, message):
+ self._message = message
+ self.update_changed.emit()
+
+ @Property(bool, notify=random_address_changed)
+ def use_random_address(self):
+ return self.random_address
+
+ @use_random_address.setter
+ def use_random_address(self, newValue):
+ self.random_address = newValue
+ self.random_address_changed.emit()
+
+ @Property(bool, notify=state_changed)
+ def state(self):
+ return self._deviceScanState
+
+ @Property(bool)
+ def controller_error(self):
+ return self.controller and (self.controller.error() != QLowEnergyController.NoError)
+
+ @Slot()
+ def start_device_discovery(self):
+ self.devices.clear()
+ self.devices_updated.emit()
+ self.update = "Scanning for devices ..."
+ self.discovery_agent.start(QBluetoothDeviceDiscoveryAgent.LowEnergyMethod)
+
+ if self.discovery_agent.isActive():
+ self._deviceScanState = True
+ self.state_changed.emit()
+
+ @Slot(str)
+ def scan_services(self, address):
+ # We need the current device for service discovery.
+ for device in self.devices:
+ if device.device_address == address:
+ self.currentDevice.set_device(device.get_device())
+ break
+
+ if not self.currentDevice.get_device().isValid():
+ warnings.warn("Not a valid device")
+ return
+
+ self._characteristics.clear()
+ self.characteristic_updated.emit()
+ self._services.clear()
+ self.services_updated.emit()
+
+ self.update = "Back\n(Connecting to device...)"
+
+ if self.controller and (self._previousAddress != self.currentDevice.device_address):
+ self.controller.disconnectFromDevice()
+ del self.controller
+ self.controller = None
+
+ if not self.controller:
+ self.controller = QLowEnergyController.createCentral(self.currentDevice.get_device())
+ self.controller.connected.connect(self.device_connected)
+ self.controller.errorOccurred.connect(self.error_received)
+ self.controller.disconnected.connect(self.device_disconnected)
+ self.controller.serviceDiscovered.connect(self.add_low_energy_service)
+ self.controller.discoveryFinished.connect(self.services_scan_done)
+
+ if self.random_address:
+ self.controller.setRemoteAddressType(QLowEnergyController.RandomAddress)
+ else:
+ self.controller.setRemoteAddressType(QLowEnergyController.PublicAddress)
+ self.controller.connectToDevice()
+
+ self._previousAddress = self.currentDevice.device_address
+
+ @Slot(str)
+ def connect_to_service(self, uuid):
+ service: QLowEnergyService = None
+ for serviceInfo in self._services:
+ if not serviceInfo:
+ continue
+
+ if serviceInfo.service_uuid == uuid:
+ service = serviceInfo.service
+ break
+
+ if not service:
+ return
+
+ self._characteristics.clear()
+ self.characteristic_updated.emit()
+
+ if service.state() == QLowEnergyService.RemoteService:
+ service.state_changed.connect(self.service_details_discovered)
+ service.discoverDetails()
+ self.update = "Back\n(Discovering details...)"
+ return
+
+ # discovery already done
+ chars = service.characteristics()
+ for ch in chars:
+ cInfo = CharacteristicInfo(ch)
+ self._characteristics.append(cInfo)
+
+ QTimer.singleShot(0, self.characteristic_updated)
+
+ @Slot()
+ def disconnect_from_device(self):
+ # UI always expects disconnect() signal when calling this signal
+ # TODO what is really needed is to extend state() to a multi value
+ # and thus allowing UI to keep track of controller progress in addition to
+ # device scan progress
+
+ if self.controller.state() != QLowEnergyController.UnconnectedState:
+ self.controller.disconnectFromDevice()
+ else:
+ self.device_disconnected()
+
+ @Slot(QBluetoothDeviceInfo)
+ def add_device(self, info):
+ if info.coreConfigurations() & QBluetoothDeviceInfo.LowEnergyCoreConfiguration:
+ self.update = "Last device added: " + info.name()
+
+ @Slot()
+ def device_scan_finished(self):
+ foundDevices = self.discovery_agent.discoveredDevices()
+ for nextDevice in foundDevices:
+ if nextDevice.coreConfigurations() & QBluetoothDeviceInfo.LowEnergyCoreConfiguration:
+ device = DeviceInfo(nextDevice)
+ self.devices.append(device)
+
+ self.devices_updated.emit()
+ self._deviceScanState = False
+ self.state_changed.emit()
+ if not self.devices:
+ self.update = "No Low Energy devices found..."
+ else:
+ self.update = "Done! Scan Again!"
+
+ @Slot("QBluetoothDeviceDiscovertAgent::Error")
+ def device_scan_error(self, error):
+ if error == QBluetoothDeviceDiscoveryAgent.PoweredOffError:
+ self.update = (
+ "The Bluetooth adaptor is powered off, power it on before doing discovery."
+ )
+ elif error == QBluetoothDeviceDiscoveryAgent.InputOutputError:
+ self.update = "Writing or reading from the device resulted in an error."
+ else:
+ qme = self.discovery_agent.metaObject().enumerator(
+ self.discovery_agent.metaObject().indexOfEnumerator("Error")
+ )
+ self.update = f"Error: {qme.valueToKey(error)}"
+
+ self._deviceScanState = False
+ self.devices_updated.emit()
+ self.state_changed.emit()
+
+ @Slot(QBluetoothUuid)
+ def add_low_energy_service(self, service_uuid):
+ service = self.controller.createServiceObject(service_uuid)
+ if not service:
+ warnings.warn("Cannot create service from uuid")
+ return
+
+ serv = ServiceInfo(service)
+ self._services.append(serv)
+ self.services_updated.emit()
+
+ @Slot()
+ def device_connected(self):
+ self.update = "Back\n(Discovering services...)"
+ self.connected = True
+ self.controller.discoverServices()
+
+ @Slot("QLowEnergyController::Error")
+ def error_received(self, error):
+ warnings.warn(f"Error: {self.controller.errorString()}")
+ self.update = f"Back\n({self.controller.errorString()})"
+
+ @Slot()
+ def services_scan_done(self):
+ self.update = "Back\n(Service scan done!)"
+ # force UI in case we didn't find anything
+ if not self._services:
+ self.services_updated.emit()
+
+ @Slot()
+ def device_disconnected(self):
+ warnings.warn("Disconnect from Device")
+ self.disconnected.emit()
+
+ @Slot("QLowEnergyService::ServiceState")
+ def service_details_discovered(self, newState):
+ if newState != QLowEnergyService.RemoteServiceDiscovered:
+ # do not hang in "Scanning for characteristics" mode forever
+ # in case the service discovery failed
+ # We have to queue the signal up to give UI time to even enter
+ # the above mode
+ if newState != QLowEnergyService.RemoteServiceDiscovering:
+ QMetaObject.invokeMethod(self.characteristic_updated, Qt.QueuedConnection)
+ return
+
+ service = self.sender()
+ if not service:
+ return
+
+ chars = service.characteristics()
+ for ch in chars:
+ cInfo = CharacteristicInfo(ch)
+ self._characteristics.append(cInfo)
+
+ self.characteristic_updated.emit()
+
+ @Slot()
+ def stop_device_discovery(self):
+ if self.discovery_agent.isActive():
+ self.discovery_agent.stop()
+
+
diff --git a/examples/bluetooth/lowenergyscanner/deviceinfo.py b/examples/bluetooth/lowenergyscanner/deviceinfo.py
new file mode 100644
index 000000000..edcbef89d
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/deviceinfo.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtBluetooth import QBluetoothDeviceInfo
+
+
+class DeviceInfo(QObject):
+
+ device_changed = Signal()
+
+ def __init__(self, d: QBluetoothDeviceInfo = None) -> None:
+ super().__init__()
+ self._device = d
+
+ @Property(str, notify=device_changed)
+ def device_name(self):
+ return self._device.name()
+
+ @Property(str, notify=device_changed)
+ def device_address(self):
+ if sys.platform == "darwin":
+ return self._device.deviceUuid().toString()
+
+ return self._device.address().toString()
+
+ def get_device(self):
+ return self._device
+
+ def set_device(self, device):
+ self._device = device
+ self.device_changed.emit()
+
diff --git a/examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.png b/examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.png
new file mode 100644
index 000000000..29f41deb4
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.png
Binary files differ
diff --git a/examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.rst b/examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.rst
new file mode 100644
index 000000000..c20c2d8b7
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.rst
@@ -0,0 +1,9 @@
+Bluetooth Low Energy Scanner Example
+====================================
+
+A Python application that demonstrates the analogous example in Qt
+`Bluetooth Low Energy Scanner <https://doc.qt.io/qt-6/qtbluetooth-lowenergyscanner-example.html>`_
+
+.. image:: lowenergyscanner.png
+ :width: 400
+ :alt: lowenergyscanner screenshot
diff --git a/examples/bluetooth/lowenergyscanner/lowenergyscanner.pyproject b/examples/bluetooth/lowenergyscanner/lowenergyscanner.pyproject
new file mode 100644
index 000000000..7e0cfa3d9
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/lowenergyscanner.pyproject
@@ -0,0 +1,7 @@
+{
+ "files": ["main.py", "device.py", "deviceinfo.py", "serviceinfo.py", "characteristicinfo.py",
+ "Scanner/Main.qml", "Scanner/Menu.qml","Scanner/Header.qml",
+ "Scanner/Characteristics.qml", "Scanner/Dialog.qml", "Scanner/Services.qml",
+ "Scanner/Label.qml", "Scanner/Devices.qml", "Scanner/assets/busy_dark.png",
+ "Scanner/qmldir"]
+}
diff --git a/examples/bluetooth/lowenergyscanner/main.py b/examples/bluetooth/lowenergyscanner/main.py
new file mode 100644
index 000000000..dfbff97b3
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/main.py
@@ -0,0 +1,27 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the bluetooth/lowenergyscanner example from Qt v6.x"""
+
+
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from device import Device
+from pathlib import Path
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ engine = QQmlApplicationEngine()
+ engine.addImportPath(Path(__file__).parent)
+ engine.loadFromModule("Scanner", "Main")
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ ex = QCoreApplication.exec()
+ del engine
+ sys.exit(ex)
diff --git a/examples/bluetooth/lowenergyscanner/serviceinfo.py b/examples/bluetooth/lowenergyscanner/serviceinfo.py
new file mode 100644
index 000000000..092e9898f
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/serviceinfo.py
@@ -0,0 +1,66 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtBluetooth import QLowEnergyService
+
+
+class ServiceInfo(QObject):
+
+ service_changed = Signal()
+
+ def __init__(self, service: QLowEnergyService) -> None:
+ super().__init__()
+ self._service = service
+ self.service.setParent(self)
+
+ @Property(str, notify=service_changed)
+ def service_name(self):
+ if not self.service:
+ return ""
+
+ return self.service.service_name()
+
+ @Property(str, notify=service_changed)
+ def service_type(self):
+ if not self.service:
+ return ""
+
+ result = ""
+ if (self.service.type() & QLowEnergyService.PrimaryService):
+ result += "primary"
+ else:
+ result += "secondary"
+
+ if (self.service.type() & QLowEnergyService.IncludedService):
+ result += " included"
+
+ result = '<' + result + '>'
+
+ return result
+
+ @Property(str, notify=service_changed)
+ def service_uuid(self):
+ if not self.service:
+ return ""
+
+ uuid = self.service.service_uuid()
+ result16, success16 = uuid.toUInt16()
+ if success16:
+ return f"0x{result16:x}"
+
+ result32, sucesss32 = uuid.toUInt32()
+ if sucesss32:
+ return f"0x{result32:x}"
+
+ return uuid.toString().replace('{', '').replace('}', '')
+
+ @property
+ def service(self):
+ return self._service
+
+ @service.setter
+ def service(self, service):
+ self._service = service
+
+
diff --git a/examples/charts/audio/audio.py b/examples/charts/audio/audio.py
index 14026e897..8ba0b20c5 100644
--- a/examples/charts/audio/audio.py
+++ b/examples/charts/audio/audio.py
@@ -6,8 +6,7 @@
import sys
from PySide6.QtCharts import QChart, QChartView, QLineSeries, QValueAxis
from PySide6.QtCore import QPointF, Slot
-from PySide6.QtMultimedia import (QAudioDevice, QAudioFormat,
- QAudioSource, QMediaDevices)
+from PySide6.QtMultimedia import QAudioFormat, QAudioSource, QMediaDevices
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox
diff --git a/examples/charts/chartthemes/README.md b/examples/charts/chartthemes/README.md
index 806e391d2..1b1ab51f7 100644
--- a/examples/charts/chartthemes/README.md
+++ b/examples/charts/chartthemes/README.md
@@ -3,7 +3,7 @@
To generated the file `ui_themewidget.py`, the following
command need to be executed:
-`pyside6-uic themewidget.ui > ui_themewidget.py`
+`pyside6-uic themewidget.ui -o ui_themewidget.py`
Also, if you modify the UI file, then you would need
to run the previous command again.
diff --git a/examples/charts/chartthemes/main.py b/examples/charts/chartthemes/main.py
index fd5524f20..192382a98 100644
--- a/examples/charts/chartthemes/main.py
+++ b/examples/charts/chartthemes/main.py
@@ -134,7 +134,7 @@ class ThemeWidget(QWidget):
for j in range(len(self.data_table[i])):
data = self.data_table[i][j]
if lower_series:
- points = lower_series.pointsVector()
+ points = lower_series.points()
y_value = points[i].y() + data[0].y()
upper_series.append(QPointF(j, y_value))
else:
@@ -145,10 +145,12 @@ class ThemeWidget(QWidget):
lower_series = upper_series
chart.createDefaultAxes()
- chart.axisX().setRange(0, self.value_count - 1)
- chart.axisY().setRange(0, self.value_max)
+ axis_x = chart.axes(Qt.Horizontal)[0]
+ axis_x.setRange(0, self.value_count - 1)
+ axis_y = chart.axes(Qt.Vertical)[0]
+ axis_y.setRange(0, self.value_max)
# Add space to label to add space between labels and axis
- chart.axisY().setLabelFormat("%.1f ")
+ axis_y.setLabelFormat("%.1f ")
return chart
@@ -166,9 +168,10 @@ class ThemeWidget(QWidget):
chart.addSeries(series)
chart.createDefaultAxes()
- chart.axisY().setRange(0, self.value_max * 2)
+ axis_y = chart.axes(Qt.Vertical)[0]
+ axis_y.setRange(0, self.value_max * 2)
# Add space to label to add space between labels and axis
- chart.axisY().setLabelFormat("%.1f ")
+ axis_y.setLabelFormat("%.1f ")
return chart
@@ -185,10 +188,12 @@ class ThemeWidget(QWidget):
chart.addSeries(series)
chart.createDefaultAxes()
- chart.axisX().setRange(0, self.value_max)
- chart.axisY().setRange(0, self.value_count)
+ axis_x = chart.axes(Qt.Horizontal)[0]
+ axis_x.setRange(0, self.value_max)
+ axis_y = chart.axes(Qt.Vertical)[0]
+ axis_y.setRange(0, self.value_count)
# Add space to label to add space between labels and axis
- chart.axisY().setLabelFormat("%.1f ")
+ axis_y.setLabelFormat("%.1f ")
return chart
@@ -222,10 +227,12 @@ class ThemeWidget(QWidget):
chart.addSeries(series)
chart.createDefaultAxes()
- chart.axisX().setRange(0, self.value_max)
- chart.axisY().setRange(0, self.value_count)
+ axis_x = chart.axes(Qt.Horizontal)[0]
+ axis_x.setRange(0, self.value_max)
+ axis_y = chart.axes(Qt.Vertical)[0]
+ axis_y.setRange(0, self.value_count)
# Add space to label to add space between labels and axis
- chart.axisY().setLabelFormat("%.1f ")
+ axis_y.setLabelFormat("%.1f ")
return chart
@@ -241,10 +248,12 @@ class ThemeWidget(QWidget):
chart.addSeries(series)
chart.createDefaultAxes()
- chart.axisX().setRange(0, self.value_max)
- chart.axisY().setRange(0, self.value_count)
+ axis_x = chart.axes(Qt.Horizontal)[0]
+ axis_x.setRange(0, self.value_max)
+ axis_y = chart.axes(Qt.Vertical)[0]
+ axis_y.setRange(0, self.value_count)
# Add space to label to add space between labels and axis
- chart.axisY().setLabelFormat("%.1f ")
+ axis_y.setLabelFormat("%.1f ")
return chart
@@ -262,26 +271,8 @@ class ThemeWidget(QWidget):
chart_theme = self.charts[0].chart().theme()
if chart_theme != theme:
for chart_view in self.charts:
- if theme == 0:
- theme_name = QChart.ChartThemeLight
- elif theme == 1:
- theme_name = QChart.ChartThemeBlueCerulean
- elif theme == 2:
- theme_name = QChart.ChartThemeDark
- elif theme == 3:
- theme_name = QChart.ChartThemeBrownSand
- elif theme == 4:
- theme_name = QChart.ChartThemeBlueNcs
- elif theme == 5:
- theme_name = QChart.ChartThemeHighContrast
- elif theme == 6:
- theme_name = QChart.ChartThemeBlueIcy
- elif theme == 7:
- theme_name = QChart.ChartThemeQt
- else:
- theme_name = QChart.ChartThemeLight
-
- chart_view.chart().setTheme(theme_name)
+ chart_view.chart().setTheme(theme)
+
# Set palette colors based on selected theme
if theme == QChart.ChartThemeLight:
diff --git a/examples/charts/donutbreakdown/donutbreakdown.py b/examples/charts/donutbreakdown/donutbreakdown.py
index bd9c6240f..1597685c2 100644
--- a/examples/charts/donutbreakdown/donutbreakdown.py
+++ b/examples/charts/donutbreakdown/donutbreakdown.py
@@ -5,8 +5,8 @@
import sys
-from PySide6.QtCore import Qt
-from PySide6.QtGui import QColor, QFont, QPainter, QScreen
+from PySide6.QtCore import Qt, Slot
+from PySide6.QtGui import QColor, QFont, QPainter
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCharts import QChart, QChartView, QPieSeries, QPieSlice
@@ -29,6 +29,7 @@ class MainSlice(QPieSlice):
def name(self):
return self.name
+ @Slot()
def update_label(self):
p = self.percentage() * 100
self.setLabel(f"{self.name} {p:.2f}%")
diff --git a/examples/charts/legend/legend.py b/examples/charts/legend/legend.py
index 4b46cc58b..f1929547d 100644
--- a/examples/charts/legend/legend.py
+++ b/examples/charts/legend/legend.py
@@ -4,7 +4,7 @@
"""PySide6 port of the Legend example from Qt v5.x"""
import sys
-from PySide6.QtCore import Qt, QRectF
+from PySide6.QtCore import Qt, QRectF, Slot
from PySide6.QtGui import QBrush, QColor, QPainter, QPen
from PySide6.QtWidgets import (QApplication, QDoubleSpinBox,
QFormLayout, QGridLayout, QGroupBox, QPushButton, QWidget)
@@ -123,6 +123,7 @@ class MainWidget(QWidget):
def hide_legend_spinbox(self):
self.legend_settings.setVisible(False)
+ @Slot()
def toggle_attached(self):
legend = self.chart.legend()
if legend.isAttachedToChart():
@@ -139,6 +140,7 @@ class MainWidget(QWidget):
self.hide_legend_spinbox()
self.update()
+ @Slot()
def add_barset(self):
series_count = self.series.count()
bar_set = QBarSet(f"set {series_count}")
@@ -146,12 +148,14 @@ class MainWidget(QWidget):
bar_set.append([1 + delta, 2 + delta, 3 + delta, 4 + delta])
self.series.append(bar_set)
+ @Slot()
def remove_barset(self):
sets = self.series.barSets()
len_sets = len(sets)
if len_sets > 0:
self.series.remove(sets[len_sets - 1])
+ @Slot()
def set_legend_alignment(self):
button = self.sender()
legend = self.chart.legend()
@@ -174,18 +178,21 @@ class MainWidget(QWidget):
button.setText("Align (Top)")
legend.setAlignment(Qt.AlignTop)
+ @Slot()
def toggle_bold(self):
legend = self.chart.legend()
font = legend.font()
font.setBold(not font.bold())
legend.setFont(font)
+ @Slot()
def toggle_italic(self):
legend = self.chart.legend()
font = legend.font()
font.setItalic(not font.italic())
legend.setFont(font)
+ @Slot()
def font_size_changed(self):
legend = self.chart.legend()
font = legend.font()
@@ -195,6 +202,7 @@ class MainWidget(QWidget):
font.setPointSizeF(font_size)
legend.setFont(font)
+ @Slot()
def update_legend_layout(self):
legend = self.chart.legend()
diff --git a/examples/charts/lineandbar/lineandbar.py b/examples/charts/lineandbar/lineandbar.py
index 142d011d6..62721c763 100644
--- a/examples/charts/lineandbar/lineandbar.py
+++ b/examples/charts/lineandbar/lineandbar.py
@@ -51,13 +51,15 @@ class TestChart(QMainWindow):
self.categories = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
self._axis_x = QBarCategoryAxis()
self._axis_x.append(self.categories)
- self.chart.setAxisX(self._axis_x, self._line_series)
- self.chart.setAxisX(self._axis_x, self._bar_series)
+ self.chart.addAxis(self._axis_x, Qt.AlignBottom)
+ self._line_series.attachAxis(self._axis_x)
+ self._bar_series.attachAxis(self._axis_x)
self._axis_x.setRange("Jan", "Jun")
self._axis_y = QValueAxis()
- self.chart.setAxisY(self._axis_y, self._line_series)
- self.chart.setAxisY(self._axis_y, self._bar_series)
+ self.chart.addAxis(self._axis_x, Qt.AlignLeft)
+ self._line_series.attachAxis(self._axis_y)
+ self._bar_series.attachAxis(self._axis_y)
self._axis_y.setRange(0, 20)
self.chart.legend().setVisible(True)
diff --git a/examples/charts/nesteddonuts/nesteddonuts.py b/examples/charts/nesteddonuts/nesteddonuts.py
index 2ce068bcf..49173c33a 100644
--- a/examples/charts/nesteddonuts/nesteddonuts.py
+++ b/examples/charts/nesteddonuts/nesteddonuts.py
@@ -5,7 +5,7 @@
import sys
-from PySide6.QtCore import Qt, QTimer
+from PySide6.QtCore import Qt, QTimer, Slot
from PySide6.QtGui import QPainter
from PySide6.QtWidgets import QApplication, QGridLayout, QWidget
from PySide6.QtCharts import QChart, QChartView, QPieSeries, QPieSlice
@@ -65,6 +65,7 @@ class Widget(QWidget):
self.donuts.append(donut)
self.chart_view.chart().addSeries(donut)
+ @Slot()
def update_rotation(self):
for donut in self.donuts:
phase_shift = randrange(-50, 100)
diff --git a/examples/charts/percentbarchart/percentbarchart.py b/examples/charts/percentbarchart/percentbarchart.py
index f6021ee8b..cfb11800a 100644
--- a/examples/charts/percentbarchart/percentbarchart.py
+++ b/examples/charts/percentbarchart/percentbarchart.py
@@ -43,7 +43,8 @@ class MainWindow(QMainWindow):
axis = QBarCategoryAxis()
axis.append(categories)
chart.createDefaultAxes()
- chart.setAxisX(axis, series)
+ chart.addAxis(axis, Qt.AlignBottom)
+ series.attachAxis(axis)
chart.legend().setVisible(True)
chart.legend().setAlignment(Qt.AlignBottom)
diff --git a/examples/charts/pointconfiguration/chartwindow.py b/examples/charts/pointconfiguration/chartwindow.py
new file mode 100644
index 000000000..055802b9f
--- /dev/null
+++ b/examples/charts/pointconfiguration/chartwindow.py
@@ -0,0 +1,156 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the Selected Point Configuration Example from Qt 6.5"""
+from PySide6.QtCore import QPointF, Slot
+from PySide6.QtGui import QColor, QIcon, QPainter
+from PySide6.QtWidgets import QMainWindow, QLineEdit, QLabel, QComboBox
+from PySide6.QtWidgets import QCheckBox, QWidget, QGridLayout, QHBoxLayout
+from PySide6.QtCharts import QLineSeries, QXYSeries, QChart, QChartView
+from typing import Union
+
+
+PointConfig = QXYSeries.PointConfiguration
+
+
+class ChartWindow(QMainWindow):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self.setWindowTitle("Chart")
+ self._series = QLineSeries(self)
+ self._series.setName("Customized series")
+ self._series.setPointsVisible(True)
+ self._series.append([QPointF(0, 7), QPointF(2, 4),
+ QPointF(3, 5), QPointF(7, 4),
+ QPointF(10, 5), QPointF(11, 1),
+ QPointF(13, 3), QPointF(17, 6),
+ QPointF(18, 3), QPointF(20, 2)])
+
+ selected_point_index_label = QLabel("Selected Point: ")
+ self._selected_point_index_lineedit = QLineEdit()
+ self._selected_point_index_lineedit.setReadOnly(True)
+ self._selected_point_index_lineedit.setStyleSheet(
+ "background-color: rgba(0, 0, 0, 0); border: 0px")
+
+ color_label = QLabel("Color: ")
+ self._color_combobox = QComboBox()
+ color_strings = ["red", "orange", "yellow", "green", "blue",
+ "indigo", "violet", "black"]
+ for color_str in color_strings:
+ self._color_combobox.addItem(QIcon(), color_str, QColor(color_str))
+
+ size_label = QLabel("Size: ")
+ self._size_combobox = QComboBox()
+ for size in [2, 3, 4, 6, 8, 10, 12, 15]:
+ self._size_combobox.addItem(QIcon(), str(size), size)
+
+ label_visibility_label = QLabel("Label Visibility: ")
+ self._label_visibility_checkbox = QCheckBox()
+
+ custom_label_label = QLabel("Custom Label: ")
+ self._custom_label_lineedit = QLineEdit()
+
+ self._series.clicked.connect(self._select_point)
+ self._color_combobox.activated.connect(self._set_color)
+ self._size_combobox.activated.connect(self._set_size)
+ label_vis_checkbox = self._label_visibility_checkbox
+ label_vis_checkbox.clicked.connect(self._set_label_visibility)
+ clabel_lineedit = self._custom_label_lineedit
+ clabel_lineedit.editingFinished.connect(self._set_custom_label)
+
+ self._chart = QChart()
+ self._chart.addSeries(self._series)
+ self._chart.createDefaultAxes()
+
+ chart_view = QChartView(self._chart)
+ chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
+
+ control_widget = QWidget(self)
+ control_layout = QGridLayout(control_widget)
+ control_layout.setColumnStretch(1, 1)
+
+ control_layout.addWidget(selected_point_index_label, 0, 0)
+ control_layout.addWidget(self._selected_point_index_lineedit, 0, 1)
+
+ control_layout.addWidget(color_label, 1, 0)
+ control_layout.addWidget(self._color_combobox, 1, 1)
+
+ control_layout.addWidget(size_label, 2, 0)
+ control_layout.addWidget(self._size_combobox, 2, 1)
+
+ control_layout.addWidget(label_visibility_label, 3, 0)
+ control_layout.addWidget(self._label_visibility_checkbox, 3, 1, 1, 2)
+
+ control_layout.addWidget(custom_label_label, 4, 0)
+ control_layout.addWidget(self._custom_label_lineedit, 4, 1)
+
+ main_widget = QWidget(self)
+ main_layout = QHBoxLayout(main_widget)
+ main_layout.addWidget(chart_view)
+ main_layout.setStretch(0, 1)
+ main_layout.addWidget(control_widget)
+ self.setCentralWidget(main_widget)
+
+ self._select_point(4)
+
+ @Slot(QPointF)
+ def _select_point(self, point: Union[QPointF, int]):
+ try:
+ index = (self._series.points().index(point.toPoint()) if
+ isinstance(point, QPointF) else point)
+ except ValueError:
+ # Do nothing if the place that was clicked on wasn't a point.
+ return
+
+ self._series.deselectAllPoints()
+ self._series.selectPoint(index)
+ self._selectedPointIndex = index
+ self._selectedPointConfig = self._series.pointConfiguration(index)
+ selected_point = self._series.at(index)
+ selected_index_lineedit = self._selected_point_index_lineedit
+ selected_index_lineedit.setText("(" + str(selected_point.x()) + ", " +
+ str(selected_point.y()) + ")")
+ config = self._series.pointConfiguration(index)
+
+ color = config.get(PointConfig.Color) or self._series.color()
+ size = config.get(PointConfig.Size) or self._series.markerSize()
+ labelVisibility = (config.get(PointConfig.LabelVisibility) or
+ self._series.pointLabelsVisible())
+ customLabel = config.get(PointConfig.LabelFormat) or ""
+
+ combobox_value_list = [
+ (self._color_combobox, color.name(), color),
+ (self._size_combobox, str(size), size)
+ ]
+ for box, value_str, value in combobox_value_list:
+ if box.findData(value) < 0:
+ box.addItem(value_str, value)
+ box.setCurrentIndex(box.findData(value))
+
+ self._label_visibility_checkbox.setChecked(labelVisibility)
+ self._custom_label_lineedit.setText(customLabel)
+
+ @Slot(int)
+ def _set_color(self, index: int):
+ spc = self._selectedPointConfig
+ spc[PointConfig.Color] = self._color_combobox.currentData()
+ self._series.setPointConfiguration(self._selectedPointIndex, spc)
+
+ @Slot(int)
+ def _set_size(self, index: int):
+ spc = self._selectedPointConfig
+ spc[PointConfig.Size] = self._size_combobox.currentData()
+ self._series.setPointConfiguration(self._selectedPointIndex, spc)
+
+ @Slot(bool)
+ def _set_label_visibility(self, checked: bool):
+ spc = self._selectedPointConfig
+ spc[PointConfig.LabelVisibility] = checked
+ self._series.setPointConfiguration(self._selectedPointIndex, spc)
+
+ @Slot()
+ def _set_custom_label(self):
+ spc = self._selectedPointConfig
+ spc[PointConfig.LabelFormat] = self._custom_label_lineedit.text()
+ self._series.setPointConfiguration(self._selectedPointIndex, spc)
diff --git a/examples/charts/pointconfiguration/doc/pointconfiguration.png b/examples/charts/pointconfiguration/doc/pointconfiguration.png
new file mode 100644
index 000000000..791698587
--- /dev/null
+++ b/examples/charts/pointconfiguration/doc/pointconfiguration.png
Binary files differ
diff --git a/examples/charts/pointconfiguration/doc/pointconfiguration.rst b/examples/charts/pointconfiguration/doc/pointconfiguration.rst
new file mode 100644
index 000000000..ffe865d5e
--- /dev/null
+++ b/examples/charts/pointconfiguration/doc/pointconfiguration.rst
@@ -0,0 +1,144 @@
+.. role:: py(code)
+ :language: python
+
+Selected Point Configuration Example
+====================================
+
+This example shows how to configure individual points of a :py:`QLineSeries`.
+
+.. image:: pointconfiguration.png
+ :width: 90%
+ :align: center
+ :alt: Line chart with controls for configuring selected points
+
+Features demonstrated
+~~~~~~~~~~~~~~~~~~~~~
+
+In this application you will learn how to:
+
+* Select a series of points on click
+* Override the configuration for the following properties of specific points:
+
+ * Color
+ * Size
+ * Label visibility
+ * Text format of the label
+
+Subclass QMainWindow
+~~~~~~~~~~~~~~~~~~~~
+
+Create a subclass of :py:`QMainWindow` to contain the chart and controls.
+
+.. literalinclude:: chartwindow.py
+ :linenos:
+ :lineno-start: 16
+ :lines: 16-18
+
+Create a line series
+~~~~~~~~~~~~~~~~~~~~
+
+Create a :py:`QLineSeries` containing the points to plot. Give it a name and make the points
+visible.
+
+.. literalinclude:: chartwindow.py
+ :linenos:
+ :lineno-start: 20
+ :lines: 20-28
+
+Create the point configuration controls
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now, create controls to configure the color, size, and label visibility attributes of a point.
+
+#. Create an associated label for each control, so the user knows what the control does.
+#. For the color and size, use a :py:`QComboBox`, populating it with a variety of colors and size
+ choices.
+#. Create the final two controls. Create a :py:`QCheckbox` to control the visibility of the selected
+ point, and a :py:`QLineEdit` to allow the user to provide a custom label for it.
+
+.. note::
+ Do not set initial values for any of the controls, as a point will always be selected showing
+ its current settings.
+
+.. literalinclude:: chartwindow.py
+ :linenos:
+ :lineno-start: 31
+ :lines: 31-52
+
+Populate the controls upon selecting a point
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Add the logic to set the current control values depending on the chosen point. Note that the whole
+series value is used if there is no customization for a selected point. In this case, if the series
+is set to show blue points, a blue color value will be shown in the color combobox.
+
+Perform some actions upon clicking on the lineseries. Look up the point clicked on and remove the
+prior point selection. Finally, select the point that was clicked on. This makes the point larger to
+indicate its selection. The current selected point's index and :py:`PointConfigurations` are saved
+to a member variable for later use.
+
+Query the :py:`PointConfigurations`, and use those to find the matching indices in the combo boxes.
+Set the current indices of the comboboxes to the corresponding values you looked up. Similarly,
+look up the values in :py:`PointConfigurations`, and update the checkbox and line edit controls.
+
+.. literalinclude:: chartwindow.py
+ :linenos:
+ :lineno-start: 54
+ :lines: 54
+.. literalinclude:: chartwindow.py
+ :linenos:
+ :lineno-start: 97
+ :lines: 97-132
+
+Provide the logic to configure the selected point
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that the controls are populated with some values, add logic to do something when the value
+changes. Connect the control signals and the logic, to configure the selected point based on the
+chosen values in the controls. You can do this by setting the :py:`QXYSeries::PointConfiguration`
+value that is associated with the control, to the :py:`m_selectedPointConfig` and
+:py:`PointConfigurations` member variables, and call :py:`QXYSeries::setPointConfiguration`.
+
+.. literalinclude:: chartwindow.py
+ :linenos:
+ :lineno-start: 55
+ :lines: 55-60
+.. literalinclude:: chartwindow.py
+ :linenos:
+ :lineno-start: 140
+ :lines: 140-156
+
+Create the chart and lay out the controls
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Finally, create the chart and its view, add the series to the chart, create the layout of the
+window, and select an initial point.
+
+.. literalinclude:: chartwindow.py
+ :linenos:
+ :lineno-start: 62
+ :lines: 62-95
+
+In our entrypoint file `pointconfiguration.py`, instantiate the :py:`ChartWindow`, resize it, show
+it, and start the event loop.
+
+.. literalinclude:: pointconfiguration.py
+ :linenos:
+ :lineno-start: 11
+ :lines: 11-17
+
+You now have a fully functioning application that demonstrates how to customize individual chart
+points.
+
+Usage
+-----
+To use this example, click any point you'd like to customize, change any of the comboboxes and
+checkboxes controlling the individual point color, size, label visibility. You can customize the
+label text in the line edit at the bottom.
+
+There are three special formatting strings for the label that you can use: ``@pointX``, ``@pointY``,
+and ``@index``. These are replaced with the x value, y value, and index of the point, respectively.
+More information about that can be found in the documentation for
+`QtCharts.QXYSeries.pointLabelsFormat`_.
+
+.. _`QtCharts.QXYSeries.pointLabelsFormat`: https://doc.qt.io/qtforpython/PySide6/QtCharts/QXYSeries.html#PySide6.QtCharts.PySide6.QtCharts.QXYSeries.pointLabelsFormat
diff --git a/examples/charts/pointconfiguration/pointconfiguration.py b/examples/charts/pointconfiguration/pointconfiguration.py
new file mode 100644
index 000000000..d8c90d2df
--- /dev/null
+++ b/examples/charts/pointconfiguration/pointconfiguration.py
@@ -0,0 +1,17 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the Light Markers Points Selection example from Qt v6.2"""
+import sys
+from PySide6.QtWidgets import QApplication
+
+from chartwindow import ChartWindow
+
+
+if __name__ == "__main__":
+
+ a = QApplication(sys.argv)
+ main_window = ChartWindow()
+ main_window.resize(640, 480)
+ main_window.show()
+ sys.exit(a.exec())
diff --git a/examples/charts/pointconfiguration/pointconfiguration.pyproject b/examples/charts/pointconfiguration/pointconfiguration.pyproject
new file mode 100644
index 000000000..c53d798be
--- /dev/null
+++ b/examples/charts/pointconfiguration/pointconfiguration.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["pointconfiguration.py", "chartwindow.py"]
+}
diff --git a/examples/charts/pointselectionandmarkers/doc/pointselectionandmarkers.png b/examples/charts/pointselectionandmarkers/doc/pointselectionandmarkers.png
new file mode 100644
index 000000000..209bc3b0d
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/doc/pointselectionandmarkers.png
Binary files differ
diff --git a/examples/charts/pointselectionandmarkers/doc/pointselectionandmarkers.rst b/examples/charts/pointselectionandmarkers/doc/pointselectionandmarkers.rst
new file mode 100644
index 000000000..e8776daf8
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/doc/pointselectionandmarkers.rst
@@ -0,0 +1,72 @@
+.. role:: py(code)
+ :language: python
+
+Light Markers and Points Selection Example
+==========================================
+
+The Light Markers and Points Selection example shows how to use light markers
+and point selections in a series.
+
+.. image:: pointselectionandmarkers.png
+ :width: 90%
+ :align: center
+ :alt: QChart with Light Markers shown
+
+Creating the chart and its elements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We start by creating a series, filling it with the data, and enabling the light marker and point selection features.
+It is important not to set points visibility to :py:`True`, because light markers functionality is an independent feature and setting both would result in undesired behavior.
+
+.. literalinclude:: pointselectionandmarkers.py
+ :linenos:
+ :lineno-start: 20
+ :lines: 20-42
+ :emphasize-lines: 2-12
+
+Then we create the :py:`QChart`, the :py:`QChartview` and the control widget with its layout to arrange customization elements.
+
+.. literalinclude:: pointselectionandmarkers.py
+ :lineno-start: 44
+ :lines: 44-53
+ :emphasize-lines: 1,6,9
+
+Creating UI for configuring the chart
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The next step is where we create user interface elements that allow customizing the chart, including setting light marker and selection marker images.
+
+.. literalinclude:: pointselectionandmarkers.py
+ :linenos:
+ :lineno-start: 54
+ :lines: 54-57
+
+We create the label for the marker selection combobox and fill the combobox with the items. We then provide functionality to the combobox, allowing the user's selection to set the desired light marker image. As light markers are enabled and disabled by setting a valid QImage or setting an empty :py:`QImage()`, we need to make sure that if the user does not wish unselected points to be displayed, we do not actually set the light marker image.
+If checking isn't performed, a new :py:`QImage` will be set as the light marker and unselected points will be visible even though it has been switched off.
+
+.. literalinclude:: pointselectionandmarkers.py
+ :linenos:
+ :lineno-start: 59
+ :lines: 59-67
+ :emphasize-lines: 1-3
+
+Almost the same procedure applies to the selected point light marker and line color. The only difference is that there is no need to check the visibility of unselected points as it doesn't affect the functionality.
+
+.. literalinclude:: pointselectionandmarkers.py
+ :linenos:
+ :lineno-start: 70
+ :lines: 70-85
+
+A small difference comes with changing visibility of unselected points. As it was mentioned before, making light markers invisible is achieved by setting the light marker to an empty :py:`QImage()`. That is why, depending on checkbox state, selected point light marker is set to an empty :py:`QImage` or to the light marker extracted from the current index of the corresponding combobox.
+
+.. literalinclude:: pointselectionandmarkers.py
+ :linenos:
+ :lineno-start: 88
+ :lines: 88-97
+ :emphasize-lines: 5-6
+
+The final part is to lay out the widgets within the main widget and set the main window size.
+
+Usage
+-----
+To use this example, change any of the comboboxes and checkboxes controlling the markers, line color, and unselected point visibility on the right. Then try clicking on points in the chart to select or deselect them.
diff --git a/examples/charts/pointselectionandmarkers/images/blue_triangle.png b/examples/charts/pointselectionandmarkers/images/blue_triangle.png
new file mode 100644
index 000000000..7790453c8
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/images/blue_triangle.png
Binary files differ
diff --git a/examples/charts/pointselectionandmarkers/images/green_triangle.png b/examples/charts/pointselectionandmarkers/images/green_triangle.png
new file mode 100644
index 000000000..29ae043f2
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/images/green_triangle.png
Binary files differ
diff --git a/examples/charts/pointselectionandmarkers/markers.qrc b/examples/charts/pointselectionandmarkers/markers.qrc
new file mode 100644
index 000000000..eb1e56d19
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/markers.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/blue_triangle.png</file>
+ <file>images/green_triangle.png</file>
+ </qresource>
+</RCC>
diff --git a/examples/charts/pointselectionandmarkers/pointselectionandmarkers.py b/examples/charts/pointselectionandmarkers/pointselectionandmarkers.py
new file mode 100644
index 000000000..4f9540d42
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/pointselectionandmarkers.py
@@ -0,0 +1,127 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the Light Markers Points Selection example from Qt v6.2"""
+import sys
+
+from PySide6.QtCore import Slot, QPointF, Qt
+from PySide6.QtCharts import QChart, QChartView, QSplineSeries
+from PySide6.QtGui import QPainter, QImage
+from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout, QComboBox, QCheckBox, QLabel, QHBoxLayout
+
+import utilities as Utilities
+
+if __name__ == "__main__":
+
+ a = QApplication(sys.argv)
+ window = QMainWindow()
+ window.setWindowTitle("Light Markers and Points Selection")
+
+ marker_size = 20.
+ series = QSplineSeries()
+ series.append([QPointF(0, 0),
+ QPointF(0.5, 2.27),
+ QPointF(1.5, 2.2),
+ QPointF(3.3, 1.7),
+ QPointF(4.23, 3.1),
+ QPointF(5.3, 2.3),
+ QPointF(6.47, 4.1)])
+ series.setMarkerSize(marker_size)
+ series.setLightMarker(Utilities.default_light_marker(marker_size))
+ series.setSelectedLightMarker(Utilities.default_selected_light_marker(marker_size))
+
+ @Slot(QPointF)
+ def toggle_selection(point):
+ try:
+ index = series.points().index(point)
+ if index != -1:
+ series.toggleSelection([index])
+ except ValueError:
+ pass
+
+ series.clicked.connect(toggle_selection)
+
+ chart = QChart()
+ chart.addSeries(series)
+ chart.createDefaultAxes()
+ chart.legend().setVisible(False)
+
+ chart_view = QChartView(chart)
+ chart_view.setRenderHint(QPainter.Antialiasing)
+
+ control_widget = QWidget(window)
+ control_layout = QGridLayout(control_widget)
+ char_point_combobox = QComboBox()
+ char_point_selected_combobox = QComboBox()
+ line_color_combobox = QComboBox()
+ show_unselected_points_checkbox = QCheckBox()
+
+ @Slot(int)
+ def set_light_marker(index):
+ if show_unselected_points_checkbox.isChecked():
+ series.setLightMarker(Utilities.get_point_representation(
+ Utilities.point_type(index), marker_size))
+
+ char_point = QLabel("Char point: ")
+ char_point_combobox.addItems(["Red rectangle", "Green triangle", "Orange circle"])
+ char_point_combobox.currentIndexChanged.connect(set_light_marker)
+
+
+ @Slot(int)
+ def set_selected_light_marker(index):
+ series.setSelectedLightMarker(Utilities.get_selected_point_representation(Utilities.selected_point_type(index), marker_size))
+
+ char_point_selected = QLabel("Char point selected: ")
+ char_point_selected_combobox.addItems(["Blue triangle", "Yellow rectangle", "Lavender circle"])
+ char_point_selected_combobox.currentIndexChanged.connect(set_selected_light_marker)
+
+
+ @Slot(int)
+ def set_line_color(index):
+ series.setColor(Utilities.make_line_color(Utilities.line_color(index)))
+
+ line_color_label = QLabel("Line color: ")
+ line_color_combobox.addItems(["Blue", "Black", "Mint"])
+ line_color_combobox.currentIndexChanged.connect(set_line_color)
+
+
+ @Slot(int)
+ def display_unselected_points(checkbox_state):
+ if checkbox_state:
+ series.setLightMarker(Utilities.get_point_representation(Utilities.point_type(char_point_combobox.currentIndex()), marker_size))
+ else:
+ series.setLightMarker(QImage())
+
+ show_unselected_points_label = QLabel("Display unselected points: ")
+ show_unselected_points_checkbox.setChecked(True)
+ show_unselected_points_checkbox.stateChanged.connect(display_unselected_points)
+
+
+ control_label = QLabel("Marker and Selection Controls")
+ control_label.setAlignment(Qt.AlignHCenter)
+ control_label_font = control_label.font()
+ control_label_font.setBold(True)
+ control_label.setFont(control_label_font)
+ control_layout.addWidget(control_label, 0, 0, 1, 2)
+ control_layout.addWidget(char_point, 1, 0)
+ control_layout.addWidget(char_point_combobox, 1, 1)
+
+ control_layout.addWidget(char_point_selected, 2, 0)
+ control_layout.addWidget(char_point_selected_combobox, 2, 1)
+
+ control_layout.addWidget(line_color_label, 3, 0)
+ control_layout.addWidget(line_color_combobox, 3, 1)
+
+ control_layout.addWidget(show_unselected_points_label, 4, 0)
+ control_layout.addWidget(show_unselected_points_checkbox, 4, 1, 1, 2)
+ control_layout.setRowStretch(5, 1)
+
+ main_widget = QWidget(window)
+ main_layout = QHBoxLayout(main_widget)
+ main_layout.addWidget(chart_view)
+ main_layout.addWidget(control_widget)
+
+ window.setCentralWidget(main_widget)
+ window.resize(1080, 720)
+ window.show()
+ sys.exit(a.exec())
diff --git a/examples/charts/pointselectionandmarkers/pointselectionandmarkers.pyproject b/examples/charts/pointselectionandmarkers/pointselectionandmarkers.pyproject
new file mode 100644
index 000000000..8c394457c
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/pointselectionandmarkers.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["pointselectionandmarkers.py", "utilities.py", "markers.qrc"]
+}
diff --git a/examples/charts/pointselectionandmarkers/rc_markers.py b/examples/charts/pointselectionandmarkers/rc_markers.py
new file mode 100644
index 000000000..f5a9cd42b
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/rc_markers.py
@@ -0,0 +1,275 @@
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.3.1
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x05m\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x02\x00\x00\x00\x01\xf6\x02\x03\x00\x00\x00{5\xdc\xf0\
+\x00\x00\x00\x09PLTE\x00\x00\x00\x98\xbag\x98\xba\
+g\xb0,\xf9`\x00\x00\x00\x02tRNS\x00\x93\x1f\
+\x95\x0f\xc6\x00\x00\x05\x11IDATx\xda\xdc\xd31\
+\x95\xc4@\x0c\x04\xd1U\x22\x10Bc<Bc\x10N\
+\x5c(\x0fC\xfb\xde\x8c\xe5\xeeX\xc1\x0fT\xbf\x7f.\
+~//\x8f\xdf\xbb\xab\xf3\xf7\xee\xfa\xfa\xbd\xba\xe0\x96\
+\xee\xfd\x00\x09\xd2\x17\xfa\x01\x0a\xa4\x0c\xfc\x00\x0dR\x87\
+v\x80\x00\xa9C?@\x82\xd4\xa1\x1f\xa0@\xea\xd0\x0f\
+\xd0 uh\x07\x08\x90:\xf4\x03$H\x1d\xfa\x01\x0a\
+\xa4\x0e\xfd\x00\x0dR\x87v\x80\x00\xa9C?@\x82\xd4\
+\xa1\x1f\xa0@\xea\xd0\x0f\xd0 uh\x07\x08\x90:\xf4\
+\x03$H\x1d\xfa\x01\x0a\xa4\x0e\xfd\x00\x0dR\x87v\x80\
+\x00\xa9C?@\x82\xd4\xa1\x1f\xa0@\xea\xd0\x0f\xd0 \
+uh\x07\x08\x90:\xf4\x03$H\x1d\xfa\x01\x0a\xa4\x0e\
+\xfd\x00\x0dR\x87v\x80\x00\xa9C?@\x82\xd4\xa1\x1f\
+\xa0@\xea\xd0\x0f\xd0 uh\x07\x08\x90:\xf4\x03$\
+H\x1d\xfa\x01\x0a\xa4\x0e\xfd\x00\x0dR\x87v\x80\x00\xa9\
+C?@\x82\xd4\xa1\x1f\xa0@\xea\xd0\x0f\xd0\xf0n\x87\
+\xcd\xbb\x19\x04#\x01\x1b3\xc8\xa1\x80}\x19\xd4P\xc0\
+\xbe\x0e{(`[\x87\xc1P\xc0\xb6\x0es,`W\
+\x875\x16\xb0\xab\xc3\x1e\x0b\xd8\xd4a0\x16\xb0\xa9\xc3\
+\x1c\x0c\xd8\xd3a\x0d\x06\xec\xe9\xb0\x07\x03\xb6t\x18\x0c\
+\x06l\xe90G\x03vtX\xa3\x01;:\xec\xd1\x80\
+\x0d\x1d\x06\xa3\x01\x1b:\xcc\xe1\x80\xf5\x1d\xd6p\xc0\xfa\
+\x0e{8`y\x87\xc1p\xc0\xf2\x0es<`u\x87\
+5\x1e\xb0\xba\xc3\x1e\x0fX\xdca0\x1e\xb0\xb8\xc3\xfc\
+\x00`m\x87\xf5\x01\xc0\xda\x0e\xfb\x03\x80\xa5\x1d\x06\x1f\
+\x00,\xed0?\x01X\xd9a}\x02\xb0\xb2\xc3\xfe\x04\
+`a\x87\xc1'\x00\x0b;\xcc\x8f\x00\xd6uXo\x03\
+\x1a\xa4\x0e\xed\x00\x01R\x87~\x80\x04\xa9C?@\x81\
+\xd4\xa1\x1f\xa0A\xea\xd0\x0e\x10 u\xe8\x07H\x90:\
+\xf4\x03\x14H\x1d\xfa\x01\x1a\xa4\x0e\xed\x00\x01R\x87~\
+\x80\x04\xa9C?@\x81\xd4\xa1\x1f\xa0A\xea\xd0\x0e\x10\
+(\xbb\x0d\x01\x89\xb4\xc3\x0fPH;\xfd\x00\x8d\xb4\xcb\
+\x0e\x10h\xbb\xed\x00\x89\xb8\xc3\x0dP\x88;\xdd\x00\x8d\
+\xb8\xcb\x0c\x10\xa8\xbb\xcd\x00\x89\xbc\xc3\x0bP\xc8;\xbd\
+\x00\x8d\xbc\xcb\x0a\x10\xe8\xbb\xad\x00\x09\xfa\x0e'@\x81\
+\xbe\xd3\x09\xd0\xa0\xef2\x02\x04Ov\x1b\x01\x92G;\
+|\x00\xc5\xa3\x9d>\x80\xe6\xd1.\x1b@\xf0l\xb7\x0d\
+ y\xb8\xc3\x05P<\xdc\xe9\x02h\x1e\xee2\x01\x04\
+Ow\x9b\x00\x92\xc7;<\x00\xc5\xe3\x9d\x1e\x80\xe6\xf1\
+.\x0b@\xf0|\xb7\x05\xe0\x8f{:(\x82(\x06\x81\
+ Z\xb9 \x22j\xd0\x83\x1aDpaT\xae\x86\xd4\
+\x87,\x93V\xf0.-\xf8\x90\xbe\x00\xd8\xf8\x90\xbf\x00\
+0|(\x1e\x00,|)\x1f\x00\x08>\xa5\xfc\x80\x8d\
+O9?\xc0\xf0\xa9\xa0\x07,|+\xe9\x01\x82\x8f)\
+;`\xe3c\xce\x0e0|,\xc8\x01\x0b_Kr\x80\
+\xe0s\xca\x0d\xd8\xf8\x9cs\x03\x0c\x9f\x0bj\xc0\xc2\xf7\
+\x92\x1a (H\x99\x01\x1b\x0593\xc0PP\x10\x03\
+\x16*Jb\x80\xa0$\xe5\x05l\x94\xe4\xbc\x00CI\
+A\x0bX\xa8)i\x01\x82\xa2\x94\x15\xb0Q\x94\xb3\x02\
+\x0cE\x05)`\xa1\xaa$\x05\x08\xcaRN\xc0FY\
+\xce\x090\x94\x15\x94\x80\x85\xba\x92\x12 (L\x19\x01\
+\x1b\x859#\xc0PX\x10\x02\x16*KB\x80\xa04\
+\xe5\x03l\x94\xe6|\x00CiA\x07X\xa8-\xe9\x00\
+\x82\xe2\x94\x0d\xb0Q\x9c\xb3\x01\x0c\xc5\x05\x19`\xa1\xba\
+$\x03\x08\xcaS.\xc0Fy\xce\x050\x94\x17T\x80\
+\x85\xfa\x92\x0a hH\x99\x00\x1b\x0d9\x13\xc0\xd0P\
+\x10\x01\x16:J\x22\x80\xa0%\xe5\x01l\xb4\xe4<\x00\
+CKA\x03X\xe8)i\x00\x82\xa6\x94\x05\xb0\xd1\x94\
+\xb3\x00\x0cM\x05\x09`\xa1\xab$\x01\x08\xdaR\x0e\xc0\
+F[\xce\x010\xb4\x15\x14\x80\x85\xbe\x92\x02 hL\
+\x19\x00\x1b\x8d9\x03\xc0\xd0X\x10\x00\x16:K\x02\x80\
+\xa05\x9d\x0f\xd8h\xcd\xe7\x03\x0c\xad\xc5x\xc0Bo\
+9\x1e hN\xa7\x036\x9a\xf3\xe9\x00Cs1\x1c\
+\xb0\xd0]\x0e\x07\x08\xda\xd3\xd9\x80\x8d\xf6|6\xc0\xd0\
+^\x8c\x06,\xf4\x97\xa3\x01\x82\x0b\xe9d\xc0\xc6\x85|\
+2\xc0p\xa1\x18\x0cX\xb8Q\x0e\x06\x08\xae\xa4s\x01\
+\x1bW\xf2\xb9\x00\xc3\x95b,`\xe1N9\x16 \xb8\
+\x94N\x05l\x5c\xca\xa7\x02\x0c\x97\x8a\xa1\x80\x85[\xe5\
+P\x80\xe0Z:\x13\xb0q-\x9f\x090\x5c+F\x02\
+\x16\xee\x95#\x01\x82\x8b\xe9D\xc0\xc6\xc5|\x22\xc0p\
+\xb1\x18\x08X\xb8Y\x0e\x04\x08\xae\xa6\xff\x06\xf8\xe1\x85\
+\xef\x01\xe2\xf0\xc2\xf7\x00yx\xe1{\x00\xe8\xe1\x85\xef\
+\x01\xfc\xf0\xc2\xf7\x00qx\xe1{\x80<\xbc\xf0=\x00\
+\xf4\xf0\xc2\xf7\x00~x\xe1{\x808\xbc\xf0=@\x1e\
+^\xf8\x1e\x00zx\xe1{\x00?\xbc\xf0=@\x1c^\
+\xf8\x1e \x0f/|\x0f\x00=\xbc\xf0=\x80\x1f^\xf8\
+\x1e\xe0\xd7=\x1d\x13\x01\x00\xc30\x0c\xa4f\x88\x01\x91\
+%FY\x0c\x9az'\x06\xbf\xfc\xc2\x85>\xc0\xc1\x85\
+>@\x03\x17\xfa\x00\x03\x17\xfa\x00\x0b\x17\xfa\x00\x07\x17\
+\xfa\x00\x0d\x5c\xe8\x03\x0c\x5c\xe8\x03,\x5c\xe8\x03\x1c\x5c\
+\xe8\x034p\xa1\x0f0p\xa1\x0f\xb0p\xa1\x0fpp\
+\xa1\x0f\xd0\xfc]\xd8\xe6\x01\x9f\xda\xf9\x10\xf2\x1c\xc7\xb0\
+\x00\x00\x00\x00IEND\xaeB`\x82\
+\x00\x00\x08\xac\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x01\x00\x00\x00\x00\xfb\x08\x03\x00\x00\x00\xb2\x8e\xba:\
+\x00\x00\x00\x8dPLTE\x00\x00\x00i\xba\xf0i\xba\
+\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0\
+i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\
+\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\
+\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0\
+i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\
+\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\
+\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0\
+i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\xba\xf0i\
+\xba\xf0i\xba\xf0\x9c\xb2p\x14\x00\x00\x00.tRN\
+S\x00\x05\xf7\xfb\x19\x13U\x09\xf3\xe2H\x1d\x94z\x0f\
+0\xdf\xc9e\xec\xcd3\x22\xa37\x86\xda\xc0tO\xac\
+\xe5\xb8\x80\xb3k\xe9\x9a\xa7\xc5*\xd4@_\x8e:=\
+\xa4\x0e\xbb\x00\x00\x07\xa0IDATx\xda\xe4\xdd\x07\
+b\xaa@\x10\x80av\x11\x10i\x8a\x82=\xf6\xae{\
+\xff\xe3=\xf2L\x1c\xc0(b\x81e\xe6?\x020\xbb\
+\x9f\x94D)3V\xdbnk.S\xc8\xc6\xc2\xfd\xde\
+\xb3\x5c\x85ll\xef\xf4G\xfa\x96\xee%\xe0\xfa\xc3\xce\
+\xd4\x1e\x1b\x0a\xd5\xc2Y\x93O\xa6\xbe\xa6\xd0\x8ci\xfb\
+U[\x88\xc1,Th\xa6\xf5\x16s.D}\xb5\xd7\
+h\xae\x02\x96\xdf\xdd\xa9B\xf0\xe9\xe2Ds\x08\x02{\
+P\xff>\x00\xbb\xaeOr\x08\x8c\xf1\x9c\x8b(\xb5\xdd\
+9\x06\x0a\xb9\x98\xeb-Z\xe2\x5c}\xde\xa0\xb7\x08\xb0\
+\xadn\x0f\xc4O\x1d\xdf\x22g\x01\xa3\xd1\x1f\xb6\xc5O\
+M[\x0f\xa9]\x03\xae?\x9dp\xf1S{xlP\
+\xbb\x04\xc2\xd9@\x15\xbfq\x93\x1a\x07\xcf\x08\x84Tj\
+\x1c\xd4z\xfe\x9c\x8bK\xf48\x18\x9e\x11\x08E\x1c\xec\
+Q\x1a\x823\x02/\x91\xe3\xa01\x9er\x91Hm\xb7\
+\x8e=\x85H\xcc\xdd\x8e:\xe2\x12=\x0e\x02\x02\x13u\
+\x16\x16\x91#\x00\x08\xfc\x1f=\x0e\x02\x02\xffG\x8f\x83\
+\x09\x04\x0ar\x1cL!\x90\x1e\x07\xb5 \x81@z\x1c\
+\x04\x04^\xa5\x92\xe0 \xf0*\x95\x02\x07\x01\x81\xd7\
+\xa9K\xf4\x1cL#\xf0\x9a\x83c\xdc\x8b\xc07\x02\x9b\
+\xe2\x12=\x0e\x02\x02od\xdac\xd4\x1c\x8c\x10hr\
+q\x89\x1e\x07\x93\x08$\xc7\xc1\x14\x02\xe9q0\x85\xc0\
+[\x1c<\xa1}k(\xf4Wq\x04\xd2\xe3`\xcf\xde\
+\xd5\x05D\x8e\x83I\x04\xde\xe3\xe0IA\x18 0\xab\
+\xfaT\xc78\x03\x06 0\xab\x9d\xe3!\xfcY\x0c\x08\
+\xccl\xd2\xf5=|\x07@\x03\x04fU\xeftu|\
+\xaf\x8ff 0\xf5\x94dd)\xb8\x02\x04>\xd4\xe4\
+\x88m\x06\x00\x81\x0f\xc5\xd7\xe3\x10\xd7o\xa2\xf0\xb0\xda\
+\x89\x1c\xb5\xfa\xe3\x9a\x82)@\xe0C\xa9\xe6t\xe6)\
+\x88b\x80\xc0\xc7\xe2f\x17\xd1\xb3R@\xe0\xe3\xa9\xc3\
+C\x0d\xcd\x11\x00\x04\xe6h0;\xa19\x02\x11\x02\xa7\
+m\x91\xb3\xc9z\x11`\xd9\x08\x22\x046\xb9\xb8D\x8f\
+\x83\x11\x02\xb9\xc8\x9b\xda\xde \xe1`\x0a\x81\xf48\xa8\
+y\x80\xc0\x5c\xf1\xaf1\x8aw\xa8\x01\x81yk\xcd\x1a\
+\x188\x98D`>\x0e:\x088\xc8t@`\xcex\
+\xb3\xfa\x1cL!\x90\x1e\x07S\x08\xa4\xc7A@\xe0S\
+-+\xcfA\xcd\x9f\xc7\x11\x98\x9f\x83\xab\x8as\xf0\x1b\
+\x81\xaax:^q\x0e\xa6\x10H\x8f\x83\xdf\x08\x8c\x9d\
+\x7fz\x1cL\x22\x90 \x07\x01\x81\xcfW\xe5\xbb\x83I\
+\x04\x92\xe3 \xf0\xb5\xd4ME9\x98B =\x0e\
+\xa6\x10\xf8\x0a\x07\xfdJr0\x85@z\x1c\xdc\xf6\x01\
+\x81/\xc5\x97U\xe4\xa0a5\xba\xb1\x01\xa0\xc7\xc1\xda\
+~4\x8c\x9d\x7fz\x1c\xdc\x8e\xbeb[\x00A\x0e\xee\
+\xd7\xf1O\xe3\xe8q\xd0=\xb4\xc4\x1b\xe3\xe6\xbaR\x1c\
+d\xb5S\x7f \xde\x99\xba\xf1\xab\x84!\xa3\xb7XO\
+\xc4%z\x1c\xd4\xf4U\xa7..\xd1\xe3\xa05j\xb5\
+Uq\x89\x1c\x07\x99g'\x06\x80\x1a\x07\x8dP\xff\xe2\
+\xe2\xedM\xec\xaap\xd0j\xcc6\xaax{je8\
+\xe89\xf3\x18\x02\x09r\xb0\x01\x08|kUyX\xec\
+\x1e:\xe2#\xf1f\x158\x08\x08|\x7f\xbc\x0a\x1c\x04\
+\x04~\xa0A_~\x0eF\x08l%\x11H\x8c\x83W\
+\x08\xa4\xc6\xc14\x02\x89q0\x13\x81\xafs0\x90z\
+\x11\xf8\x0b\x81\xa48\xe8\xfd}'\x90\x0e\x07\x1bk\x93\
+\x8b\xab\xe8p\xf0\x16\x02\x89p\xf0c\x08LsP\xd6\
+\x19\xb8\x87@\x12\x1c\xd4t\xfb&\x02Ip\x10\x10\xf8\
+\xd1\xea\xad\x95\xa4\x9f\xd7\xdfF \x09\x0e\x02\x02?\x9e\
+)%\x07\xef#\x90\x00\x07\x01\x81\x9f\xaf\xe5H\xc8\xc1\
+L\x04\x22\xe7\xa0\xebg \x107\x07\x99\xd6\xebg\x0d\
+\x00j\x0e\xba=\xbf\x00\x04&8\xd8\x93\x8a\x83\x80\xc0\
+\x82jK\xc6Ak\xb4Y\xaa\x22+\xc4\x1c\x04\x04\x16\
+\x14\x9f\x0c%\xe2 \xb0\xb8T\x998X\x14\x02\xd3\
+\x1c\xac\xc9\xb2\x0a\xe4A J\x0e\x02\x02\x8b\xcc\x9c;\
+\x92\xfc\x8f\xb6\x5c\x08\xc4\xc7\xc1\x9c\x08\xc4\xc7\xc1o\x04\
+.E\xb1\x01\x07%\xf8\xbb\x83\x80\xc0\xc2kw\x0f^\
+\xf9\x1b\x81\xe5\x14\x88\xc04\x07m\x098\xe8\xd9%\x0c\
+\x00p0T\xca\xcd\xb0\xf2\x22\x10\x19\x07#\x04\xb6J\
+\x19\x00\xe0`\xb9G (\x1c\x81i\x0e\xee\xcb\xdd\x0a\
+\x1b\xeb'>\x8dC\xc4\xc12\x10\x98\xe6\xe08\xe7\x0c\
+\xa0@ \xc4[\x8b\xf2f\xc0\x0d\xfc\xeeR\x94\x5c3\
+/\x07q P\x0e\x0e>\x8d@,\x1c\xf4\xec\xd2\x07\
+\xa0\x14\x0e\x02\x02\xe7\x5c\x94_\xc4\xc1\x1eS\x0a\x0d\x10\
+(dH\x9d?\xceAL\x08,\x9d\x83\x11\x02e\x98\
+\x80|\x1c\xc4\x84\xc08\x07\xbf\x1e\xe3 2\x04\x96\xca\
+A78\x94\x8f\xc0R8\x08\x08\xdc\x94\x8e\xc0\xbc\x1c\
+D\x87\xc029\xe8\xad$\x1a\x80\xff\x1ct\xb28\x88\
+\x11\x81y8\x88\x13\x81y8\x88\x13\x81P\xa7H\x0e\
+\x8e\xd7\x03\x89\xb6\x80\xe29\xa8\xc9\x83@\xa8~\x97\x83\
+x\x11\xf80\x07\xd1\x22\xb0x\x0e\xd6\xf4\xa3L\x08|\
+\x90\x83x\x11X<\x07\x03\xb9\x10\xf8\x10\x07Q#0\
+\xc9A\xe5\xaf\x90#0\x9b\x83\xd8\x11X$\x07\x99\x8c\
+\x08,\x92\x83\xda\xa2#\xe5\x0eP\x10\x07\x99\x16H\x89\
+\xc0\xc28\xe8z\x92\x22\xf0\x1e\x07I \xb0(\x0eZ\
+\xcep\x22\xf3\x12\xf0'\x07I \xf0\xd3\x1c\x04\x04J\
+~\xfe\xaf\xef\x0e\x92A`\x9c\x83\xfag8\xc8zR\
+#\xf0\x06\x07\xe9 \xf0o\x0e\x12B\xe0\xa79\xc8\xdc\
+\xa0o\x8aj\x04\x1c|3\x02\xdb\xa2\x22\xfdr\x90\x18\
+\x02?\xcb\xc1P~\x04B\xfc\xccAj\x08\xfc$\x07\
+Y%\x10\x08\x16R\xcdU\xef\xdd\x08\x94\xf0iX\x16\
+\x07\x09\x220\xc9A\x82\x08Lr\x90 \x02\xa1\xfa \
+\xe2 A\x04&9H\x11\x81)\x0e\xd2C`\x8a\x83\
+\xf4\x10\x08\xf1\xd6Q\xd7\xe8!\xf0\xfd\x1cd5}Z\
+\xc1\xf3\x1f\xc5\xcd\xd5\x89\x11D`\x8a\x83\x04\x11\xf8^\
+\x0e\xb2q\xb7j\x08\x84\xcc/' \x88\xc0\x14\x07\x09\
+\x220\xc9A\x8a\x08\x8cs0p\xd9+\x08\x1c\xf7\xab\
+\x88\xc0\x04\x07\xb7\x06=\x04&9X\xa3\x87\xc0\x14\x07\
+\x09\x220\xc9A\x8a\x08\x84\xd4i\xc4A\x82\x08|\x89\
+\x83\x80\xc0]\xa5\xb7\x80_\x0e\x8e\x82\xa7\x11(\xeb[\
+\xc199\xa83\x82\x08Lr\x90\x22\x02!\x138\x98\
+\x13\x81\x18& \xaa\xbe\xd6\xb7.=\x04\xbe\xca\xc1`\
+\x85d\x00~8H\x10\x81I\x0e2\x82\x08|\x8a\x83\
+\x80@\x1c{\xe0\x93\x1c4p 0\xc9A\x82\x08\x8c\
+sp\xad\xb3\x1c\x08\xf4\x90 \xf0\x92\x9a\x8b\x83\xeeV\
+\xc7\x82\xc0\x9c\x1c\x04\x04\x0eqM@T\xbd\xabo\x0d\
+z\x08\x84\xf8\xe6q\x0e\xf6d\xff2\xea\x99Ts\xe8\
+l\x1fF\xa0\xc0\x97\xfa\xe8\xddA\x86\x0c\x81)\x0e\x12\
+D \xb4\x8b8\xc8\xe8!\x10\x9a\xcc\x9d\x13\xa3\x87@\
+\x88O\xa6\x07#k\x00\x0ct\x08\x8c\xa5\xee\x9c\x90\x22\
+\x02\xa1\xa5\xbd\xaf\x19\xf4\x10\x08\xf1\xf9\xe2\xa4\xd1C`\
+|\x06\xd6\xbeu\x1f\x81\x98\x07@\x08\xb5>\xb0\xbd\xfb\
+\x08\x1c\x0a\xdc\xd5\xbf\x1a\x06A\x04\xc6\xda,<\x8d \
+\x02\xa1\x81\xado\xd9-\x04\xeah\x11\x08\xb5\x87\xc7=\
+\xbb\x83@\xc4[\xc09\xbe\x1c\x1e\x0c\x82\x08\x8c5p\
+B\x8a\x08Lr\x90\xd1C\xe0\x1d\x0e\x02\x02M\xf4+\
+\xc0\x99\x83]\xdf\xa2\x87\xc0,\x0e2\x0d=\x02!>\
+o\x18\x7f p'\xc8\xf4\xcdA\x82\x08\xfcW\x9e\x1d\
+\xa3\x00\x08\x03A\x14]\x95\x10Q\x12Q,D,\x84\
+46z\xff\xe3i\xaau\xd6\xca63\x97\xf8\x0f\xc6\
+p\x90\x0f\x81\xc0\xc1\xae\xb2\x08l)\x12\xa0\x1c\x8c\xf2\
+\x1a\x0b\x02\x81\x83\x8c\x084\x1c\xe4C pp\xd1\x10\
+\x1ca\xe7@\xa0\xe1`\xd9w\xf0\x0f\x0e2!\x109\
+\xc8\x88@\xdd\x98\x86(\xcf\xfcI\x85\xc0/\x07\x1d\x17\
+\x02\x91\x83>7p\xa3B pp\xca%\xec\xc3z\
+Q\xae\xa9\xe7\xe4\xe4\x06\xc7Z\x00\x1d\x81h\xe2\xc4\x00\
+\x00\x00\x00IEND\xaeB`\x82\
+"
+
+qt_resource_name = b"\
+\x00\x06\
+\x07\x03}\xc3\
+\x00i\
+\x00m\x00a\x00g\x00e\x00s\
+\x00\x12\
+\x0c\xf7v\xe7\
+\x00g\
+\x00r\x00e\x00e\x00n\x00_\x00t\x00r\x00i\x00a\x00n\x00g\x00l\x00e\x00.\x00p\x00n\
+\x00g\
+\x00\x11\
+\x02\xf5Q\x07\
+\x00b\
+\x00l\x00u\x00e\x00_\x00t\x00r\x00i\x00a\x00n\x00g\x00l\x00e\x00.\x00p\x00n\x00g\
+\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00<\x00\x00\x00\x00\x00\x01\x00\x00\x05q\
+\x00\x00\x01\x82`\x07\x0a\xa2\
+\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x82`\x07!\xf4\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
diff --git a/examples/charts/pointselectionandmarkers/utilities.py b/examples/charts/pointselectionandmarkers/utilities.py
new file mode 100644
index 000000000..6b96d6e26
--- /dev/null
+++ b/examples/charts/pointselectionandmarkers/utilities.py
@@ -0,0 +1,67 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtGui import QImage, QPainter, QColor
+from PySide6.QtCore import Qt
+
+import rc_markers
+
+def rectangle(point_type, image_size):
+ image = QImage(image_size, image_size, QImage.Format_RGB32)
+ painter = QPainter()
+ painter.begin(image)
+ painter.setRenderHint(QPainter.Antialiasing)
+ painter.fillRect(0, 0, image_size, image_size, point_type[2])
+ painter.end()
+ return image
+
+def triangle(point_type, image_size):
+ return QImage(point_type[3]).scaled(image_size, image_size)
+
+def circle(point_type, image_size):
+ image = QImage(image_size, image_size, QImage.Format_ARGB32)
+ image.fill(QColor(0, 0, 0, 0))
+ painter = QPainter()
+ painter.begin(image)
+ painter.setRenderHint(QPainter.Antialiasing)
+ painter.setBrush(point_type[2])
+ pen = painter.pen()
+ pen.setWidth(0)
+ painter.setPen(pen)
+ painter.drawEllipse(0, 0, image_size * 0.9, image_size * 0.9)
+ painter.end()
+ return image
+
+_point_types = [("RedRectangle", rectangle, Qt.red),
+ ("GreenTriangle", triangle, Qt.green, ":/images/green_triangle.png"),
+ ("OrangeCircle", circle, QColor(255, 127, 80))]
+_selected_point_types = [("BlueTriangle", triangle, Qt.blue, ":/images/blue_triangle.png"),
+ ("YellowRectangle", rectangle, Qt.yellow),
+ ("LavenderCircle", circle, QColor(147, 112, 219))]
+_line_colors = [("Blue", QColor(65, 105, 225)), ("Black", Qt.black), ("Mint", QColor(70, 203, 155))]
+
+def point_type(index):
+ return _point_types[index]
+
+def selected_point_type(index):
+ return _selected_point_types[index]
+
+def line_color(index):
+ return _line_colors[index]
+
+
+def default_light_marker(image_size):
+ return rectangle(_point_types[0], image_size)
+
+def default_selected_light_marker(image_size):
+ return triangle(_selected_point_types[0], image_size)
+
+
+def get_point_representation(point_type, image_size):
+ return point_type[1](point_type, image_size)
+
+def get_selected_point_representation(point_type, image_size):
+ return point_type[1](point_type, image_size)
+
+def make_line_color(line_color):
+ return line_color[1]
diff --git a/examples/charts/qmlpolarchart/qmlpolarchart.py b/examples/charts/qmlpolarchart/qmlpolarchart.py
index f12e6f5e8..6391fc305 100644
--- a/examples/charts/qmlpolarchart/qmlpolarchart.py
+++ b/examples/charts/qmlpolarchart/qmlpolarchart.py
@@ -8,8 +8,8 @@ from pathlib import Path
import sys
from PySide6.QtQuick import QQuickView
-from PySide6.QtCore import Qt, QUrl
-from PySide6.QtWidgets import QApplication, QMainWindow
+from PySide6.QtCore import QUrl
+from PySide6.QtWidgets import QApplication
if __name__ == '__main__':
diff --git a/examples/corelib/settingseditor/settingseditor.py b/examples/corelib/settingseditor/settingseditor.py
index 266ddda4e..a77349e5e 100644
--- a/examples/corelib/settingseditor/settingseditor.py
+++ b/examples/corelib/settingseditor/settingseditor.py
@@ -7,7 +7,7 @@
import sys
from PySide6.QtCore import (QByteArray, QDate, QDateTime, QDir, QEvent, QPoint,
- QRect, QRegularExpression, QSettings, QSize, QTime, QTimer, Qt)
+ QRect, QRegularExpression, QSettings, QSize, QTime, QTimer, Qt, Slot)
from PySide6.QtGui import (QAction, QColor, QIcon, QIntValidator,
QDoubleValidator, QRegularExpressionValidator, QValidator)
from PySide6.QtWidgets import (QAbstractItemView, QApplication,
@@ -144,6 +144,7 @@ class MainWindow(QMainWindow):
self.setWindowTitle("Settings Editor")
self.resize(500, 600)
+ @Slot()
def open_settings(self):
if self.location_dialog is None:
self.location_dialog = LocationDialog(self)
@@ -156,6 +157,7 @@ class MainWindow(QMainWindow):
self.set_settings_object(settings)
self.fallbacks_action.setEnabled(True)
+ @Slot()
def open_inifile(self):
file_name, _ = QFileDialog.getOpenFileName(self, "Open INI File",
'', "INI Files (*.ini *.conf)")
@@ -170,6 +172,7 @@ class MainWindow(QMainWindow):
self.set_settings_object(settings)
self.fallbacks_action.setEnabled(False)
+ @Slot()
def open_property_list(self):
file_name, _ = QFileDialog.getOpenFileName(self,
"Open Property List", '', "Property List Files (*.plist)")
@@ -179,6 +182,7 @@ class MainWindow(QMainWindow):
self.set_settings_object(settings)
self.fallbacks_action.setEnabled(False)
+ @Slot()
def open_registry_path(self):
path, ok = QInputDialog.getText(self, "Open Registry Path",
"Enter the path in the Windows registry:",
@@ -189,19 +193,20 @@ class MainWindow(QMainWindow):
self.set_settings_object(settings)
self.fallbacks_action.setEnabled(False)
+ @Slot()
def about(self):
QMessageBox.about(self, "About Settings Editor",
"The <b>Settings Editor</b> example shows how to access "
"application settings using Qt.")
def create_actions(self):
- self._open_settings_act = QtGui.QAction("&Open Application Settings...",
+ self._open_settings_act = QAction("&Open Application Settings...",
self, shortcut="Ctrl+O", triggered=self.openSettings)
- self._open_ini_file_act = QtGui.QAction("Open I&NI File...", self,
+ self._open_ini_file_act = QAction("Open I&NI File...", self,
shortcut="Ctrl+N", triggered=self.openIniFile)
- self._open_property_list_act = QtGui.QAction("Open macOS &Property List...",
+ self._open_property_list_act = QAction("Open macOS &Property List...",
self, shortcut="Ctrl+P", triggered=self.openPropertyList)
def create_actions(self):
@@ -473,6 +478,7 @@ class SettingsTree(QTreeWidget):
def sizeHint(self):
return QSize(800, 600)
+ @Slot(bool)
def set_auto_refresh(self, autoRefresh):
self.auto_refresh = autoRefresh
@@ -483,15 +489,18 @@ class SettingsTree(QTreeWidget):
else:
self.refresh_timer.stop()
+ @Slot(bool)
def set_fallbacks_enabled(self, enabled):
if self.settings is not None:
self.settings.setFallbacksEnabled(enabled)
self.refresh()
+ @Slot()
def maybe_refresh(self):
if self.state() != QAbstractItemView.EditingState:
self.refresh()
+ @Slot()
def refresh(self):
if self.settings is None:
return
diff --git a/examples/corelib/threads/mandelbrot.py b/examples/corelib/threads/mandelbrot.py
index d92e8b604..b8cab06ef 100644
--- a/examples/corelib/threads/mandelbrot.py
+++ b/examples/corelib/threads/mandelbrot.py
@@ -9,7 +9,7 @@ import sys
from PySide6.QtCore import (Signal, QMutex, QElapsedTimer, QMutexLocker,
QPoint, QPointF, QSize, Qt, QThread,
- QWaitCondition)
+ QWaitCondition, Slot)
from PySide6.QtGui import QColor, QImage, QPainter, QPixmap, qRgb
from PySide6.QtWidgets import QApplication, QWidget
@@ -302,6 +302,7 @@ class MandelbrotWidget(QWidget):
delta_y = (self.height() - self.pixmap.height()) / 2 - self._pixmap_offset.y()
self.scroll(delta_x, delta_y)
+ @Slot(QImage,float)
def update_pixmap(self, image, scale_factor):
if not self._last_drag_pos.isNull():
return
diff --git a/examples/datavisualization/graphgallery/axesinputhandler.py b/examples/datavisualization/graphgallery/axesinputhandler.py
new file mode 100644
index 000000000..7f721d4aa
--- /dev/null
+++ b/examples/datavisualization/graphgallery/axesinputhandler.py
@@ -0,0 +1,101 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from enum import Enum
+from math import sin, cos, degrees
+
+from PySide6.QtCore import Qt
+from PySide6.QtDataVisualization import QAbstract3DGraph, Q3DInputHandler
+
+
+class InputState(Enum):
+ StateNormal = 0
+ StateDraggingX = 1
+ StateDraggingZ = 2
+ StateDraggingY = 3
+
+
+class AxesInputHandler(Q3DInputHandler):
+
+ def __init__(self, graph, parent=None):
+ super().__init__(parent)
+ self._mousePressed = False
+ self._state = InputState.StateNormal
+ self._axisX = None
+ self._axisZ = None
+ self._axisY = None
+ self._speedModifier = 15.0
+
+ # Connect to the item selection signal from graph
+ graph.selectedElementChanged.connect(self.handleElementSelected)
+
+ def setAxes(self, axisX, axisZ, axisY):
+ self._axisX = axisX
+ self._axisZ = axisZ
+ self._axisY = axisY
+
+ def setDragSpeedModifier(self, modifier):
+ self._speedModifier = modifier
+
+ def mousePressEvent(self, event, mousePos):
+ super().mousePressEvent(event, mousePos)
+ if Qt.LeftButton == event.button():
+ self._mousePressed = True
+
+ def mouseMoveEvent(self, event, mousePos):
+ # Check if we're trying to drag axis label
+ if self._mousePressed and self._state != InputState.StateNormal:
+ self.setPreviousInputPos(self.inputPosition())
+ self.setInputPosition(mousePos)
+ self.handleAxisDragging()
+ else:
+ super().mouseMoveEvent(event, mousePos)
+
+ def mouseReleaseEvent(self, event, mousePos):
+ super().mouseReleaseEvent(event, mousePos)
+ self._mousePressed = False
+ self._state = InputState.StateNormal
+
+ def handleElementSelected(self, type):
+ if type == QAbstract3DGraph.ElementAxisXLabel:
+ self._state = InputState.StateDraggingX
+ elif type == QAbstract3DGraph.ElementAxisYLabel:
+ self._state = InputState.StateDraggingY
+ elif type == QAbstract3DGraph.ElementAxisZLabel:
+ self._state = InputState.StateDraggingZ
+ else:
+ self._state = InputState.StateNormal
+
+ def handleAxisDragging(self):
+ distance = 0.0
+ # Get scene orientation from active camera
+ ac = self.scene().activeCamera()
+ xRotation = ac.xRotation()
+ yRotation = ac.yRotation()
+
+ # Calculate directional drag multipliers based on rotation
+ xMulX = cos(degrees(xRotation))
+ xMulY = sin(degrees(xRotation))
+ zMulX = sin(degrees(xRotation))
+ zMulY = cos(degrees(xRotation))
+
+ # Get the drag amount
+ move = self.inputPosition() - self.previousInputPos()
+
+ # Flip the effect of y movement if we're viewing from below
+ yMove = -move.y() if yRotation < 0 else move.y()
+
+ # Adjust axes
+ if self._state == InputState.StateDraggingX:
+ distance = (move.x() * xMulX - yMove * xMulY) / self._speedModifier
+ self._axisX.setRange(self._axisX.min() - distance,
+ self._axisX.max() - distance)
+ elif self._state == InputState.StateDraggingZ:
+ distance = (move.x() * zMulX + yMove * zMulY) / self._speedModifier
+ self._axisZ.setRange(self._axisZ.min() + distance,
+ self._axisZ.max() + distance)
+ elif self._state == InputState.StateDraggingY:
+ # No need to use adjusted y move here
+ distance = move.y() / self._speedModifier
+ self._axisY.setRange(self._axisY.min() + distance,
+ self._axisY.max() + distance)
diff --git a/examples/datavisualization/graphgallery/bargraph.py b/examples/datavisualization/graphgallery/bargraph.py
new file mode 100644
index 000000000..6c61d6708
--- /dev/null
+++ b/examples/datavisualization/graphgallery/bargraph.py
@@ -0,0 +1,281 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from graphmodifier import GraphModifier
+
+from PySide6.QtCore import QObject, Qt
+from PySide6.QtGui import QFont
+from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
+ QLabel, QPushButton, QHBoxLayout, QSizePolicy,
+ QRadioButton, QSlider, QVBoxLayout, QWidget)
+from PySide6.QtDataVisualization import (QAbstract3DGraph, QAbstract3DSeries,
+ Q3DBars)
+
+
+class BarGraph(QObject):
+
+ def __init__(self):
+ super().__init__()
+ self._barsGraph = Q3DBars()
+ self._container = None
+ self._barsWidget = None
+
+ def barsWidget(self):
+ return self._barsWidget
+
+ def initialize(self, minimum_graph_size, maximum_graph_size):
+ if not self._barsGraph.hasContext():
+ return False
+
+ self._barsWidget = QWidget()
+ hLayout = QHBoxLayout(self._barsWidget)
+ self._container = QWidget.createWindowContainer(self._barsGraph,
+ self._barsWidget)
+ self._container.setMinimumSize(minimum_graph_size)
+ self._container.setMaximumSize(maximum_graph_size)
+ self._container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ self._container.setFocusPolicy(Qt.StrongFocus)
+ hLayout.addWidget(self._container, 1)
+
+ vLayout = QVBoxLayout()
+ hLayout.addLayout(vLayout)
+
+ themeList = QComboBox(self._barsWidget)
+ themeList.addItem("Qt")
+ themeList.addItem("Primary Colors")
+ themeList.addItem("Digia")
+ themeList.addItem("Stone Moss")
+ themeList.addItem("Army Blue")
+ themeList.addItem("Retro")
+ themeList.addItem("Ebony")
+ themeList.addItem("Isabelle")
+ themeList.setCurrentIndex(0)
+
+ labelButton = QPushButton(self._barsWidget)
+ labelButton.setText("Change label style")
+
+ smoothCheckBox = QCheckBox(self._barsWidget)
+ smoothCheckBox.setText("Smooth bars")
+ smoothCheckBox.setChecked(False)
+
+ barStyleList = QComboBox(self._barsWidget)
+ barStyleList.addItem("Bar", QAbstract3DSeries.MeshBar)
+ barStyleList.addItem("Pyramid", QAbstract3DSeries.MeshPyramid)
+ barStyleList.addItem("Cone", QAbstract3DSeries.MeshCone)
+ barStyleList.addItem("Cylinder", QAbstract3DSeries.MeshCylinder)
+ barStyleList.addItem("Bevel bar", QAbstract3DSeries.MeshBevelBar)
+ barStyleList.addItem("Sphere", QAbstract3DSeries.MeshSphere)
+ barStyleList.setCurrentIndex(4)
+
+ cameraButton = QPushButton(self._barsWidget)
+ cameraButton.setText("Change camera preset")
+
+ zoomToSelectedButton = QPushButton(self._barsWidget)
+ zoomToSelectedButton.setText("Zoom to selected bar")
+
+ selectionModeList = QComboBox(self._barsWidget)
+ selectionModeList.addItem("None", QAbstract3DGraph.SelectionNone)
+ selectionModeList.addItem("Bar", QAbstract3DGraph.SelectionItem)
+ selectionModeList.addItem("Row", QAbstract3DGraph.SelectionRow)
+ sel = QAbstract3DGraph.SelectionItemAndRow
+ selectionModeList.addItem("Bar and Row", sel)
+ selectionModeList.addItem("Column", QAbstract3DGraph.SelectionColumn)
+ sel = QAbstract3DGraph.SelectionItemAndColumn
+ selectionModeList.addItem("Bar and Column", sel)
+ sel = QAbstract3DGraph.SelectionRowAndColumn
+ selectionModeList.addItem("Row and Column", sel)
+ sel = QAbstract3DGraph.SelectionItemRowAndColumn
+ selectionModeList.addItem("Bar, Row and Column", sel)
+ sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionRow
+ selectionModeList.addItem("Slice into Row", sel)
+ sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow
+ selectionModeList.addItem("Slice into Row and Item", sel)
+ sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionColumn
+ selectionModeList.addItem("Slice into Column", sel)
+ sel = (QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionItemAndColumn)
+ selectionModeList.addItem("Slice into Column and Item", sel)
+ sel = (QAbstract3DGraph.SelectionItemRowAndColumn
+ | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.addItem("Multi: Bar, Row, Col", sel)
+ sel = (QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionItemAndRow
+ | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.addItem("Multi, Slice: Row, Item", sel)
+ sel = (QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionItemAndColumn
+ | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.addItem("Multi, Slice: Col, Item", sel)
+ selectionModeList.setCurrentIndex(1)
+
+ backgroundCheckBox = QCheckBox(self._barsWidget)
+ backgroundCheckBox.setText("Show background")
+ backgroundCheckBox.setChecked(False)
+
+ gridCheckBox = QCheckBox(self._barsWidget)
+ gridCheckBox.setText("Show grid")
+ gridCheckBox.setChecked(True)
+
+ seriesCheckBox = QCheckBox(self._barsWidget)
+ seriesCheckBox.setText("Show second series")
+ seriesCheckBox.setChecked(False)
+
+ reverseValueAxisCheckBox = QCheckBox(self._barsWidget)
+ reverseValueAxisCheckBox.setText("Reverse value axis")
+ reverseValueAxisCheckBox.setChecked(False)
+
+ reflectionCheckBox = QCheckBox(self._barsWidget)
+ reflectionCheckBox.setText("Show reflections")
+ reflectionCheckBox.setChecked(False)
+
+ rotationSliderX = QSlider(Qt.Horizontal, self._barsWidget)
+ rotationSliderX.setTickInterval(30)
+ rotationSliderX.setTickPosition(QSlider.TicksBelow)
+ rotationSliderX.setMinimum(-180)
+ rotationSliderX.setValue(0)
+ rotationSliderX.setMaximum(180)
+ rotationSliderY = QSlider(Qt.Horizontal, self._barsWidget)
+ rotationSliderY.setTickInterval(15)
+ rotationSliderY.setTickPosition(QSlider.TicksAbove)
+ rotationSliderY.setMinimum(-90)
+ rotationSliderY.setValue(0)
+ rotationSliderY.setMaximum(90)
+
+ fontSizeSlider = QSlider(Qt.Horizontal, self._barsWidget)
+ fontSizeSlider.setTickInterval(10)
+ fontSizeSlider.setTickPosition(QSlider.TicksBelow)
+ fontSizeSlider.setMinimum(1)
+ fontSizeSlider.setValue(30)
+ fontSizeSlider.setMaximum(100)
+
+ fontList = QFontComboBox(self._barsWidget)
+ fontList.setCurrentFont(QFont("Times New Roman"))
+
+ shadowQuality = QComboBox(self._barsWidget)
+ shadowQuality.addItem("None")
+ shadowQuality.addItem("Low")
+ shadowQuality.addItem("Medium")
+ shadowQuality.addItem("High")
+ shadowQuality.addItem("Low Soft")
+ shadowQuality.addItem("Medium Soft")
+ shadowQuality.addItem("High Soft")
+ shadowQuality.setCurrentIndex(5)
+
+ rangeList = QComboBox(self._barsWidget)
+ rangeList.addItem("2015")
+ rangeList.addItem("2016")
+ rangeList.addItem("2017")
+ rangeList.addItem("2018")
+ rangeList.addItem("2019")
+ rangeList.addItem("2020")
+ rangeList.addItem("2021")
+ rangeList.addItem("2022")
+ rangeList.addItem("All")
+ rangeList.setCurrentIndex(8)
+
+ axisTitlesVisibleCB = QCheckBox(self._barsWidget)
+ axisTitlesVisibleCB.setText("Axis titles visible")
+ axisTitlesVisibleCB.setChecked(True)
+
+ axisTitlesFixedCB = QCheckBox(self._barsWidget)
+ axisTitlesFixedCB.setText("Axis titles fixed")
+ axisTitlesFixedCB.setChecked(True)
+
+ axisLabelRotationSlider = QSlider(Qt.Horizontal, self._barsWidget)
+ axisLabelRotationSlider.setTickInterval(10)
+ axisLabelRotationSlider.setTickPosition(QSlider.TicksBelow)
+ axisLabelRotationSlider.setMinimum(0)
+ axisLabelRotationSlider.setValue(30)
+ axisLabelRotationSlider.setMaximum(90)
+
+ modeGroup = QButtonGroup(self._barsWidget)
+ modeWeather = QRadioButton("Temperature Data", self._barsWidget)
+ modeWeather.setChecked(True)
+ modeCustomProxy = QRadioButton("Custom Proxy Data", self._barsWidget)
+ modeGroup.addButton(modeWeather)
+ modeGroup.addButton(modeCustomProxy)
+
+ vLayout.addWidget(QLabel("Rotate horizontally"))
+ vLayout.addWidget(rotationSliderX, 0, Qt.AlignTop)
+ vLayout.addWidget(QLabel("Rotate vertically"))
+ vLayout.addWidget(rotationSliderY, 0, Qt.AlignTop)
+ vLayout.addWidget(labelButton, 0, Qt.AlignTop)
+ vLayout.addWidget(cameraButton, 0, Qt.AlignTop)
+ vLayout.addWidget(zoomToSelectedButton, 0, Qt.AlignTop)
+ vLayout.addWidget(backgroundCheckBox)
+ vLayout.addWidget(gridCheckBox)
+ vLayout.addWidget(smoothCheckBox)
+ vLayout.addWidget(reflectionCheckBox)
+ vLayout.addWidget(seriesCheckBox)
+ vLayout.addWidget(reverseValueAxisCheckBox)
+ vLayout.addWidget(axisTitlesVisibleCB)
+ vLayout.addWidget(axisTitlesFixedCB)
+ vLayout.addWidget(QLabel("Show year"))
+ vLayout.addWidget(rangeList)
+ vLayout.addWidget(QLabel("Change bar style"))
+ vLayout.addWidget(barStyleList)
+ vLayout.addWidget(QLabel("Change selection mode"))
+ vLayout.addWidget(selectionModeList)
+ vLayout.addWidget(QLabel("Change theme"))
+ vLayout.addWidget(themeList)
+ vLayout.addWidget(QLabel("Adjust shadow quality"))
+ vLayout.addWidget(shadowQuality)
+ vLayout.addWidget(QLabel("Change font"))
+ vLayout.addWidget(fontList)
+ vLayout.addWidget(QLabel("Adjust font size"))
+ vLayout.addWidget(fontSizeSlider)
+ vLayout.addWidget(QLabel("Axis label rotation"))
+ vLayout.addWidget(axisLabelRotationSlider, 0, Qt.AlignTop)
+ vLayout.addWidget(modeWeather, 0, Qt.AlignTop)
+ vLayout.addWidget(modeCustomProxy, 1, Qt.AlignTop)
+
+ self._modifier = GraphModifier(self._barsGraph, self)
+
+ rotationSliderX.valueChanged.connect(self._modifier.rotateX)
+ rotationSliderY.valueChanged.connect(self._modifier.rotateY)
+
+ labelButton.clicked.connect(self._modifier.changeLabelBackground)
+ cameraButton.clicked.connect(self._modifier.changePresetCamera)
+ zoomToSelectedButton.clicked.connect(self._modifier.zoomToSelectedBar)
+
+ backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
+ gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
+ smoothCheckBox.stateChanged.connect(self._modifier.setSmoothBars)
+ seriesCheckBox.stateChanged.connect(self._modifier.setSeriesVisibility)
+ reverseValueAxisCheckBox.stateChanged.connect(self._modifier.setReverseValueAxis)
+ reflectionCheckBox.stateChanged.connect(self._modifier.setReflection)
+
+ self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
+ self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
+
+ rangeList.currentIndexChanged.connect(self._modifier.changeRange)
+
+ barStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
+
+ selectionModeList.currentIndexChanged.connect(self._modifier.changeSelectionMode)
+
+ themeList.currentIndexChanged.connect(self._modifier.changeTheme)
+
+ shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
+
+ self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+ self._barsGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
+
+ fontSizeSlider.valueChanged.connect(self._modifier.changeFontSize)
+ fontList.currentFontChanged.connect(self._modifier.changeFont)
+
+ self._modifier.fontSizeChanged.connect(fontSizeSlider.setValue)
+ self._modifier.fontChanged.connect(fontList.setCurrentFont)
+
+ axisTitlesVisibleCB.stateChanged.connect(self._modifier.setAxisTitleVisibility)
+ axisTitlesFixedCB.stateChanged.connect(self._modifier.setAxisTitleFixed)
+ axisLabelRotationSlider.valueChanged.connect(self._modifier.changeLabelRotation)
+
+ modeWeather.toggled.connect(self._modifier.setDataModeToWeather)
+ modeCustomProxy.toggled.connect(self._modifier.setDataModeToCustom)
+ modeWeather.toggled.connect(seriesCheckBox.setEnabled)
+ modeWeather.toggled.connect(rangeList.setEnabled)
+ modeWeather.toggled.connect(axisTitlesVisibleCB.setEnabled)
+ modeWeather.toggled.connect(axisTitlesFixedCB.setEnabled)
+ modeWeather.toggled.connect(axisLabelRotationSlider.setEnabled)
+ return True
diff --git a/examples/datavisualization/graphgallery/custominputhandler.py b/examples/datavisualization/graphgallery/custominputhandler.py
new file mode 100644
index 000000000..0402be607
--- /dev/null
+++ b/examples/datavisualization/graphgallery/custominputhandler.py
@@ -0,0 +1,177 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from enum import Enum
+from math import sin, cos, degrees
+
+from PySide6.QtCore import Qt
+from PySide6.QtDataVisualization import (QAbstract3DGraph, Q3DInputHandler)
+
+
+class InputState(Enum):
+ StateNormal = 0
+ StateDraggingX = 1
+ StateDraggingZ = 2
+ StateDraggingY = 3
+
+
+class CustomInputHandler(Q3DInputHandler):
+
+ def __init__(self, graph, parent=None):
+ super().__init__(parent)
+ self._highlight = None
+ self._mousePressed = False
+ self._state = InputState.StateNormal
+ self._axisX = None
+ self._axisY = None
+ self._axisZ = None
+ self._speedModifier = 20.0
+ self._aspectRatio = 0.0
+ self._axisXMinValue = 0.0
+ self._axisXMaxValue = 0.0
+ self._axisXMinRange = 0.0
+ self._axisZMinValue = 0.0
+ self._axisZMaxValue = 0.0
+ self._axisZMinRange = 0.0
+ self._areaMinValue = 0.0
+ self._areaMaxValue = 0.0
+
+ # Connect to the item selection signal from graph
+ graph.selectedElementChanged.connect(self.handleElementSelected)
+
+ def setAspectRatio(self, ratio):
+ self._aspectRatio = ratio
+
+ def setHighlightSeries(self, series):
+ self._highlight = series
+
+ def setDragSpeedModifier(self, modifier):
+ self._speedModifier = modifier
+
+ def setLimits(self, min, max, minRange):
+ self._areaMinValue = min
+ self._areaMaxValue = max
+ self._axisXMinValue = self._areaMinValue
+ self._axisXMaxValue = self._areaMaxValue
+ self._axisZMinValue = self._areaMinValue
+ self._axisZMaxValue = self._areaMaxValue
+ self._axisXMinRange = minRange
+ self._axisZMinRange = minRange
+
+ def setAxes(self, axisX, axisY, axisZ):
+ self._axisX = axisX
+ self._axisY = axisY
+ self._axisZ = axisZ
+
+ def mousePressEvent(self, event, mousePos):
+ if Qt.LeftButton == event.button():
+ self._highlight.setVisible(False)
+ self._mousePressed = True
+ super().mousePressEvent(event, mousePos)
+
+ def wheelEvent(self, event):
+ delta = float(event.angleDelta().y())
+
+ self._axisXMinValue += delta
+ self._axisXMaxValue -= delta
+ self._axisZMinValue += delta
+ self._axisZMaxValue -= delta
+ self.checkConstraints()
+
+ y = (self._axisXMaxValue - self._axisXMinValue) * self._aspectRatio
+
+ self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
+ self._axisY.setRange(100.0, y)
+ self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
+
+ def mouseMoveEvent(self, event, mousePos):
+ # Check if we're trying to drag axis label
+ if self._mousePressed and self._state != InputState.StateNormal:
+ self.setPreviousInputPos(self.inputPosition())
+ self.setInputPosition(mousePos)
+ self.handleAxisDragging()
+ else:
+ super().mouseMoveEvent(event, mousePos)
+
+ def mouseReleaseEvent(self, event, mousePos):
+ super().mouseReleaseEvent(event, mousePos)
+ self._mousePressed = False
+ self._state = InputState.StateNormal
+
+ def handleElementSelected(self, type):
+ if type == QAbstract3DGraph.ElementAxisXLabel:
+ self._state = InputState.StateDraggingX
+ elif type == QAbstract3DGraph.ElementAxisZLabel:
+ self._state = InputState.StateDraggingZ
+ else:
+ self._state = InputState.StateNormal
+
+ def handleAxisDragging(self):
+ distance = 0.0
+
+ # Get scene orientation from active camera
+ xRotation = self.scene().activeCamera().xRotation()
+
+ # Calculate directional drag multipliers based on rotation
+ xMulX = cos(degrees(xRotation))
+ xMulY = sin(degrees(xRotation))
+ zMulX = xMulY
+ zMulY = xMulX
+
+ # Get the drag amount
+ move = self.inputPosition() - self.previousInputPos()
+
+ # Adjust axes
+ if self._state == InputState.StateDraggingX:
+ distance = (move.x() * xMulX - move.y() * xMulY) * self._speedModifier
+ self._axisXMinValue -= distance
+ self._axisXMaxValue -= distance
+ if self._axisXMinValue < self._areaMinValue:
+ dist = self._axisXMaxValue - self._axisXMinValue
+ self._axisXMinValue = self._areaMinValue
+ self._axisXMaxValue = self._axisXMinValue + dist
+
+ if self._axisXMaxValue > self._areaMaxValue:
+ dist = self._axisXMaxValue - self._axisXMinValue
+ self._axisXMaxValue = self._areaMaxValue
+ self._axisXMinValue = self._axisXMaxValue - dist
+
+ self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
+ elif self._state == InputState.StateDraggingZ:
+ distance = (move.x() * zMulX + move.y() * zMulY) * self._speedModifier
+ self._axisZMinValue += distance
+ self._axisZMaxValue += distance
+ if self._axisZMinValue < self._areaMinValue:
+ dist = self._axisZMaxValue - self._axisZMinValue
+ self._axisZMinValue = self._areaMinValue
+ self._axisZMaxValue = self._axisZMinValue + dist
+
+ if self._axisZMaxValue > self._areaMaxValue:
+ dist = self._axisZMaxValue - self._axisZMinValue
+ self._axisZMaxValue = self._areaMaxValue
+ self._axisZMinValue = self._axisZMaxValue - dist
+
+ self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
+
+ def checkConstraints(self):
+ if self._axisXMinValue < self._areaMinValue:
+ self._axisXMinValue = self._areaMinValue
+ if self._axisXMaxValue > self._areaMaxValue:
+ self._axisXMaxValue = self._areaMaxValue
+ # Don't allow too much zoom in
+ range = self._axisXMaxValue - self._axisXMinValue
+ if range < self._axisXMinRange:
+ adjust = (self._axisXMinRange - range) / 2.0
+ self._axisXMinValue -= adjust
+ self._axisXMaxValue += adjust
+
+ if self._axisZMinValue < self._areaMinValue:
+ self._axisZMinValue = self._areaMinValue
+ if self._axisZMaxValue > self._areaMaxValue:
+ self._axisZMaxValue = self._areaMaxValue
+ # Don't allow too much zoom in
+ range = self._axisZMaxValue - self._axisZMinValue
+ if range < self._axisZMinRange:
+ adjust = (self._axisZMinRange - range) / 2.0
+ self._axisZMinValue -= adjust
+ self._axisZMaxValue += adjust
diff --git a/examples/datavisualization/graphgallery/data/layer_1.png b/examples/datavisualization/graphgallery/data/layer_1.png
new file mode 100644
index 000000000..9138c710a
--- /dev/null
+++ b/examples/datavisualization/graphgallery/data/layer_1.png
Binary files differ
diff --git a/examples/datavisualization/graphgallery/data/layer_2.png b/examples/datavisualization/graphgallery/data/layer_2.png
new file mode 100644
index 000000000..61631ae8b
--- /dev/null
+++ b/examples/datavisualization/graphgallery/data/layer_2.png
Binary files differ
diff --git a/examples/datavisualization/graphgallery/data/layer_3.png b/examples/datavisualization/graphgallery/data/layer_3.png
new file mode 100644
index 000000000..066ffbe75
--- /dev/null
+++ b/examples/datavisualization/graphgallery/data/layer_3.png
Binary files differ
diff --git a/examples/datavisualization/graphgallery/data/license.txt b/examples/datavisualization/graphgallery/data/license.txt
new file mode 100644
index 000000000..749daf31f
--- /dev/null
+++ b/examples/datavisualization/graphgallery/data/license.txt
@@ -0,0 +1,77 @@
+License information regarding the data obtained from National Land Survey of
+Finland http://www.maanmittauslaitos.fi/en
+- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and
+ U4422C) 08/2014
+- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014
+
+National Land Survey open data licence - version 1.0 - 1 May 2012
+
+1. General information
+
+The National Land Survey of Finland (hereinafter the Licensor), as the holder
+of the immaterial rights to the data, has granted on the terms mentioned below
+the right to use a copy (hereinafter data or dataset(s)) of the data (or a part
+of it).
+
+The Licensee is a natural or legal person who makes use of the data covered by
+this licence. The Licensee accepts the terms of this licence by receiving the
+dataset(s) covered by the licence.
+
+This Licence agreement does not create a co-operation or business relationship
+between the Licensee and the Licensor.
+
+2. Terms of the licence
+
+2.1. Right of use
+
+This licence grants a worldwide, free of charge and irrevocable parallel right
+of use to open data. According to the terms of the licence, data received by
+the Licensee can be freely:
+ - copied, distributed and published,
+ - modified and utilised commercially and non-commercially,
+ - inserted into other products and
+ - used as a part of a software application or service.
+
+2.2. Duties and responsibilities of the Licensee
+
+Through reasonable means suitable to the distribution medium or method which is
+used in conjunction with a product containing data or a service utilising data
+covered by this licence or while distributing data, the Licensee shall:
+ - mention the name of the Licensor, the name of the dataset(s) and the time
+ when the National Land Survey has delivered the dataset(s) (e.g.: contains
+ data from the National Land Survey of Finland Topographic Database 06/2012)
+ - provide a copy of this licence or a link to it, as well as
+ - require third parties to provide the same information when granting rights
+ to copies of dataset(s) or products and services containing such data and
+ - remove the name of the Licensor from the product or service, if required to
+ do so by the Licensor.
+
+The terms of this licence do not allow the Licensee to state in conjunction
+with the use of dataset(s) that the Licensor supports or recommends such use.
+
+2.3. Duties and responsibilities of the Licensor
+
+The Licensor shall ensure that
+ - the Licensor has the right to grant rights to the dataset(s) in accordance
+ with this licence.
+
+The data has been licensed "as is" and the Licensor
+ - shall not be held responsible for any errors or omissions in the data,
+ disclaims any warranty for the validity or up to date status of the data and
+ shall be free from liability for direct or consequential damages arising
+ from the use of data provided by the Licensor,
+ - and is not obligated to ensure the continuous availability of the data, nor
+ to announce in advance the interruption or cessation of availability, and
+ the Licensor shall be free from liability for direct or consequential
+ damages arising from any such interruption or cessation.
+
+3. Jurisdiction
+
+Finnish law shall apply to this licence.
+
+4. Changes to this licence
+
+The Licensor may at any time change the terms of the licence or apply a
+different licence to the data. The terms of this licence shall, however, still
+apply to such data that has been received prior to the change of the terms of
+the licence or the licence itself.
diff --git a/examples/datavisualization/graphgallery/data/maptexture.jpg b/examples/datavisualization/graphgallery/data/maptexture.jpg
new file mode 100644
index 000000000..ae5d66ebe
--- /dev/null
+++ b/examples/datavisualization/graphgallery/data/maptexture.jpg
Binary files differ
diff --git a/examples/datavisualization/graphgallery/data/oilrig.obj b/examples/datavisualization/graphgallery/data/oilrig.obj
new file mode 100644
index 000000000..c3b6ea570
--- /dev/null
+++ b/examples/datavisualization/graphgallery/data/oilrig.obj
@@ -0,0 +1,2322 @@
+# Blender v2.66 (sub 0) OBJ File: 'oilrig.blend'
+# www.blender.org
+v 0.057462 2.272318 -1.170324
+v 0.057461 8.181165 -0.128434
+v 0.055540 2.268930 -1.151111
+v 0.055539 8.177776 -0.109221
+v 0.049849 2.265673 -1.132637
+v 0.049849 8.174520 -0.090747
+v 0.040608 2.262671 -1.115611
+v 0.040608 8.171517 -0.073721
+v 0.028172 2.260039 -1.100687
+v 0.028172 8.168886 -0.058798
+v 0.013019 2.257880 -1.088440
+v 0.013018 8.166726 -0.046550
+v -0.004270 2.256275 -1.079339
+v -0.004271 8.165121 -0.037450
+v -0.023029 2.255287 -1.073735
+v -0.023030 8.164133 -0.031846
+v -0.042539 2.254953 -1.071843
+v -0.042539 8.163799 -0.029953
+v -0.062048 2.255287 -1.073735
+v -0.062048 8.164133 -0.031846
+v -0.080807 2.256275 -1.079339
+v -0.080808 8.165121 -0.037450
+v -0.098096 2.257880 -1.088440
+v -0.098096 8.166726 -0.046550
+v -0.113249 2.260039 -1.100687
+v -0.113250 8.168886 -0.058798
+v -0.125685 2.262671 -1.115611
+v -0.125686 8.171517 -0.073721
+v -0.134926 2.265673 -1.132637
+v -0.134927 8.174520 -0.090747
+v -0.140617 2.268930 -1.151111
+v -0.140618 8.177776 -0.109222
+v -0.142538 2.272318 -1.170324
+v -0.142539 8.181165 -0.128434
+v -0.140617 2.275706 -1.189536
+v -0.140618 8.184552 -0.147647
+v -0.134926 2.278963 -1.208011
+v -0.134927 8.187810 -0.166121
+v -0.125685 2.281965 -1.225037
+v -0.125686 8.190812 -0.183147
+v -0.113249 2.284597 -1.239960
+v -0.113250 8.193443 -0.198071
+v -0.098095 2.286757 -1.252208
+v -0.098096 8.195602 -0.210318
+v -0.080807 2.288361 -1.261308
+v -0.080807 8.197207 -0.219419
+v -0.062047 2.289349 -1.266912
+v -0.062048 8.198195 -0.225023
+v -0.042538 2.289683 -1.268804
+v -0.042539 8.198529 -0.226915
+v -0.023029 2.289349 -1.266912
+v -0.023030 8.198195 -0.225023
+v -0.004270 2.288361 -1.261308
+v -0.004271 8.197207 -0.219418
+v 0.013019 2.286757 -1.252207
+v 0.013018 8.195602 -0.210318
+v 0.028172 2.284597 -1.239960
+v 0.028172 8.193443 -0.198070
+v 0.040609 2.281965 -1.225036
+v 0.040608 8.190812 -0.183147
+v 0.049850 2.278963 -1.208010
+v 0.049849 8.187810 -0.166121
+v 0.055540 2.275706 -1.189536
+v 0.055539 8.184552 -0.147646
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.961940 0.308658
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.990393 0.597545
+vt 0.915735 0.222215
+vt 0.961940 0.691342
+vt 0.853553 0.146447
+vt 0.915735 0.777785
+vt 0.777785 0.084265
+vt 0.853553 0.853553
+vt 0.691342 0.038060
+vt 0.777785 0.915735
+vt 0.597545 0.009607
+vt 0.691342 0.961940
+vt 0.000000 0.500000
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.038060 0.691342
+vt 0.009607 0.597546
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.308659 0.961940
+vt 0.222215 0.915735
+vt 0.084265 0.222215
+vt 0.146447 0.853554
+vt 0.084266 0.777786
+vn 0.995185 -0.017020 0.096528
+vn 0.956940 -0.050408 0.285877
+vn 0.881921 -0.081857 0.464235
+vn 0.773009 -0.110162 0.624758
+vn 0.634397 -0.134231 0.761264
+vn 0.471397 -0.153144 0.868523
+vn 0.290285 -0.166171 0.942402
+vn 0.098018 -0.172812 0.980066
+vn -0.098022 -0.172812 0.980065
+vn -0.290285 -0.166171 0.942402
+vn -0.471392 -0.153145 0.868526
+vn -0.634399 -0.134231 0.761262
+vn -0.773009 -0.110162 0.624757
+vn -0.881923 -0.081857 0.464231
+vn -0.956941 -0.050407 0.285873
+vn -0.995185 -0.017021 0.096528
+vn -0.995185 0.017021 -0.096529
+vn -0.956940 0.050407 -0.285875
+vn -0.881920 0.081858 -0.464238
+vn -0.773015 0.110160 -0.624751
+vn -0.634391 0.134232 -0.761268
+vn -0.471394 0.153144 -0.868524
+vn -0.290288 0.166171 -0.942401
+vn -0.098006 0.172812 -0.980067
+vn 0.098019 0.172812 -0.980065
+vn 0.290285 0.166171 -0.942402
+vn 0.471401 0.153144 -0.868521
+vn 0.634393 0.134232 -0.761267
+vn 0.773011 0.110161 -0.624755
+vn 0.881922 0.081857 -0.464233
+vn -0.000000 0.984796 0.173713
+vn 0.995185 0.017020 -0.096527
+vn 0.956941 0.050407 -0.285872
+vn -0.000011 -0.984808 -0.173648
+vn 0.995185 -0.017020 0.096529
+vn 0.956940 -0.050407 0.285875
+vn 0.881920 -0.081858 0.464237
+vn 0.773011 -0.110161 0.624755
+vn 0.634396 -0.134231 0.761265
+vn 0.471389 -0.153145 0.868527
+vn 0.290287 -0.166171 0.942402
+vn 0.098019 -0.172812 0.980065
+vn -0.098021 -0.172812 0.980065
+vn -0.290282 -0.166171 0.942403
+vn -0.471402 -0.153144 0.868520
+vn -0.634395 -0.134232 0.761265
+vn -0.773010 -0.110161 0.624756
+vn -0.881922 -0.081857 0.464234
+vn -0.956940 -0.050408 0.285875
+vn -0.995185 -0.017020 0.096527
+vn -0.956940 0.050408 -0.285876
+vn -0.881921 0.081857 -0.464236
+vn -0.773009 0.110162 -0.624757
+vn -0.634391 0.134232 -0.761269
+vn -0.471398 0.153144 -0.868522
+vn -0.290282 0.166171 -0.942403
+vn -0.098016 0.172812 -0.980066
+vn 0.098018 0.172812 -0.980065
+vn 0.290287 0.166171 -0.942402
+vn 0.471399 0.153144 -0.868522
+vn 0.773014 0.110161 -0.624751
+vn 0.881921 0.081857 -0.464236
+vn -0.000000 0.984812 0.173622
+vn -0.000000 0.984812 0.173623
+vn -0.000000 0.984801 0.173685
+vn -0.000000 0.984814 0.173615
+vn -0.000000 0.984810 0.173634
+vn -0.000000 0.984808 0.173649
+vn -0.000000 0.984806 0.173660
+vn -0.000043 0.984788 0.173763
+vn -0.000000 0.984797 0.173710
+vn -0.000000 0.984805 0.173661
+vn -0.000000 0.984810 0.173635
+vn -0.000000 0.984810 0.173637
+vn -0.000000 0.984802 0.173683
+vn -0.000000 0.984814 0.173611
+vn -0.000000 0.984800 0.173689
+vn -0.000000 0.984800 0.173690
+vn -0.000000 0.984801 0.173686
+vn 0.000005 0.984810 0.173637
+vn 0.956941 0.050407 -0.285873
+vn -0.000000 -0.984818 -0.173587
+vn -0.000007 -0.984807 -0.173654
+vn -0.000000 -0.984808 -0.173648
+vn -0.000027 -0.984801 -0.173685
+vn -0.000004 -0.984807 -0.173652
+vn -0.000010 -0.984800 -0.173693
+vn -0.000020 -0.984817 -0.173596
+vn -0.000013 -0.984810 -0.173638
+vn -0.000001 -0.984807 -0.173650
+vn -0.000005 -0.984808 -0.173646
+vn -0.000002 -0.984808 -0.173648
+vn 0.000002 -0.984808 -0.173649
+vn 0.000001 -0.984808 -0.173649
+vn -0.000011 -0.984809 -0.173642
+vn -0.000004 -0.984808 -0.173646
+vn -0.000001 -0.984808 -0.173648
+vn -0.000002 -0.984808 -0.173649
+vn -0.000006 -0.984808 -0.173649
+vn 0.000004 -0.984808 -0.173648
+vn 0.000000 -0.984808 -0.173649
+vn -0.000004 -0.984807 -0.173650
+vn 0.000005 -0.984808 -0.173646
+vn 0.000003 -0.984808 -0.173647
+vn 0.000008 -0.984805 -0.173663
+s off
+f 1/1/1 2/2/1 4/3/1
+f 3/1/2 4/2/2 6/3/2
+f 5/1/3 6/2/3 8/3/3
+f 7/1/4 8/2/4 10/3/4
+f 9/1/5 10/2/5 12/3/5
+f 11/1/6 12/2/6 14/3/6
+f 13/1/7 14/2/7 16/3/7
+f 15/1/8 16/2/8 18/3/8
+f 17/1/9 18/2/9 19/4/9
+f 19/1/10 20/2/10 21/4/10
+f 21/1/11 22/2/11 23/4/11
+f 23/1/12 24/2/12 25/4/12
+f 25/1/13 26/2/13 27/4/13
+f 27/1/14 28/2/14 29/4/14
+f 29/1/15 30/2/15 31/4/15
+f 31/1/16 32/2/16 33/4/16
+f 33/1/17 34/2/17 35/4/17
+f 35/1/18 36/2/18 37/4/18
+f 37/1/19 38/2/19 39/4/19
+f 39/1/20 40/2/20 41/4/20
+f 41/1/21 42/2/21 43/4/21
+f 43/1/22 44/2/22 45/4/22
+f 45/1/23 46/2/23 47/4/23
+f 47/1/24 48/2/24 49/4/24
+f 49/1/25 50/2/25 52/3/25
+f 51/1/26 52/2/26 54/3/26
+f 53/1/27 54/2/27 56/3/27
+f 55/1/28 56/2/28 58/3/28
+f 57/1/29 58/2/29 60/3/29
+f 59/1/30 60/2/30 62/3/30
+f 48/5/31 52/6/31 50/7/31
+f 63/1/32 64/2/32 2/3/32
+f 61/1/33 62/2/33 64/3/33
+f 1/8/34 3/9/34 63/10/34
+f 3/4/35 1/1/35 4/3/35
+f 5/4/36 3/1/36 6/3/36
+f 7/4/37 5/1/37 8/3/37
+f 9/4/38 7/1/38 10/3/38
+f 11/4/39 9/1/39 12/3/39
+f 13/4/40 11/1/40 14/3/40
+f 15/4/41 13/1/41 16/3/41
+f 17/4/42 15/1/42 18/3/42
+f 18/2/43 20/3/43 19/4/43
+f 20/2/44 22/3/44 21/4/44
+f 22/2/45 24/3/45 23/4/45
+f 24/2/46 26/3/46 25/4/46
+f 26/2/47 28/3/47 27/4/47
+f 28/2/48 30/3/48 29/4/48
+f 30/2/49 32/3/49 31/4/49
+f 32/2/50 34/3/50 33/4/50
+f 34/2/17 36/3/17 35/4/17
+f 36/2/51 38/3/51 37/4/51
+f 38/2/52 40/3/52 39/4/52
+f 40/2/53 42/3/53 41/4/53
+f 42/2/54 44/3/54 43/4/54
+f 44/2/55 46/3/55 45/4/55
+f 46/2/56 48/3/56 47/4/56
+f 48/2/57 50/3/57 49/4/57
+f 51/4/58 49/1/58 52/3/58
+f 53/4/59 51/1/59 54/3/59
+f 55/4/60 53/1/60 56/3/60
+f 57/4/28 55/1/28 58/3/28
+f 59/4/61 57/1/61 60/3/61
+f 61/4/62 59/1/62 62/3/62
+f 48/5/63 54/11/63 52/6/63
+f 46/12/64 54/11/64 48/5/64
+f 46/12/65 56/13/65 54/11/65
+f 44/14/65 56/13/65 46/12/65
+f 44/14/66 58/15/66 56/13/66
+f 42/16/66 58/15/66 44/14/66
+f 42/16/67 60/17/67 58/15/67
+f 40/18/67 60/17/67 42/16/67
+f 40/18/68 62/19/68 60/17/68
+f 38/20/68 62/19/68 40/18/68
+f 38/20/69 64/21/69 62/19/69
+f 20/22/70 24/23/70 22/24/70
+f 16/25/71 20/22/71 18/26/71
+f 16/25/72 24/23/72 20/22/72
+f 36/27/69 64/21/69 38/20/69
+f 34/28/73 64/21/73 36/27/73
+f 2/9/74 64/21/74 34/28/74
+f 4/8/75 2/9/75 34/28/75
+f 4/8/75 34/28/75 32/29/75
+f 6/10/76 4/8/76 32/29/76
+f 6/10/76 32/29/76 30/30/76
+f 6/10/68 30/30/68 28/31/68
+f 8/32/68 6/10/68 28/31/68
+f 10/33/73 8/32/73 28/31/73
+f 10/33/67 28/31/67 26/34/67
+f 12/35/77 10/33/77 26/34/77
+f 12/35/78 26/34/78 24/23/78
+f 14/36/79 12/35/79 24/23/79
+f 16/25/80 14/36/80 24/23/80
+f 1/4/32 63/1/32 2/3/32
+f 63/4/81 61/1/81 64/3/81
+f 19/7/82 15/11/82 17/6/82
+f 41/31/83 37/29/83 39/30/83
+f 3/9/84 5/21/84 63/10/84
+f 45/23/85 41/31/85 43/34/85
+f 45/23/86 37/29/86 41/31/86
+f 49/22/87 45/23/87 47/24/87
+f 49/22/84 37/29/84 45/23/84
+f 49/22/84 35/28/84 37/29/84
+f 49/22/84 33/27/84 35/28/84
+f 49/22/84 31/20/84 33/27/84
+f 55/36/88 51/26/88 53/25/88
+f 59/33/89 55/36/89 57/35/89
+f 61/32/90 55/36/90 59/33/90
+f 63/10/91 55/36/91 61/32/91
+f 63/10/92 5/21/92 55/36/92
+f 5/21/93 7/19/93 55/36/93
+f 7/19/94 9/17/94 55/36/94
+f 29/18/95 25/14/95 27/16/95
+f 31/20/96 25/14/96 29/18/96
+f 49/22/97 25/14/97 31/20/97
+f 49/22/98 23/12/98 25/14/98
+f 9/17/93 11/15/93 55/36/93
+f 11/15/99 13/13/99 55/36/99
+f 13/13/100 15/11/100 55/36/100
+f 15/11/101 19/7/101 55/36/101
+f 19/7/102 21/5/102 55/36/102
+f 21/5/103 23/12/103 55/36/103
+f 23/12/104 49/22/104 55/36/104
+f 49/22/105 51/26/105 55/36/105
+v 0.053672 2.252534 1.125439
+v 0.053673 8.161380 0.083549
+v 0.051751 2.255921 1.144652
+v 0.051751 8.164768 0.102762
+v 0.046060 2.259179 1.163126
+v 0.046061 8.168025 0.121236
+v 0.036819 2.262181 1.180152
+v 0.036820 8.171027 0.138262
+v 0.024383 2.264812 1.195075
+v 0.024384 8.173658 0.153186
+v 0.009229 2.266972 1.207323
+v 0.009230 8.175818 0.165433
+v -0.008059 2.268577 1.216423
+v -0.008059 8.177423 0.174534
+v -0.026819 2.269565 1.222028
+v -0.026818 8.178411 0.180138
+v -0.046328 2.269898 1.223920
+v -0.046327 8.178745 0.182030
+v -0.065837 2.269565 1.222027
+v -0.065836 8.178411 0.180138
+v -0.084596 2.268577 1.216423
+v -0.084595 8.177423 0.174534
+v -0.101885 2.266972 1.207323
+v -0.101884 8.175818 0.165433
+v -0.117038 2.264812 1.195075
+v -0.117038 8.173658 0.153186
+v -0.129475 2.262181 1.180152
+v -0.129474 8.171027 0.138262
+v -0.138716 2.259179 1.163126
+v -0.138715 8.168025 0.121236
+v -0.144406 2.255921 1.144651
+v -0.144406 8.164768 0.102762
+v -0.146328 2.252534 1.125439
+v -0.146327 8.161380 0.083549
+v -0.144406 2.249146 1.106226
+v -0.144406 8.157992 0.064337
+v -0.138716 2.245888 1.087752
+v -0.138715 8.154735 0.045862
+v -0.129475 2.242886 1.070726
+v -0.129474 8.151732 0.028836
+v -0.117038 2.240255 1.055802
+v -0.117038 8.149101 0.013913
+v -0.101885 2.238095 1.043555
+v -0.101884 8.146942 0.001666
+v -0.084596 2.236491 1.034454
+v -0.084595 8.145337 -0.007435
+v -0.065837 2.235502 1.028850
+v -0.065836 8.144349 -0.013039
+v -0.046328 2.235169 1.026958
+v -0.046327 8.144015 -0.014931
+v -0.026819 2.235502 1.028850
+v -0.026818 8.144349 -0.013039
+v -0.008059 2.236491 1.034455
+v -0.008059 8.145337 -0.007435
+v 0.009229 2.238095 1.043555
+v 0.009230 8.146942 0.001666
+v 0.024383 2.240255 1.055803
+v 0.024384 8.149101 0.013913
+v 0.036819 2.242886 1.070726
+v 0.036820 8.151732 0.028837
+v 0.046060 2.245888 1.087752
+v 0.046061 8.154735 0.045863
+v 0.051751 2.249146 1.106226
+v 0.051752 8.157992 0.064337
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.308659 0.961940
+vt 0.222215 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 0.990393 0.402455
+vt 1.000000 0.500000
+vt 0.853553 0.146447
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.691342 0.038060
+vt 0.777785 0.084265
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.146447 0.853554
+vt 0.084266 0.777786
+vt 0.038060 0.691342
+vt 0.308658 0.038060
+vt 0.402455 0.009607
+vt 0.146446 0.146447
+vt 0.222215 0.084265
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.009607 0.597546
+vt 0.000000 0.500000
+vn 0.995185 0.017020 0.096528
+vn 0.956940 0.050408 0.285877
+vn 0.881921 0.081857 0.464236
+vn 0.773010 0.110161 0.624756
+vn 0.634396 0.134232 0.761265
+vn 0.471397 0.153144 0.868523
+vn 0.290283 0.166171 0.942403
+vn 0.098018 0.172812 0.980066
+vn -0.098018 0.172812 0.980066
+vn -0.290285 0.166171 0.942402
+vn -0.471398 0.153144 0.868522
+vn -0.634392 0.134232 0.761268
+vn -0.773007 0.110162 0.624759
+vn -0.881923 0.081857 0.464231
+vn -0.956941 0.050407 0.285874
+vn -0.995185 0.017021 0.096528
+vn -0.995185 -0.017021 -0.096529
+vn -0.956940 -0.050408 -0.285877
+vn -0.881921 -0.081857 -0.464236
+vn -0.773008 -0.110162 -0.624758
+vn -0.634391 -0.134232 -0.761269
+vn -0.471388 -0.153145 -0.868528
+vn -0.290288 -0.166171 -0.942401
+vn -0.098006 -0.172812 -0.980067
+vn 0.098021 -0.172812 -0.980065
+vn 0.290285 -0.166171 -0.942402
+vn 0.471402 -0.153144 -0.868520
+vn 0.634392 -0.134232 -0.761267
+vn 0.773010 -0.110161 -0.624755
+vn 0.881922 -0.081857 -0.464233
+vn -0.000356 0.984796 -0.173717
+vn 0.995185 -0.017020 -0.096527
+vn 0.956941 -0.050407 -0.285872
+vn -0.000005 -0.984808 0.173646
+vn 0.995185 0.017020 0.096529
+vn 0.956940 0.050407 0.285875
+vn 0.881920 0.081858 0.464237
+vn 0.773011 0.110161 0.624755
+vn 0.634396 0.134231 0.761264
+vn 0.471389 0.153145 0.868527
+vn 0.290292 0.166171 0.942400
+vn 0.098013 0.172812 0.980066
+vn -0.098019 0.172812 0.980065
+vn -0.290283 0.166171 0.942403
+vn -0.471400 0.153144 0.868521
+vn -0.634396 0.134232 0.761264
+vn -0.773010 0.110161 0.624756
+vn -0.881922 0.081857 0.464234
+vn -0.956940 0.050408 0.285876
+vn -0.995185 0.017020 0.096526
+vn -0.995185 -0.017021 -0.096530
+vn -0.956940 -0.050407 -0.285875
+vn -0.773009 -0.110161 -0.624757
+vn -0.634390 -0.134232 -0.761269
+vn -0.471399 -0.153144 -0.868522
+vn -0.290282 -0.166171 -0.942403
+vn -0.098018 -0.172812 -0.980066
+vn 0.098018 -0.172812 -0.980065
+vn 0.290287 -0.166171 -0.942402
+vn 0.471399 -0.153144 -0.868522
+vn 0.634393 -0.134232 -0.761267
+vn 0.773014 -0.110161 -0.624751
+vn 0.881921 -0.081857 -0.464236
+vn -0.000114 0.984812 -0.173626
+vn -0.000002 0.984808 -0.173648
+vn -0.000054 0.984802 -0.173678
+vn -0.000008 0.984808 -0.173648
+vn -0.000004 0.984808 -0.173647
+vn -0.000107 0.984836 -0.173487
+vn -0.000015 0.984812 -0.173622
+vn -0.000013 0.984820 -0.173580
+vn -0.000001 0.984809 -0.173639
+vn -0.000044 0.984787 -0.173766
+vn -0.000047 0.984804 -0.173672
+vn -0.000051 0.984803 -0.173676
+vn -0.000050 0.984803 -0.173675
+vn -0.000006 0.984799 -0.173697
+vn -0.000037 0.984791 -0.173743
+vn -0.000244 0.984806 -0.173659
+vn -0.000025 0.984806 -0.173659
+vn -0.000168 0.984823 -0.173560
+vn -0.000013 0.984812 -0.173624
+vn -0.000088 0.984819 -0.173584
+vn -0.000036 0.984813 -0.173618
+vn 0.000016 0.984808 -0.173645
+vn 0.000005 0.984808 -0.173650
+vn 0.000006 0.984807 -0.173650
+vn 0.000005 0.984808 -0.173649
+vn 0.000002 0.984808 -0.173648
+vn 0.000003 0.984807 -0.173650
+vn 0.000005 0.984804 -0.173667
+vn 0.956941 -0.050407 -0.285873
+vn -0.000005 -0.984808 0.173649
+vn -0.000001 -0.984818 0.173592
+vn -0.000000 -0.984808 0.173648
+vn -0.000006 -0.984808 0.173646
+vn -0.000001 -0.984808 0.173648
+vn -0.000002 -0.984808 0.173648
+vn -0.000001 -0.984806 0.173661
+vn -0.000000 -0.984805 0.173663
+vn -0.000000 -0.984810 0.173635
+vn -0.000000 -0.984810 0.173634
+vn -0.000000 -0.984807 0.173653
+vn -0.000000 -0.984807 0.173650
+vn -0.000000 -0.984808 0.173647
+vn 0.000000 -0.984807 0.173651
+vn -0.000000 -0.984807 0.173649
+vn -0.000000 -0.984810 0.173637
+vn 0.000000 -0.984806 0.173659
+vn -0.000001 -0.984805 0.173664
+s off
+f 65/37/106 66/38/106 68/39/106
+f 67/37/107 68/38/107 70/39/107
+f 69/37/108 70/38/108 72/39/108
+f 71/37/109 72/38/109 74/39/109
+f 73/37/110 74/38/110 76/39/110
+f 75/37/111 76/38/111 78/39/111
+f 77/37/112 78/38/112 80/39/112
+f 79/37/113 80/38/113 82/39/113
+f 81/37/114 82/38/114 83/40/114
+f 83/37/115 84/38/115 85/40/115
+f 85/37/116 86/38/116 87/40/116
+f 87/37/117 88/38/117 89/40/117
+f 89/37/118 90/38/118 91/40/118
+f 91/37/119 92/38/119 93/40/119
+f 93/37/120 94/38/120 95/40/120
+f 95/37/121 96/38/121 97/40/121
+f 97/37/122 98/38/122 99/40/122
+f 99/37/123 100/38/123 101/40/123
+f 101/37/124 102/38/124 103/40/124
+f 103/37/125 104/38/125 105/40/125
+f 105/37/126 106/38/126 107/40/126
+f 107/37/127 108/38/127 109/40/127
+f 109/37/128 110/38/128 111/40/128
+f 111/37/129 112/38/129 113/40/129
+f 113/37/130 114/38/130 116/39/130
+f 115/37/131 116/38/131 118/39/131
+f 117/37/132 118/38/132 120/39/132
+f 119/37/133 120/38/133 122/39/133
+f 121/37/134 122/38/134 124/39/134
+f 123/37/135 124/38/135 126/39/135
+f 68/41/136 66/42/136 70/43/136
+f 127/37/137 128/38/137 66/39/137
+f 125/37/138 126/38/138 128/39/138
+f 65/41/139 67/42/139 69/44/139
+f 67/40/140 65/37/140 68/39/140
+f 69/40/141 67/37/141 70/39/141
+f 71/40/142 69/37/142 72/39/142
+f 73/40/143 71/37/143 74/39/143
+f 75/40/144 73/37/144 76/39/144
+f 77/40/145 75/37/145 78/39/145
+f 79/40/146 77/37/146 80/39/146
+f 81/40/147 79/37/147 82/39/147
+f 82/38/148 84/39/148 83/40/148
+f 84/38/149 86/39/149 85/40/149
+f 86/38/150 88/39/150 87/40/150
+f 88/38/151 90/39/151 89/40/151
+f 90/38/152 92/39/152 91/40/152
+f 92/38/153 94/39/153 93/40/153
+f 94/38/154 96/39/154 95/40/154
+f 96/38/155 98/39/155 97/40/155
+f 98/38/156 100/39/156 99/40/156
+f 100/38/157 102/39/157 101/40/157
+f 102/38/124 104/39/124 103/40/124
+f 104/38/158 106/39/158 105/40/158
+f 106/38/159 108/39/159 107/40/159
+f 108/38/160 110/39/160 109/40/160
+f 110/38/161 112/39/161 111/40/161
+f 112/38/162 114/39/162 113/40/162
+f 115/40/163 113/37/163 116/39/163
+f 117/40/164 115/37/164 118/39/164
+f 119/40/165 117/37/165 120/39/165
+f 121/40/166 119/37/166 122/39/166
+f 123/40/167 121/37/167 124/39/167
+f 125/40/168 123/37/168 126/39/168
+f 66/42/169 128/44/169 126/45/169
+f 70/43/170 66/42/170 126/45/170
+f 72/46/171 70/43/171 74/47/171
+f 70/43/172 126/45/172 74/47/172
+f 126/45/173 124/48/173 74/47/173
+f 122/49/174 120/50/174 118/51/174
+f 124/48/175 122/49/175 118/51/175
+f 114/52/176 118/51/176 116/53/176
+f 124/48/177 118/51/177 114/52/177
+f 108/54/178 112/55/178 110/56/178
+f 104/57/179 108/54/179 106/58/179
+f 102/59/180 108/54/180 104/57/180
+f 100/60/181 108/54/181 102/59/181
+f 76/61/182 74/47/182 78/62/182
+f 78/62/183 74/47/183 80/63/183
+f 96/64/184 100/60/184 98/65/184
+f 96/64/185 108/54/185 100/60/185
+f 92/66/186 96/64/186 94/67/186
+f 90/68/187 96/64/187 92/66/187
+f 88/69/188 96/64/188 90/68/188
+f 86/70/189 96/64/189 88/69/189
+f 82/71/176 86/70/176 84/72/176
+f 74/47/190 124/48/190 80/63/190
+f 124/48/191 114/52/191 80/63/191
+f 114/52/192 112/55/192 80/63/192
+f 112/55/193 108/54/193 80/63/193
+f 108/54/194 96/64/194 80/63/194
+f 96/64/195 86/70/195 80/63/195
+f 86/70/196 82/71/196 80/63/196
+f 65/40/137 127/37/137 66/39/137
+f 127/40/197 125/37/197 128/39/197
+f 127/43/198 65/41/198 125/46/198
+f 83/52/199 79/51/199 81/53/199
+f 97/60/200 93/57/200 95/59/200
+f 99/65/200 93/57/200 97/60/200
+f 103/67/201 99/65/201 101/64/201
+f 103/67/202 93/57/202 99/65/202
+f 105/66/203 93/57/203 103/67/203
+f 115/71/199 111/70/199 113/72/199
+f 115/71/204 109/69/204 111/70/204
+f 117/63/205 109/69/205 115/71/205
+f 117/63/206 107/68/206 109/69/206
+f 119/62/207 107/68/207 117/63/207
+f 119/62/208 105/66/208 107/68/208
+f 121/61/208 105/66/208 119/62/208
+f 123/47/200 105/66/200 121/61/200
+f 125/46/209 105/66/209 123/47/209
+f 125/46/200 65/41/200 105/66/200
+f 105/66/200 65/41/200 93/57/200
+f 65/41/210 69/44/210 93/57/210
+f 69/44/209 71/45/209 93/57/209
+f 93/57/200 71/45/200 91/58/200
+f 91/58/211 71/45/211 89/54/211
+f 71/45/212 73/48/212 89/54/212
+f 73/48/211 75/49/211 89/54/211
+f 89/54/208 75/49/208 87/56/208
+f 75/49/213 77/50/213 87/56/213
+f 87/56/207 77/50/207 85/55/207
+f 77/50/214 79/51/214 85/55/214
+f 79/51/215 83/52/215 85/55/215
+v 1.116865 2.257815 -0.125221
+v 0.074976 8.166661 -0.125221
+v 1.136078 2.261203 -0.123300
+v 0.094188 8.170050 -0.123300
+v 1.154552 2.264460 -0.117609
+v 0.112663 8.173306 -0.117609
+v 1.171578 2.267462 -0.108368
+v 0.129689 8.176309 -0.108368
+v 1.186502 2.270094 -0.095932
+v 0.144612 8.178940 -0.095932
+v 1.198749 2.272254 -0.080778
+v 0.156859 8.181100 -0.080778
+v 1.207850 2.273858 -0.063490
+v 0.165960 8.182705 -0.063490
+v 1.213454 2.274846 -0.044730
+v 0.171564 8.183693 -0.044730
+v 1.215346 2.275180 -0.025221
+v 0.173456 8.184027 -0.025221
+v 1.213454 2.274846 -0.005712
+v 0.171564 8.183693 -0.005712
+v 1.207850 2.273858 0.013047
+v 0.165960 8.182705 0.013047
+v 1.198749 2.272254 0.030336
+v 0.156859 8.181100 0.030336
+v 1.186502 2.270094 0.045489
+v 0.144612 8.178940 0.045489
+v 1.171578 2.267462 0.057926
+v 0.129689 8.176309 0.057926
+v 1.154552 2.264460 0.067167
+v 0.112663 8.173306 0.067167
+v 1.136078 2.261203 0.072857
+v 0.094188 8.170050 0.072857
+v 1.116865 2.257815 0.074779
+v 0.074976 8.166661 0.074779
+v 1.097653 2.254427 0.072857
+v 0.055763 8.163274 0.072857
+v 1.079178 2.251170 0.067167
+v 0.037289 8.160016 0.067167
+v 1.062152 2.248168 0.057926
+v 0.020263 8.157014 0.057926
+v 1.047229 2.245536 0.045489
+v 0.005339 8.154383 0.045489
+v 1.034981 2.243377 0.030336
+v -0.006908 8.152224 0.030336
+v 1.025881 2.241772 0.013047
+v -0.016009 8.150619 0.013047
+v 1.020277 2.240784 -0.005712
+v -0.021613 8.149631 -0.005712
+v 1.018384 2.240450 -0.025221
+v -0.023505 8.149297 -0.025221
+v 1.020277 2.240784 -0.044730
+v -0.021613 8.149631 -0.044730
+v 1.025881 2.241772 -0.063490
+v -0.016009 8.150619 -0.063490
+v 1.034981 2.243377 -0.080778
+v -0.006908 8.152224 -0.080778
+v 1.047229 2.245536 -0.095932
+v 0.005339 8.154383 -0.095932
+v 1.062152 2.248168 -0.108368
+v 0.020263 8.157014 -0.108368
+v 1.079178 2.251170 -0.117609
+v 0.037289 8.160016 -0.117609
+v 1.097653 2.254427 -0.123300
+v 0.055763 8.163274 -0.123300
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.000000 0.500000
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.146447 0.853554
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.308659 0.961940
+vt 0.222215 0.915735
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.009607 0.597546
+vt 0.084265 0.222215
+vn 0.096528 0.017020 -0.995185
+vn 0.285876 0.050408 -0.956940
+vn 0.464235 0.081857 -0.881921
+vn 0.624757 0.110162 -0.773009
+vn 0.761264 0.134232 -0.634396
+vn 0.868521 0.153144 -0.471400
+vn 0.942403 0.166171 -0.290283
+vn 0.980066 0.172812 -0.098018
+vn 0.980066 0.172812 0.098018
+vn 0.942403 0.166171 0.290283
+vn 0.868521 0.153144 0.471399
+vn 0.761264 0.134232 0.634396
+vn 0.624757 0.110162 0.773009
+vn 0.464235 0.081857 0.881921
+vn 0.285876 0.050408 0.956940
+vn 0.096527 0.017020 0.995185
+vn -0.096528 -0.017021 0.995185
+vn -0.285875 -0.050407 0.956940
+vn -0.464235 -0.081857 0.881921
+vn -0.624754 -0.110161 0.773012
+vn -0.761268 -0.134232 0.634391
+vn -0.868524 -0.153144 0.471394
+vn -0.942401 -0.166171 0.290288
+vn -0.980066 -0.172812 0.098010
+vn -0.980065 -0.172812 -0.098018
+vn -0.942402 -0.166171 -0.290285
+vn -0.868521 -0.153144 -0.471401
+vn -0.761266 -0.134232 -0.634394
+vn -0.624751 -0.110161 -0.773014
+vn -0.464236 -0.081857 -0.881921
+vn -0.173717 0.984796 0.000356
+vn -0.096527 -0.017020 -0.995185
+vn -0.285873 -0.050407 -0.956941
+vn 0.173648 -0.984808 -0.000011
+vn 0.285874 0.050407 -0.956940
+vn 0.464237 0.081858 -0.881920
+vn 0.624752 0.110161 -0.773013
+vn 0.761267 0.134232 -0.634393
+vn 0.868524 0.153144 -0.471394
+vn 0.942402 0.166171 -0.290287
+vn 0.980065 0.172812 -0.098019
+vn 0.980065 0.172812 0.098019
+vn 0.942402 0.166171 0.290287
+vn 0.868524 0.153144 0.471394
+vn 0.761267 0.134232 0.634393
+vn 0.624752 0.110161 0.773013
+vn 0.464237 0.081858 0.881920
+vn 0.285874 0.050407 0.956940
+vn -0.096529 -0.017021 0.995185
+vn -0.285874 -0.050407 0.956940
+vn -0.624758 -0.110162 0.773008
+vn -0.761268 -0.134232 0.634392
+vn -0.868523 -0.153144 0.471397
+vn -0.942402 -0.166171 0.290284
+vn -0.980066 -0.172812 0.098018
+vn -0.980065 -0.172812 -0.098021
+vn -0.868521 -0.153144 -0.471400
+vn -0.761267 -0.134232 -0.634392
+vn -0.624756 -0.110161 -0.773010
+vn -0.464234 -0.081857 -0.881922
+vn -0.173640 0.984809 -0.000036
+vn -0.173648 0.984808 0.000040
+vn -0.173648 0.984808 0.000002
+vn -0.173650 0.984807 -0.000020
+vn -0.173652 0.984807 -0.000031
+vn -0.173635 0.984810 0.000027
+vn -0.173649 0.984808 -0.000009
+vn -0.173641 0.984809 0.000007
+vn -0.173650 0.984807 -0.000006
+vn -0.173640 0.984809 0.000006
+vn -0.173660 0.984806 -0.000014
+vn -0.173675 0.984803 0.000050
+vn -0.173675 0.984803 0.000049
+vn -0.173767 0.984787 -0.000044
+vn -0.173767 0.984787 0.000044
+vn -0.173642 0.984809 0.000000
+vn -0.173677 0.984803 0.000052
+vn -0.173717 0.984796 -0.000357
+vn -0.173646 0.984808 0.000005
+vn -0.173645 0.984808 0.000007
+vn -0.173703 0.984798 -0.000011
+vn -0.173684 0.984802 -0.000003
+vn -0.173667 0.984805 0.000002
+vn -0.173671 0.984804 -0.000015
+vn -0.173656 0.984806 -0.000026
+vn -0.173637 0.984810 0.000008
+vn -0.173643 0.984809 0.000005
+vn -0.096528 -0.017020 -0.995185
+vn -0.285872 -0.050407 -0.956941
+vn 0.173648 -0.984808 -0.000000
+vn 0.173647 -0.984808 0.000006
+vn 0.173647 -0.984808 0.000005
+vn 0.173652 -0.984807 -0.000009
+vn 0.173645 -0.984808 0.000005
+vn 0.173653 -0.984807 -0.000007
+vn 0.173643 -0.984809 0.000006
+vn 0.173652 -0.984807 -0.000003
+vn 0.173643 -0.984809 0.000004
+vn 0.173651 -0.984807 -0.000002
+vn 0.173650 -0.984807 -0.000001
+vn 0.173647 -0.984808 0.000000
+vn 0.173649 -0.984808 0.000000
+vn 0.173652 -0.984807 0.000001
+vn 0.173642 -0.984809 -0.000003
+vn 0.173653 -0.984807 0.000003
+vn 0.173642 -0.984809 -0.000004
+vn 0.173655 -0.984806 0.000007
+vn 0.173641 -0.984809 -0.000008
+vn 0.173658 -0.984806 0.000013
+vn 0.173645 -0.984808 -0.000007
+vn 0.173654 -0.984807 0.000007
+vn 0.173646 -0.984808 -0.000004
+s off
+f 129/73/216 130/74/216 132/75/216
+f 131/73/217 132/74/217 134/75/217
+f 133/73/218 134/74/218 136/75/218
+f 135/73/219 136/74/219 138/75/219
+f 137/73/220 138/74/220 140/75/220
+f 139/73/221 140/74/221 142/75/221
+f 141/73/222 142/74/222 144/75/222
+f 143/73/223 144/74/223 146/75/223
+f 145/73/224 146/74/224 148/75/224
+f 147/73/225 148/74/225 150/75/225
+f 149/73/226 150/74/226 152/75/226
+f 151/73/227 152/74/227 154/75/227
+f 153/73/228 154/74/228 156/75/228
+f 155/73/229 156/74/229 158/75/229
+f 157/73/230 158/74/230 160/75/230
+f 159/73/231 160/74/231 162/75/231
+f 161/73/232 162/74/232 163/76/232
+f 163/73/233 164/74/233 165/76/233
+f 165/73/234 166/74/234 167/76/234
+f 167/73/235 168/74/235 169/76/235
+f 169/73/236 170/74/236 171/76/236
+f 171/73/237 172/74/237 173/76/237
+f 173/73/238 174/74/238 175/76/238
+f 175/73/239 176/74/239 177/76/239
+f 177/73/240 178/74/240 179/76/240
+f 179/73/241 180/74/241 181/76/241
+f 181/73/242 182/74/242 183/76/242
+f 183/73/243 184/74/243 185/76/243
+f 185/73/244 186/74/244 187/76/244
+f 187/73/245 188/74/245 189/76/245
+f 132/77/246 130/78/246 134/79/246
+f 191/73/247 192/74/247 129/76/247
+f 189/73/248 190/74/248 191/76/248
+f 129/77/249 131/78/249 191/79/249
+f 131/76/216 129/73/216 132/75/216
+f 133/76/250 131/73/250 134/75/250
+f 135/76/251 133/73/251 136/75/251
+f 137/76/252 135/73/252 138/75/252
+f 139/76/253 137/73/253 140/75/253
+f 141/76/254 139/73/254 142/75/254
+f 143/76/255 141/73/255 144/75/255
+f 145/76/256 143/73/256 146/75/256
+f 147/76/257 145/73/257 148/75/257
+f 149/76/258 147/73/258 150/75/258
+f 151/76/259 149/73/259 152/75/259
+f 153/76/260 151/73/260 154/75/260
+f 155/76/261 153/73/261 156/75/261
+f 157/76/262 155/73/262 158/75/262
+f 159/76/263 157/73/263 160/75/263
+f 161/76/231 159/73/231 162/75/231
+f 162/74/264 164/75/264 163/76/264
+f 164/74/265 166/75/265 165/76/265
+f 166/74/234 168/75/234 167/76/234
+f 168/74/266 170/75/266 169/76/266
+f 170/74/267 172/75/267 171/76/267
+f 172/74/268 174/75/268 173/76/268
+f 174/74/269 176/75/269 175/76/269
+f 176/74/270 178/75/270 177/76/270
+f 178/74/271 180/75/271 179/76/271
+f 180/74/241 182/75/241 181/76/241
+f 182/74/272 184/75/272 183/76/272
+f 184/74/273 186/75/273 185/76/273
+f 186/74/274 188/75/274 187/76/274
+f 188/74/275 190/75/275 189/76/275
+f 130/78/276 192/80/276 134/79/276
+f 192/80/277 190/81/277 134/79/277
+f 190/81/278 188/82/278 134/79/278
+f 188/82/279 186/83/279 134/79/279
+f 186/83/280 184/84/280 134/79/280
+f 184/84/281 182/85/281 134/79/281
+f 182/85/282 180/86/282 134/79/282
+f 180/86/283 178/87/283 134/79/283
+f 178/87/284 176/88/284 134/79/284
+f 176/88/285 174/89/285 134/79/285
+f 174/89/286 172/90/286 134/79/286
+f 170/91/287 168/92/287 166/93/287
+f 170/91/288 166/93/288 164/94/288
+f 148/95/289 152/96/289 150/97/289
+f 140/98/290 144/99/290 142/100/290
+f 172/90/287 170/91/287 164/94/287
+f 134/79/291 172/90/291 164/94/291
+f 136/101/292 134/79/292 138/102/292
+f 134/79/291 164/94/291 138/102/291
+f 162/103/293 160/104/293 158/105/293
+f 162/103/294 158/105/294 156/106/294
+f 146/107/295 152/96/295 148/95/295
+f 146/107/296 154/108/296 152/96/296
+f 144/99/297 154/108/297 146/107/297
+f 140/98/298 154/108/298 144/99/298
+f 138/102/299 164/94/299 140/98/299
+f 162/103/300 156/106/300 154/108/300
+f 164/94/301 162/103/301 140/98/301
+f 162/103/302 154/108/302 140/98/302
+f 192/74/303 130/75/303 129/76/303
+f 190/74/304 192/75/304 191/76/304
+f 131/78/305 133/80/305 191/79/305
+f 133/80/306 135/81/306 191/79/306
+f 135/81/305 137/82/305 191/79/305
+f 137/82/307 139/83/307 191/79/307
+f 139/83/308 141/84/308 191/79/308
+f 141/84/309 143/85/309 191/79/309
+f 143/85/310 145/86/310 191/79/310
+f 145/86/311 147/87/311 191/79/311
+f 147/87/312 149/88/312 191/79/312
+f 149/88/313 151/89/313 191/79/313
+f 151/89/314 153/90/314 191/79/314
+f 153/90/305 155/91/305 191/79/305
+f 155/91/315 157/92/315 191/79/315
+f 157/92/305 159/93/305 191/79/305
+f 159/93/316 161/94/316 191/79/316
+f 161/94/305 163/103/305 191/79/305
+f 163/103/305 165/104/305 191/79/305
+f 165/104/317 167/105/317 191/79/317
+f 167/105/317 169/106/317 191/79/317
+f 169/106/318 171/108/318 191/79/318
+f 171/108/319 173/96/319 191/79/319
+f 173/96/320 175/97/320 191/79/320
+f 175/97/321 177/95/321 191/79/321
+f 177/95/322 179/107/322 191/79/322
+f 179/107/323 181/99/323 191/79/323
+f 181/99/324 183/100/324 191/79/324
+f 183/100/325 185/98/325 191/79/325
+f 185/98/326 187/102/326 189/101/326
+f 191/79/327 185/98/327 189/101/327
+v -1.178897 2.277600 -0.129009
+v -0.137008 8.186446 -0.129009
+v -1.159685 2.274212 -0.127088
+v -0.117795 8.183058 -0.127088
+v -1.141210 2.270954 -0.121397
+v -0.099321 8.179801 -0.121397
+v -1.124184 2.267952 -0.112156
+v -0.082295 8.176799 -0.112156
+v -1.109261 2.265321 -0.099720
+v -0.067371 8.174168 -0.099720
+v -1.097013 2.263161 -0.084566
+v -0.055124 8.172008 -0.084566
+v -1.087913 2.261557 -0.067277
+v -0.046023 8.170403 -0.067277
+v -1.082309 2.260568 -0.048518
+v -0.040419 8.169415 -0.048518
+v -1.080417 2.260235 -0.029009
+v -0.038527 8.169081 -0.029009
+v -1.082309 2.260568 -0.009500
+v -0.040419 8.169415 -0.009500
+v -1.087913 2.261557 0.009259
+v -0.046023 8.170403 0.009259
+v -1.097013 2.263161 0.026548
+v -0.055124 8.172008 0.026548
+v -1.109261 2.265321 0.041702
+v -0.067371 8.174168 0.041702
+v -1.124184 2.267952 0.054138
+v -0.082295 8.176799 0.054138
+v -1.141210 2.270954 0.063379
+v -0.099321 8.179801 0.063379
+v -1.159685 2.274212 0.069069
+v -0.117795 8.183058 0.069069
+v -1.178897 2.277600 0.070991
+v -0.137008 8.186446 0.070991
+v -1.198110 2.280987 0.069069
+v -0.156220 8.189834 0.069069
+v -1.216584 2.284245 0.063379
+v -0.174695 8.193091 0.063379
+v -1.233610 2.287247 0.054138
+v -0.191721 8.196094 0.054138
+v -1.248534 2.289878 0.041702
+v -0.206644 8.198725 0.041702
+v -1.260781 2.292038 0.026548
+v -0.218892 8.200884 0.026548
+v -1.269882 2.293643 0.009259
+v -0.227992 8.202489 0.009259
+v -1.275486 2.294631 -0.009500
+v -0.233596 8.203477 -0.009500
+v -1.277378 2.294964 -0.029009
+v -0.235489 8.203811 -0.029009
+v -1.275486 2.294631 -0.048518
+v -0.233596 8.203477 -0.048518
+v -1.269882 2.293643 -0.067278
+v -0.227992 8.202489 -0.067278
+v -1.260781 2.292038 -0.084566
+v -0.218892 8.200884 -0.084566
+v -1.248534 2.289878 -0.099720
+v -0.206644 8.198725 -0.099720
+v -1.233610 2.287247 -0.112156
+v -0.191721 8.196094 -0.112156
+v -1.216584 2.284245 -0.121397
+v -0.174695 8.193091 -0.121397
+v -1.198110 2.280987 -0.127088
+v -0.156220 8.189834 -0.127088
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.096528 -0.017020 -0.995185
+vn 0.285876 -0.050408 -0.956940
+vn 0.464236 -0.081857 -0.881921
+vn 0.624756 -0.110161 -0.773010
+vn 0.761265 -0.134232 -0.634395
+vn 0.868521 -0.153144 -0.471400
+vn 0.942403 -0.166171 -0.290282
+vn 0.980065 -0.172812 -0.098021
+vn 0.980066 -0.172812 0.098018
+vn 0.942402 -0.166171 0.290285
+vn 0.868522 -0.153144 0.471399
+vn 0.761265 -0.134232 0.634395
+vn 0.624756 -0.110161 0.773010
+vn 0.464235 -0.081857 0.881922
+vn 0.285876 -0.050408 0.956940
+vn 0.096527 -0.017020 0.995185
+vn -0.096528 0.017020 0.995185
+vn -0.285876 0.050408 0.956940
+vn -0.464234 0.081857 0.881922
+vn -0.624758 0.110162 0.773008
+vn -0.761264 0.134231 0.634396
+vn -0.868524 0.153144 0.471394
+vn -0.942401 0.166171 0.290288
+vn -0.980066 0.172812 0.098014
+vn -0.980066 0.172812 -0.098014
+vn -0.942403 0.166171 -0.290281
+vn -0.868522 0.153144 -0.471398
+vn -0.761268 0.134232 -0.634392
+vn -0.624751 0.110161 -0.773014
+vn -0.464236 0.081857 -0.881921
+vn 0.173717 0.984795 -0.000357
+vn -0.096527 0.017020 -0.995185
+vn -0.285874 0.050407 -0.956940
+vn -0.173648 -0.984808 0.000011
+vn 0.285874 -0.050407 -0.956940
+vn 0.464237 -0.081858 -0.881920
+vn 0.624755 -0.110161 -0.773011
+vn 0.761264 -0.134231 -0.634396
+vn 0.868524 -0.153144 -0.471394
+vn 0.942402 -0.166171 -0.290287
+vn 0.980066 -0.172812 -0.098013
+vn 0.980066 -0.172812 0.098013
+vn 0.942402 -0.166171 0.290287
+vn 0.868524 -0.153144 0.471394
+vn 0.761264 -0.134231 0.634396
+vn 0.624755 -0.110161 0.773011
+vn 0.464237 -0.081858 0.881920
+vn 0.285874 -0.050407 0.956940
+vn -0.096529 0.017021 0.995185
+vn -0.285874 0.050407 0.956940
+vn -0.464235 0.081857 0.881921
+vn -0.624756 0.110161 0.773010
+vn -0.761269 0.134232 0.634391
+vn -0.868522 0.153144 0.471398
+vn -0.942403 0.166171 0.290282
+vn -0.980066 0.172812 0.098018
+vn -0.980065 0.172812 -0.098019
+vn -0.942402 0.166171 -0.290285
+vn -0.868521 0.153144 -0.471400
+vn -0.761267 0.134232 -0.634392
+vn -0.624756 0.110161 -0.773010
+vn -0.464234 0.081857 -0.881922
+vn 0.173640 0.984809 0.000036
+vn 0.173648 0.984808 -0.000041
+vn 0.173648 0.984808 -0.000002
+vn 0.173650 0.984807 0.000019
+vn 0.173652 0.984807 0.000031
+vn 0.173635 0.984810 -0.000027
+vn 0.173649 0.984808 0.000009
+vn 0.173641 0.984809 -0.000007
+vn 0.173650 0.984807 0.000006
+vn 0.173641 0.984809 -0.000005
+vn 0.173660 0.984806 0.000014
+vn 0.173629 0.984811 -0.000011
+vn 0.173638 0.984810 -0.000005
+vn 0.173649 0.984808 0.000001
+vn 0.173659 0.984806 0.000005
+vn 0.173636 0.984810 -0.000002
+vn 0.173683 0.984802 0.000007
+vn 0.173611 0.984814 0.000000
+vn 0.173649 0.984808 0.000000
+vn 0.173635 0.984810 0.000001
+vn 0.173681 0.984802 -0.000008
+vn 0.173674 0.984803 -0.000006
+vn 0.173644 0.984808 0.000007
+vn 0.173666 0.984805 -0.000005
+vn 0.173677 0.984803 -0.000016
+vn 0.173633 0.984810 0.000028
+vn 0.173518 0.984831 0.000143
+vn 0.173638 0.984810 0.000022
+vn -0.096528 0.017020 -0.995185
+vn -0.285872 0.050407 -0.956941
+vn -0.173648 -0.984808 0.000000
+vn -0.173647 -0.984808 -0.000006
+vn -0.173648 -0.984808 -0.000002
+vn -0.173647 -0.984808 -0.000003
+vn -0.173652 -0.984807 0.000009
+vn -0.173645 -0.984808 -0.000005
+vn -0.173652 -0.984807 0.000005
+vn -0.173644 -0.984809 -0.000005
+vn -0.173652 -0.984807 0.000003
+vn -0.173643 -0.984809 -0.000004
+vn -0.173650 -0.984807 0.000001
+vn -0.173680 -0.984802 0.000024
+vn -0.173592 -0.984818 0.000023
+vn -0.173693 -0.984800 0.000010
+vn -0.173644 -0.984808 -0.000000
+vn -0.173638 -0.984810 0.000013
+vn -0.173653 -0.984807 -0.000003
+vn -0.173644 -0.984808 0.000011
+vn -0.173646 -0.984808 0.000007
+vn -0.173645 -0.984808 -0.000007
+vn -0.173651 -0.984807 0.000003
+vn -0.173649 -0.984808 0.000000
+vn -0.173648 -0.984808 0.000001
+vn -0.173647 -0.984808 0.000002
+vn -0.173640 -0.984809 -0.000005
+vn -0.173647 -0.984808 -0.000001
+s off
+f 193/109/328 194/110/328 196/111/328
+f 195/109/329 196/110/329 198/111/329
+f 197/109/330 198/110/330 200/111/330
+f 199/109/331 200/110/331 202/111/331
+f 201/109/332 202/110/332 204/111/332
+f 203/109/333 204/110/333 206/111/333
+f 205/109/334 206/110/334 208/111/334
+f 207/109/335 208/110/335 210/111/335
+f 209/109/336 210/110/336 212/111/336
+f 211/109/337 212/110/337 214/111/337
+f 213/109/338 214/110/338 216/111/338
+f 215/109/339 216/110/339 218/111/339
+f 217/109/340 218/110/340 220/111/340
+f 219/109/341 220/110/341 222/111/341
+f 221/109/342 222/110/342 224/111/342
+f 223/109/343 224/110/343 226/111/343
+f 225/109/344 226/110/344 227/112/344
+f 227/109/345 228/110/345 229/112/345
+f 229/109/346 230/110/346 231/112/346
+f 231/109/347 232/110/347 233/112/347
+f 233/109/348 234/110/348 235/112/348
+f 235/109/349 236/110/349 237/112/349
+f 237/109/350 238/110/350 239/112/350
+f 239/109/351 240/110/351 241/112/351
+f 241/109/352 242/110/352 243/112/352
+f 243/109/353 244/110/353 245/112/353
+f 245/109/354 246/110/354 247/112/354
+f 247/109/355 248/110/355 249/112/355
+f 249/109/356 250/110/356 251/112/356
+f 251/109/357 252/110/357 253/112/357
+f 196/113/358 194/114/358 198/115/358
+f 255/109/359 256/110/359 193/112/359
+f 253/109/360 254/110/360 255/112/360
+f 193/113/361 195/114/361 255/115/361
+f 195/112/328 193/109/328 196/111/328
+f 197/112/362 195/109/362 198/111/362
+f 199/112/363 197/109/363 200/111/363
+f 201/112/364 199/109/364 202/111/364
+f 203/112/365 201/109/365 204/111/365
+f 205/112/366 203/109/366 206/111/366
+f 207/112/367 205/109/367 208/111/367
+f 209/112/368 207/109/368 210/111/368
+f 211/112/369 209/109/369 212/111/369
+f 213/112/370 211/109/370 214/111/370
+f 215/112/371 213/109/371 216/111/371
+f 217/112/372 215/109/372 218/111/372
+f 219/112/373 217/109/373 220/111/373
+f 221/112/374 219/109/374 222/111/374
+f 223/112/375 221/109/375 224/111/375
+f 225/112/343 223/109/343 226/111/343
+f 226/110/376 228/111/376 227/112/376
+f 228/110/377 230/111/377 229/112/377
+f 230/110/378 232/111/378 231/112/378
+f 232/110/379 234/111/379 233/112/379
+f 234/110/380 236/111/380 235/112/380
+f 236/110/381 238/111/381 237/112/381
+f 238/110/382 240/111/382 239/112/382
+f 240/110/383 242/111/383 241/112/383
+f 242/110/384 244/111/384 243/112/384
+f 244/110/385 246/111/385 245/112/385
+f 246/110/386 248/111/386 247/112/386
+f 248/110/387 250/111/387 249/112/387
+f 250/110/388 252/111/388 251/112/388
+f 252/110/389 254/111/389 253/112/389
+f 194/114/390 256/116/390 198/115/390
+f 256/116/391 254/117/391 198/115/391
+f 254/117/392 252/118/392 198/115/392
+f 252/118/393 250/119/393 198/115/393
+f 250/119/394 248/120/394 198/115/394
+f 248/120/395 246/121/395 198/115/395
+f 246/121/396 244/122/396 198/115/396
+f 244/122/397 242/123/397 198/115/397
+f 242/123/398 240/124/398 198/115/398
+f 240/124/399 238/125/399 198/115/399
+f 238/125/400 236/126/400 198/115/400
+f 236/126/401 234/127/401 198/115/401
+f 234/127/402 232/128/402 198/115/402
+f 232/128/403 230/129/403 198/115/403
+f 230/129/404 228/130/404 198/115/404
+f 228/130/405 226/131/405 198/115/405
+f 226/131/406 224/132/406 198/115/406
+f 224/132/407 222/133/407 198/115/407
+f 222/133/408 220/134/408 198/115/408
+f 220/134/409 218/135/409 198/115/409
+f 218/135/410 216/136/410 198/115/410
+f 216/136/411 214/137/411 198/115/411
+f 214/137/412 212/138/412 198/115/412
+f 212/138/413 210/139/413 198/115/413
+f 210/139/398 208/140/398 198/115/398
+f 208/140/414 206/141/414 198/115/414
+f 206/141/415 204/142/415 198/115/415
+f 204/142/416 202/143/416 200/144/416
+f 198/115/417 204/142/417 200/144/417
+f 256/110/418 194/111/418 193/112/418
+f 254/110/419 256/111/419 255/112/419
+f 195/114/420 197/116/420 255/115/420
+f 197/116/421 199/117/421 255/115/421
+f 199/117/422 201/118/422 255/115/422
+f 201/118/423 203/119/423 255/115/423
+f 203/119/424 205/120/424 255/115/424
+f 205/120/425 207/121/425 255/115/425
+f 207/121/426 209/122/426 255/115/426
+f 209/122/427 211/123/427 255/115/427
+f 211/123/428 213/124/428 255/115/428
+f 213/124/429 215/125/429 255/115/429
+f 215/125/430 217/126/430 255/115/430
+f 237/136/431 233/134/431 235/135/431
+f 247/141/432 243/139/432 245/140/432
+f 241/138/433 237/136/433 239/137/433
+f 241/138/434 233/134/434 237/136/434
+f 251/143/435 247/141/435 249/142/435
+f 251/143/436 243/139/436 247/141/436
+f 219/127/437 221/128/437 223/129/437
+f 219/127/438 223/129/438 225/130/438
+f 233/134/439 229/132/439 231/133/439
+f 241/138/440 229/132/440 233/134/440
+f 255/115/437 251/143/437 253/144/437
+f 255/115/441 217/126/441 251/143/441
+f 251/143/441 217/126/441 243/139/441
+f 219/127/442 225/130/442 227/131/442
+f 217/126/443 219/127/443 227/131/443
+f 243/139/420 217/126/420 227/131/420
+f 241/138/444 243/139/444 229/132/444
+f 243/139/445 227/131/445 229/132/445
+v -3.858562 2.027707 3.871576
+v -3.858562 2.027707 -3.907549
+v 3.920563 2.027707 -3.907549
+v 3.920563 2.027707 3.871576
+v -3.858562 2.306528 3.871576
+v -3.858562 2.306528 -3.907549
+v 3.920563 2.306528 -3.907549
+v 3.920563 2.306528 3.871576
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 0.000000 1.000000
+vt 1.000000 1.000000
+vn -1.000000 0.000000 0.000000
+vn 0.000000 0.000000 -1.000000
+vn 1.000000 -0.000000 0.000000
+vn 0.000000 0.000000 1.000000
+vn 0.000000 -1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+s off
+f 261/145/446 262/146/446 257/147/446
+f 262/145/447 263/146/447 258/147/447
+f 263/145/448 264/146/448 260/148/448
+f 264/145/449 261/146/449 257/148/449
+f 257/145/450 258/146/450 259/148/450
+f 264/145/451 263/146/451 262/148/451
+f 262/146/446 258/148/446 257/147/446
+f 263/146/447 259/148/447 258/147/447
+f 259/147/448 263/145/448 260/148/448
+f 260/147/449 264/145/449 257/148/449
+f 260/147/450 257/145/450 259/148/450
+f 261/147/451 264/145/451 262/148/451
+v 2.043798 0.024218 -3.001008
+v 2.043798 2.024218 -3.001008
+v 2.238889 0.024218 -2.981793
+v 2.238889 2.024218 -2.981793
+v 2.426482 0.024218 -2.924888
+v 2.426482 2.024218 -2.924888
+v 2.599369 0.024218 -2.832478
+v 2.599369 2.024218 -2.832478
+v 2.750905 0.024218 -2.708115
+v 2.750905 2.024218 -2.708115
+v 2.875268 0.024218 -2.556578
+v 2.875268 2.024218 -2.556578
+v 2.967678 0.024218 -2.383692
+v 2.967678 2.024218 -2.383692
+v 3.024584 0.024218 -2.196099
+v 3.024584 2.024218 -2.196099
+v 3.043798 0.024218 -2.001008
+v 3.043798 2.024218 -2.001008
+v 3.024584 0.024218 -1.805918
+v 3.024584 2.024218 -1.805918
+v 2.967678 0.024218 -1.618325
+v 2.967678 2.024218 -1.618325
+v 2.875268 0.024218 -1.445438
+v 2.875268 2.024218 -1.445438
+v 2.750905 0.024218 -1.293901
+v 2.750905 2.024218 -1.293901
+v 2.599369 0.024218 -1.169539
+v 2.599369 2.024218 -1.169539
+v 2.426482 0.024218 -1.077129
+v 2.426482 2.024218 -1.077129
+v 2.238889 0.024218 -1.020223
+v 2.238889 2.024218 -1.020223
+v 2.043798 0.024218 -1.001008
+v 2.043798 2.024218 -1.001008
+v 1.848708 0.024218 -1.020223
+v 1.848708 2.024218 -1.020223
+v 1.661115 0.024218 -1.077129
+v 1.661115 2.024218 -1.077129
+v 1.488228 0.024218 -1.169539
+v 1.488228 2.024218 -1.169539
+v 1.336691 0.024218 -1.293902
+v 1.336691 2.024218 -1.293902
+v 1.212328 0.024218 -1.445439
+v 1.212328 2.024218 -1.445439
+v 1.119919 0.024218 -1.618326
+v 1.119919 2.024218 -1.618326
+v 1.063013 0.024218 -1.805919
+v 1.063013 2.024218 -1.805919
+v 1.043798 0.024218 -2.001009
+v 1.043798 2.024218 -2.001009
+v 1.063013 0.024218 -2.196100
+v 1.063013 2.024218 -2.196100
+v 1.119919 0.024218 -2.383693
+v 1.119919 2.024218 -2.383693
+v 1.212330 0.024218 -2.556580
+v 1.212330 2.024218 -2.556580
+v 1.336693 0.024218 -2.708116
+v 1.336693 2.024218 -2.708116
+v 1.488229 0.024218 -2.832479
+v 1.488229 2.024218 -2.832479
+v 1.661116 0.024218 -2.924888
+v 1.661116 2.024218 -2.924888
+v 1.848710 0.024218 -2.981794
+v 1.848710 2.024218 -2.981794
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.098018 0.000000 -0.995185
+vn 0.290285 0.000000 -0.956940
+vn 0.471397 0.000000 -0.881921
+vn 0.634393 0.000000 -0.773011
+vn 0.773011 0.000000 -0.634393
+vn 0.881921 0.000000 -0.471398
+vn 0.956940 0.000000 -0.290285
+vn 0.995185 0.000000 -0.098017
+vn 0.995185 0.000000 0.098017
+vn 0.956940 0.000000 0.290285
+vn 0.881921 0.000000 0.471397
+vn 0.773011 0.000000 0.634393
+vn 0.634393 0.000000 0.773011
+vn 0.471397 0.000000 0.881921
+vn 0.290285 0.000000 0.956940
+vn 0.098017 0.000000 0.995185
+vn -0.098018 0.000000 0.995185
+vn -0.290285 0.000000 0.956940
+vn -0.471397 0.000000 0.881921
+vn -0.634394 0.000000 0.773010
+vn -0.773011 0.000000 0.634393
+vn -0.881922 0.000000 0.471396
+vn -0.956941 0.000000 0.290283
+vn -0.995185 0.000000 0.098017
+vn -0.995185 -0.000000 -0.098018
+vn -0.956940 -0.000000 -0.290286
+vn -0.881921 -0.000000 -0.471398
+vn -0.773010 -0.000000 -0.634394
+vn -0.634392 -0.000000 -0.773012
+vn -0.471396 -0.000000 -0.881922
+vn -0.098016 -0.000000 -0.995185
+vn -0.290283 -0.000000 -0.956941
+s off
+f 265/149/452 266/150/452 268/151/452
+f 267/149/453 268/150/453 270/151/453
+f 269/149/454 270/150/454 272/151/454
+f 271/149/455 272/150/455 274/151/455
+f 273/149/456 274/150/456 276/151/456
+f 275/149/457 276/150/457 278/151/457
+f 277/149/458 278/150/458 280/151/458
+f 279/149/459 280/150/459 282/151/459
+f 281/149/460 282/150/460 284/151/460
+f 283/149/461 284/150/461 286/151/461
+f 285/149/462 286/150/462 288/151/462
+f 287/149/463 288/150/463 290/151/463
+f 289/149/464 290/150/464 292/151/464
+f 291/149/465 292/150/465 294/151/465
+f 293/149/466 294/150/466 296/151/466
+f 295/149/467 296/150/467 298/151/467
+f 297/149/468 298/150/468 299/152/468
+f 299/149/469 300/150/469 301/152/469
+f 301/149/470 302/150/470 303/152/470
+f 303/149/471 304/150/471 305/152/471
+f 305/149/472 306/150/472 307/152/472
+f 307/149/473 308/150/473 309/152/473
+f 309/149/474 310/150/474 311/152/474
+f 311/149/475 312/150/475 313/152/475
+f 313/149/476 314/150/476 315/152/476
+f 315/149/477 316/150/477 317/152/477
+f 317/149/478 318/150/478 319/152/478
+f 319/149/479 320/150/479 321/152/479
+f 321/149/480 322/150/480 323/152/480
+f 323/149/481 324/150/481 325/152/481
+f 268/153/451 266/154/451 270/155/451
+f 327/149/482 328/150/482 265/152/482
+f 325/149/483 326/150/483 327/152/483
+f 265/153/450 267/154/450 327/155/450
+f 267/152/452 265/149/452 268/151/452
+f 269/152/453 267/149/453 270/151/453
+f 271/152/454 269/149/454 272/151/454
+f 273/152/455 271/149/455 274/151/455
+f 275/152/456 273/149/456 276/151/456
+f 277/152/457 275/149/457 278/151/457
+f 279/152/458 277/149/458 280/151/458
+f 281/152/459 279/149/459 282/151/459
+f 283/152/460 281/149/460 284/151/460
+f 285/152/461 283/149/461 286/151/461
+f 287/152/462 285/149/462 288/151/462
+f 289/152/463 287/149/463 290/151/463
+f 291/152/464 289/149/464 292/151/464
+f 293/152/465 291/149/465 294/151/465
+f 295/152/466 293/149/466 296/151/466
+f 297/152/467 295/149/467 298/151/467
+f 298/150/468 300/151/468 299/152/468
+f 300/150/469 302/151/469 301/152/469
+f 302/150/470 304/151/470 303/152/470
+f 304/150/471 306/151/471 305/152/471
+f 306/150/472 308/151/472 307/152/472
+f 308/150/473 310/151/473 309/152/473
+f 310/150/474 312/151/474 311/152/474
+f 312/150/475 314/151/475 313/152/475
+f 314/150/476 316/151/476 315/152/476
+f 316/150/477 318/151/477 317/152/477
+f 318/150/478 320/151/478 319/152/478
+f 320/150/479 322/151/479 321/152/479
+f 322/150/480 324/151/480 323/152/480
+f 324/150/481 326/151/481 325/152/481
+f 266/154/451 328/156/451 270/155/451
+f 328/156/451 326/157/451 270/155/451
+f 326/157/451 324/158/451 270/155/451
+f 324/158/451 322/159/451 270/155/451
+f 322/159/451 320/160/451 270/155/451
+f 320/160/451 318/161/451 270/155/451
+f 318/161/451 316/162/451 270/155/451
+f 316/162/451 314/163/451 270/155/451
+f 314/163/451 312/164/451 270/155/451
+f 312/164/451 310/165/451 270/155/451
+f 310/165/451 308/166/451 270/155/451
+f 308/166/451 306/167/451 270/155/451
+f 306/167/451 304/168/451 270/155/451
+f 304/168/451 302/169/451 270/155/451
+f 302/169/451 300/170/451 270/155/451
+f 300/170/451 298/171/451 270/155/451
+f 298/171/451 296/172/451 270/155/451
+f 296/172/451 294/173/451 270/155/451
+f 294/173/451 292/174/451 270/155/451
+f 292/174/451 290/175/451 270/155/451
+f 290/175/451 288/176/451 270/155/451
+f 288/176/451 286/177/451 270/155/451
+f 286/177/451 284/178/451 270/155/451
+f 284/178/451 282/179/451 270/155/451
+f 282/179/451 280/180/451 270/155/451
+f 280/180/451 278/181/451 270/155/451
+f 278/181/451 276/182/451 270/155/451
+f 276/182/451 274/183/451 270/155/451
+f 274/183/451 272/184/451 270/155/451
+f 328/150/482 266/151/482 265/152/482
+f 326/150/483 328/151/483 327/152/483
+f 267/154/450 269/156/450 327/155/450
+f 269/156/450 271/157/450 327/155/450
+f 271/157/450 273/158/450 327/155/450
+f 273/158/450 275/159/450 327/155/450
+f 275/159/450 277/160/450 327/155/450
+f 277/160/450 279/161/450 327/155/450
+f 279/161/450 281/162/450 327/155/450
+f 281/162/450 283/163/450 327/155/450
+f 283/163/450 285/164/450 327/155/450
+f 285/164/450 287/165/450 327/155/450
+f 287/165/450 289/166/450 327/155/450
+f 289/166/450 291/167/450 327/155/450
+f 291/167/450 293/168/450 327/155/450
+f 293/168/450 295/169/450 327/155/450
+f 295/169/450 297/170/450 327/155/450
+f 297/170/450 299/171/450 327/155/450
+f 299/171/450 301/172/450 327/155/450
+f 301/172/450 303/173/450 327/155/450
+f 303/173/450 305/174/450 327/155/450
+f 305/174/450 307/175/450 327/155/450
+f 307/175/450 309/176/450 327/155/450
+f 309/176/450 311/177/450 327/155/450
+f 311/177/450 313/178/450 327/155/450
+f 313/178/450 315/179/450 327/155/450
+f 315/179/450 317/180/450 327/155/450
+f 317/180/450 319/181/450 327/155/450
+f 319/181/450 321/182/450 327/155/450
+f 321/182/450 323/183/450 325/184/450
+f 327/155/450 321/182/450 325/184/450
+v -2.014818 0.007922 0.998641
+v -2.014818 2.007922 0.998641
+v -1.819728 0.007922 1.017856
+v -1.819728 2.007922 1.017856
+v -1.632135 0.007922 1.074762
+v -1.632135 2.007922 1.074762
+v -1.459248 0.007922 1.167172
+v -1.459248 2.007922 1.167172
+v -1.307712 0.007922 1.291534
+v -1.307712 2.007922 1.291534
+v -1.183349 0.007922 1.443071
+v -1.183349 2.007922 1.443071
+v -1.090939 0.007922 1.615958
+v -1.090939 2.007922 1.615958
+v -1.034033 0.007922 1.803551
+v -1.034033 2.007922 1.803551
+v -1.014818 0.007922 1.998641
+v -1.014818 2.007922 1.998641
+v -1.034033 0.007922 2.193732
+v -1.034033 2.007922 2.193732
+v -1.090939 0.007922 2.381325
+v -1.090939 2.007922 2.381325
+v -1.183349 0.007922 2.554211
+v -1.183349 2.007922 2.554211
+v -1.307712 0.007922 2.705748
+v -1.307712 2.007922 2.705748
+v -1.459248 0.007922 2.830111
+v -1.459248 2.007922 2.830111
+v -1.632135 0.007922 2.922521
+v -1.632135 2.007922 2.922521
+v -1.819728 0.007922 2.979427
+v -1.819728 2.007922 2.979427
+v -2.014819 0.007922 2.998641
+v -2.014819 2.007922 2.998641
+v -2.209909 0.007922 2.979426
+v -2.209909 2.007922 2.979426
+v -2.397502 0.007922 2.922521
+v -2.397502 2.007922 2.922521
+v -2.570389 0.007922 2.830111
+v -2.570389 2.007922 2.830111
+v -2.721926 0.007922 2.705748
+v -2.721926 2.007922 2.705748
+v -2.846288 0.007922 2.554211
+v -2.846288 2.007922 2.554211
+v -2.938698 0.007922 2.381324
+v -2.938698 2.007922 2.381324
+v -2.995604 0.007922 2.193731
+v -2.995604 2.007922 2.193731
+v -3.014818 0.007922 1.998640
+v -3.014818 2.007922 1.998640
+v -2.995604 0.007922 1.803550
+v -2.995604 2.007922 1.803550
+v -2.938698 0.007922 1.615957
+v -2.938698 2.007922 1.615957
+v -2.846287 0.007922 1.443070
+v -2.846287 2.007922 1.443070
+v -2.721924 0.007922 1.291534
+v -2.721924 2.007922 1.291534
+v -2.570388 0.007922 1.167171
+v -2.570388 2.007922 1.167171
+v -2.397501 0.007922 1.074761
+v -2.397501 2.007922 1.074761
+v -2.209907 0.007922 1.017856
+v -2.209907 2.007922 1.017856
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.098017 0.000000 -0.995185
+vn 0.881921 0.000000 -0.471397
+vn 0.634393 0.000000 0.773010
+vn 0.471396 0.000000 0.881922
+vn -0.881920 -0.000000 -0.471398
+vn -0.634393 -0.000000 -0.773011
+vn -0.471395 -0.000000 -0.881922
+s off
+f 329/185/484 330/186/484 332/187/484
+f 331/185/453 332/186/453 334/187/453
+f 333/185/454 334/186/454 336/187/454
+f 335/185/455 336/186/455 338/187/455
+f 337/185/456 338/186/456 340/187/456
+f 339/185/485 340/186/485 342/187/485
+f 341/185/458 342/186/458 344/187/458
+f 343/185/459 344/186/459 346/187/459
+f 345/185/460 346/186/460 348/187/460
+f 347/185/461 348/186/461 350/187/461
+f 349/185/462 350/186/462 352/187/462
+f 351/185/463 352/186/463 354/187/463
+f 353/185/486 354/186/486 356/187/486
+f 355/185/487 356/186/487 358/187/487
+f 357/185/466 358/186/466 360/187/466
+f 359/185/467 360/186/467 362/187/467
+f 361/185/468 362/186/468 363/188/468
+f 363/185/469 364/186/469 365/188/469
+f 365/185/470 366/186/470 367/188/470
+f 367/185/471 368/186/471 369/188/471
+f 369/185/472 370/186/472 371/188/472
+f 371/185/473 372/186/473 373/188/473
+f 373/185/474 374/186/474 375/188/474
+f 375/185/475 376/186/475 377/188/475
+f 377/185/476 378/186/476 379/188/476
+f 379/185/477 380/186/477 381/188/477
+f 381/185/488 382/186/488 383/188/488
+f 383/185/479 384/186/479 385/188/479
+f 385/185/489 386/186/489 387/188/489
+f 387/185/490 388/186/490 389/188/490
+f 332/189/451 330/190/451 334/191/451
+f 391/185/482 392/186/482 329/188/482
+f 389/185/483 390/186/483 391/188/483
+f 329/189/450 331/190/450 391/191/450
+f 331/188/484 329/185/484 332/187/484
+f 333/188/453 331/185/453 334/187/453
+f 335/188/454 333/185/454 336/187/454
+f 337/188/455 335/185/455 338/187/455
+f 339/188/456 337/185/456 340/187/456
+f 341/188/485 339/185/485 342/187/485
+f 343/188/458 341/185/458 344/187/458
+f 345/188/459 343/185/459 346/187/459
+f 347/188/460 345/185/460 348/187/460
+f 349/188/461 347/185/461 350/187/461
+f 351/188/462 349/185/462 352/187/462
+f 353/188/463 351/185/463 354/187/463
+f 355/188/486 353/185/486 356/187/486
+f 357/188/487 355/185/487 358/187/487
+f 359/188/466 357/185/466 360/187/466
+f 361/188/467 359/185/467 362/187/467
+f 362/186/468 364/187/468 363/188/468
+f 364/186/469 366/187/469 365/188/469
+f 366/186/470 368/187/470 367/188/470
+f 368/186/471 370/187/471 369/188/471
+f 370/186/472 372/187/472 371/188/472
+f 372/186/473 374/187/473 373/188/473
+f 374/186/474 376/187/474 375/188/474
+f 376/186/475 378/187/475 377/188/475
+f 378/186/476 380/187/476 379/188/476
+f 380/186/477 382/187/477 381/188/477
+f 382/186/488 384/187/488 383/188/488
+f 384/186/479 386/187/479 385/188/479
+f 386/186/489 388/187/489 387/188/489
+f 388/186/490 390/187/490 389/188/490
+f 330/190/451 392/192/451 334/191/451
+f 392/192/451 390/193/451 334/191/451
+f 390/193/451 388/194/451 334/191/451
+f 388/194/451 386/195/451 334/191/451
+f 386/195/451 384/196/451 334/191/451
+f 384/196/451 382/197/451 334/191/451
+f 382/197/451 380/198/451 334/191/451
+f 380/198/451 378/199/451 334/191/451
+f 378/199/451 376/200/451 334/191/451
+f 376/200/451 374/201/451 334/191/451
+f 374/201/451 372/202/451 334/191/451
+f 372/202/451 370/203/451 334/191/451
+f 370/203/451 368/204/451 334/191/451
+f 368/204/451 366/205/451 334/191/451
+f 366/205/451 364/206/451 334/191/451
+f 364/206/451 362/207/451 334/191/451
+f 362/207/451 360/208/451 334/191/451
+f 360/208/451 358/209/451 334/191/451
+f 358/209/451 356/210/451 334/191/451
+f 356/210/451 354/211/451 334/191/451
+f 354/211/451 352/212/451 334/191/451
+f 352/212/451 350/213/451 334/191/451
+f 350/213/451 348/214/451 334/191/451
+f 348/214/451 346/215/451 334/191/451
+f 346/215/451 344/216/451 334/191/451
+f 344/216/451 342/217/451 334/191/451
+f 342/217/451 340/218/451 334/191/451
+f 340/218/451 338/219/451 334/191/451
+f 338/219/451 336/220/451 334/191/451
+f 392/186/482 330/187/482 329/188/482
+f 390/186/483 392/187/483 391/188/483
+f 331/190/450 333/192/450 391/191/450
+f 333/192/450 335/193/450 391/191/450
+f 335/193/450 337/194/450 391/191/450
+f 337/194/450 339/195/450 391/191/450
+f 339/195/450 341/196/450 391/191/450
+f 341/196/450 343/197/450 391/191/450
+f 343/197/450 345/198/450 391/191/450
+f 345/198/450 347/199/450 391/191/450
+f 347/199/450 349/200/450 391/191/450
+f 349/200/450 351/201/450 391/191/450
+f 351/201/450 353/202/450 391/191/450
+f 353/202/450 355/203/450 391/191/450
+f 355/203/450 357/204/450 391/191/450
+f 357/204/450 359/205/450 391/191/450
+f 359/205/450 361/206/450 391/191/450
+f 361/206/450 363/207/450 391/191/450
+f 363/207/450 365/208/450 391/191/450
+f 365/208/450 367/209/450 391/191/450
+f 367/209/450 369/210/450 391/191/450
+f 369/210/450 371/211/450 391/191/450
+f 371/211/450 373/212/450 391/191/450
+f 373/212/450 375/213/450 391/191/450
+f 375/213/450 377/214/450 391/191/450
+f 377/214/450 379/215/450 391/191/450
+f 379/215/450 381/216/450 391/191/450
+f 381/216/450 383/217/450 391/191/450
+f 383/217/450 385/218/450 391/191/450
+f 385/218/450 387/219/450 391/191/450
+f 387/219/450 389/220/450 391/191/450
+v -2.001621 -0.021814 -3.021079
+v -2.001621 1.978186 -3.021079
+v -1.806530 -0.021814 -3.001864
+v -1.806530 1.978186 -3.001864
+v -1.618937 -0.021814 -2.944958
+v -1.618937 1.978186 -2.944958
+v -1.446051 -0.021814 -2.852548
+v -1.446051 1.978186 -2.852548
+v -1.294514 -0.021814 -2.728185
+v -1.294514 1.978186 -2.728185
+v -1.170151 -0.021814 -2.576649
+v -1.170151 1.978186 -2.576649
+v -1.077741 -0.021814 -2.403762
+v -1.077741 1.978186 -2.403762
+v -1.020836 -0.021814 -2.216169
+v -1.020836 1.978186 -2.216169
+v -1.001621 -0.021814 -2.021079
+v -1.001621 1.978186 -2.021079
+v -1.020835 -0.021814 -1.825988
+v -1.020835 1.978186 -1.825988
+v -1.077741 -0.021814 -1.638395
+v -1.077741 1.978186 -1.638395
+v -1.170151 -0.021814 -1.465508
+v -1.170151 1.978186 -1.465508
+v -1.294514 -0.021814 -1.313972
+v -1.294514 1.978186 -1.313972
+v -1.446051 -0.021814 -1.189609
+v -1.446051 1.978186 -1.189609
+v -1.618937 -0.021814 -1.097199
+v -1.618937 1.978186 -1.097199
+v -1.806531 -0.021814 -1.040293
+v -1.806531 1.978186 -1.040293
+v -2.001621 -0.021814 -1.021079
+v -2.001621 1.978186 -1.021079
+v -2.196712 -0.021814 -1.040293
+v -2.196712 1.978186 -1.040293
+v -2.384305 -0.021814 -1.097199
+v -2.384305 1.978186 -1.097199
+v -2.557191 -0.021814 -1.189609
+v -2.557191 1.978186 -1.189609
+v -2.708728 -0.021814 -1.313972
+v -2.708728 1.978186 -1.313972
+v -2.833091 -0.021814 -1.465509
+v -2.833091 1.978186 -1.465509
+v -2.925501 -0.021814 -1.638396
+v -2.925501 1.978186 -1.638396
+v -2.982406 -0.021814 -1.825989
+v -2.982406 1.978186 -1.825989
+v -3.001621 -0.021814 -2.021080
+v -3.001621 1.978186 -2.021080
+v -2.982406 -0.021814 -2.216170
+v -2.982406 1.978186 -2.216170
+v -2.925500 -0.021814 -2.403763
+v -2.925500 1.978186 -2.403763
+v -2.833090 -0.021814 -2.576650
+v -2.833090 1.978186 -2.576650
+v -2.708727 -0.021814 -2.728186
+v -2.708727 1.978186 -2.728186
+v -2.557190 -0.021814 -2.852549
+v -2.557190 1.978186 -2.852549
+v -2.384303 -0.021814 -2.944959
+v -2.384303 1.978186 -2.944959
+v -2.196710 -0.021814 -3.001864
+v -2.196710 1.978186 -3.001864
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+vn 0.471398 0.000000 -0.881921
+vn -0.634393 0.000000 0.773010
+vn -0.881921 -0.000000 -0.471397
+vn -0.773009 -0.000000 -0.634395
+vn -0.634393 -0.000000 -0.773010
+s off
+f 393/221/484 394/222/484 396/223/484
+f 395/221/453 396/222/453 398/223/453
+f 397/221/491 398/222/491 400/223/491
+f 399/221/455 400/222/455 402/223/455
+f 401/221/456 402/222/456 404/223/456
+f 403/221/485 404/222/485 406/223/485
+f 405/221/458 406/222/458 408/223/458
+f 407/221/459 408/222/459 410/223/459
+f 409/221/460 410/222/460 412/223/460
+f 411/221/461 412/222/461 414/223/461
+f 413/221/462 414/222/462 416/223/462
+f 415/221/463 416/222/463 418/223/463
+f 417/221/464 418/222/464 420/223/464
+f 419/221/465 420/222/465 422/223/465
+f 421/221/466 422/222/466 424/223/466
+f 423/221/467 424/222/467 426/223/467
+f 425/221/468 426/222/468 427/224/468
+f 427/221/469 428/222/469 429/224/469
+f 429/221/470 430/222/470 431/224/470
+f 431/221/492 432/222/492 433/224/492
+f 433/221/472 434/222/472 435/224/472
+f 435/221/473 436/222/473 437/224/473
+f 437/221/474 438/222/474 439/224/474
+f 439/221/475 440/222/475 441/224/475
+f 441/221/476 442/222/476 443/224/476
+f 443/221/477 444/222/477 445/224/477
+f 445/221/493 446/222/493 447/224/493
+f 447/221/494 448/222/494 449/224/494
+f 449/221/495 450/222/495 451/224/495
+f 451/221/490 452/222/490 453/224/490
+f 396/225/451 394/226/451 398/227/451
+f 455/221/482 456/222/482 393/224/482
+f 453/221/483 454/222/483 455/224/483
+f 393/225/450 395/226/450 455/227/450
+f 395/224/484 393/221/484 396/223/484
+f 397/224/453 395/221/453 398/223/453
+f 399/224/491 397/221/491 400/223/491
+f 401/224/455 399/221/455 402/223/455
+f 403/224/456 401/221/456 404/223/456
+f 405/224/485 403/221/485 406/223/485
+f 407/224/458 405/221/458 408/223/458
+f 409/224/459 407/221/459 410/223/459
+f 411/224/460 409/221/460 412/223/460
+f 413/224/461 411/221/461 414/223/461
+f 415/224/462 413/221/462 416/223/462
+f 417/224/463 415/221/463 418/223/463
+f 419/224/464 417/221/464 420/223/464
+f 421/224/465 419/221/465 422/223/465
+f 423/224/466 421/221/466 424/223/466
+f 425/224/467 423/221/467 426/223/467
+f 426/222/468 428/223/468 427/224/468
+f 428/222/469 430/223/469 429/224/469
+f 430/222/470 432/223/470 431/224/470
+f 432/222/492 434/223/492 433/224/492
+f 434/222/472 436/223/472 435/224/472
+f 436/222/473 438/223/473 437/224/473
+f 438/222/474 440/223/474 439/224/474
+f 440/222/475 442/223/475 441/224/475
+f 442/222/476 444/223/476 443/224/476
+f 444/222/477 446/223/477 445/224/477
+f 446/222/493 448/223/493 447/224/493
+f 448/222/494 450/223/494 449/224/494
+f 450/222/495 452/223/495 451/224/495
+f 452/222/490 454/223/490 453/224/490
+f 394/226/451 456/228/451 398/227/451
+f 456/228/451 454/229/451 398/227/451
+f 454/229/451 452/230/451 398/227/451
+f 452/230/451 450/231/451 398/227/451
+f 450/231/451 448/232/451 398/227/451
+f 448/232/451 446/233/451 398/227/451
+f 446/233/451 444/234/451 398/227/451
+f 444/234/451 442/235/451 398/227/451
+f 442/235/451 440/236/451 398/227/451
+f 440/236/451 438/237/451 398/227/451
+f 438/237/451 436/238/451 398/227/451
+f 436/238/451 434/239/451 398/227/451
+f 434/239/451 432/240/451 398/227/451
+f 432/240/451 430/241/451 398/227/451
+f 430/241/451 428/242/451 398/227/451
+f 428/242/451 426/243/451 398/227/451
+f 426/243/451 424/244/451 398/227/451
+f 424/244/451 422/245/451 398/227/451
+f 422/245/451 420/246/451 398/227/451
+f 420/246/451 418/247/451 398/227/451
+f 418/247/451 416/248/451 398/227/451
+f 416/248/451 414/249/451 398/227/451
+f 414/249/451 412/250/451 398/227/451
+f 412/250/451 410/251/451 398/227/451
+f 410/251/451 408/252/451 398/227/451
+f 408/252/451 406/253/451 398/227/451
+f 406/253/451 404/254/451 398/227/451
+f 404/254/451 402/255/451 398/227/451
+f 402/255/451 400/256/451 398/227/451
+f 456/222/482 394/223/482 393/224/482
+f 454/222/483 456/223/483 455/224/483
+f 395/226/450 397/228/450 455/227/450
+f 397/228/450 399/229/450 455/227/450
+f 399/229/450 401/230/450 455/227/450
+f 401/230/450 403/231/450 455/227/450
+f 403/231/450 405/232/450 455/227/450
+f 405/232/450 407/233/450 455/227/450
+f 407/233/450 409/234/450 455/227/450
+f 409/234/450 411/235/450 455/227/450
+f 411/235/450 413/236/450 455/227/450
+f 413/236/450 415/237/450 455/227/450
+f 415/237/450 417/238/450 455/227/450
+f 417/238/450 419/239/450 455/227/450
+f 419/239/450 421/240/450 455/227/450
+f 421/240/450 423/241/450 455/227/450
+f 423/241/450 425/242/450 455/227/450
+f 425/242/450 427/243/450 455/227/450
+f 427/243/450 429/244/450 455/227/450
+f 429/244/450 431/245/450 455/227/450
+f 431/245/450 433/246/450 455/227/450
+f 433/246/450 435/247/450 455/227/450
+f 435/247/450 437/248/450 455/227/450
+f 437/248/450 439/249/450 455/227/450
+f 439/249/450 441/250/450 455/227/450
+f 441/250/450 443/251/450 455/227/450
+f 443/251/450 445/252/450 455/227/450
+f 445/252/450 447/253/450 455/227/450
+f 447/253/450 449/254/450 455/227/450
+f 449/254/450 451/255/450 455/227/450
+f 451/255/450 453/256/450 455/227/450
+v 2.021592 0.003623 1.016610
+v 2.021592 2.003623 1.016610
+v 2.216682 0.003623 1.035825
+v 2.216682 2.003623 1.035825
+v 2.404276 0.003623 1.092731
+v 2.404276 2.003623 1.092731
+v 2.577162 0.003623 1.185141
+v 2.577162 2.003623 1.185141
+v 2.728699 0.003623 1.309503
+v 2.728699 2.003623 1.309503
+v 2.853062 0.003623 1.461040
+v 2.853062 2.003623 1.461040
+v 2.945472 0.003623 1.633927
+v 2.945472 2.003623 1.633927
+v 3.002378 0.003623 1.821520
+v 3.002378 2.003623 1.821520
+v 3.021592 0.003623 2.016610
+v 3.021592 2.003623 2.016610
+v 3.002378 0.003623 2.211700
+v 3.002378 2.003623 2.211700
+v 2.945472 0.003623 2.399293
+v 2.945472 2.003623 2.399293
+v 2.853062 0.003623 2.572180
+v 2.853062 2.003623 2.572180
+v 2.728699 0.003623 2.723717
+v 2.728699 2.003623 2.723717
+v 2.577162 0.003623 2.848080
+v 2.577162 2.003623 2.848080
+v 2.404275 0.003623 2.940490
+v 2.404275 2.003623 2.940490
+v 2.216682 0.003623 2.997396
+v 2.216682 2.003623 2.997396
+v 2.021592 0.003623 3.016610
+v 2.021592 2.003623 3.016610
+v 1.826501 0.003623 2.997395
+v 1.826501 2.003623 2.997395
+v 1.638908 0.003623 2.940490
+v 1.638908 2.003623 2.940490
+v 1.466021 0.003623 2.848079
+v 1.466021 2.003623 2.848079
+v 1.314485 0.003623 2.723716
+v 1.314485 2.003623 2.723716
+v 1.190122 0.003623 2.572180
+v 1.190122 2.003623 2.572180
+v 1.097712 0.003623 2.399293
+v 1.097712 2.003623 2.399293
+v 1.040807 0.003623 2.211699
+v 1.040807 2.003623 2.211699
+v 1.021592 0.003623 2.016609
+v 1.021592 2.003623 2.016609
+v 1.040807 0.003623 1.821519
+v 1.040807 2.003623 1.821519
+v 1.097713 0.003623 1.633926
+v 1.097713 2.003623 1.633926
+v 1.190123 0.003623 1.461039
+v 1.190123 2.003623 1.461039
+v 1.314486 0.003623 1.309502
+v 1.314486 2.003623 1.309502
+v 1.466023 0.003623 1.185140
+v 1.466023 2.003623 1.185140
+v 1.638910 0.003623 1.092730
+v 1.638910 2.003623 1.092730
+v 1.826503 0.003623 1.035825
+v 1.826503 2.003623 1.035825
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.308658 0.038060
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.038060 0.308659
+vt 0.009607 0.402455
+vt 0.000000 0.500000
+vt 0.009607 0.597546
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vt 0.146447 0.853554
+vt 0.222215 0.915735
+vt 0.308659 0.961940
+s off
+f 457/257/484 458/258/484 460/259/484
+f 459/257/453 460/258/453 462/259/453
+f 461/257/454 462/258/454 464/259/454
+f 463/257/455 464/258/455 466/259/455
+f 465/257/456 466/258/456 468/259/456
+f 467/257/457 468/258/457 470/259/457
+f 469/257/458 470/258/458 472/259/458
+f 471/257/459 472/258/459 474/259/459
+f 473/257/460 474/258/460 476/259/460
+f 475/257/461 476/258/461 478/259/461
+f 477/257/462 478/258/462 480/259/462
+f 479/257/463 480/258/463 482/259/463
+f 481/257/464 482/258/464 484/259/464
+f 483/257/465 484/258/465 486/259/465
+f 485/257/466 486/258/466 488/259/466
+f 487/257/467 488/258/467 490/259/467
+f 489/257/468 490/258/468 491/260/468
+f 491/257/469 492/258/469 493/260/469
+f 493/257/470 494/258/470 495/260/470
+f 495/257/471 496/258/471 497/260/471
+f 497/257/472 498/258/472 499/260/472
+f 499/257/473 500/258/473 501/260/473
+f 501/257/474 502/258/474 503/260/474
+f 503/257/475 504/258/475 505/260/475
+f 505/257/476 506/258/476 507/260/476
+f 507/257/477 508/258/477 509/260/477
+f 509/257/478 510/258/478 511/260/478
+f 511/257/494 512/258/494 513/260/494
+f 513/257/489 514/258/489 515/260/489
+f 515/257/490 516/258/490 517/260/490
+f 460/261/451 458/262/451 462/263/451
+f 519/257/482 520/258/482 457/260/482
+f 517/257/483 518/258/483 519/260/483
+f 457/261/450 459/262/450 519/263/450
+f 459/260/484 457/257/484 460/259/484
+f 461/260/453 459/257/453 462/259/453
+f 463/260/454 461/257/454 464/259/454
+f 465/260/455 463/257/455 466/259/455
+f 467/260/456 465/257/456 468/259/456
+f 469/260/457 467/257/457 470/259/457
+f 471/260/458 469/257/458 472/259/458
+f 473/260/459 471/257/459 474/259/459
+f 475/260/460 473/257/460 476/259/460
+f 477/260/461 475/257/461 478/259/461
+f 479/260/462 477/257/462 480/259/462
+f 481/260/463 479/257/463 482/259/463
+f 483/260/464 481/257/464 484/259/464
+f 485/260/465 483/257/465 486/259/465
+f 487/260/466 485/257/466 488/259/466
+f 489/260/467 487/257/467 490/259/467
+f 490/258/468 492/259/468 491/260/468
+f 492/258/469 494/259/469 493/260/469
+f 494/258/470 496/259/470 495/260/470
+f 496/258/471 498/259/471 497/260/471
+f 498/258/472 500/259/472 499/260/472
+f 500/258/473 502/259/473 501/260/473
+f 502/258/474 504/259/474 503/260/474
+f 504/258/475 506/259/475 505/260/475
+f 506/258/476 508/259/476 507/260/476
+f 508/258/477 510/259/477 509/260/477
+f 510/258/478 512/259/478 511/260/478
+f 512/258/494 514/259/494 513/260/494
+f 514/258/489 516/259/489 515/260/489
+f 516/258/490 518/259/490 517/260/490
+f 458/262/451 520/264/451 462/263/451
+f 520/264/451 518/265/451 462/263/451
+f 518/265/451 516/266/451 462/263/451
+f 516/266/451 514/267/451 462/263/451
+f 514/267/451 512/268/451 462/263/451
+f 512/268/451 510/269/451 462/263/451
+f 510/269/451 508/270/451 462/263/451
+f 508/270/451 506/271/451 462/263/451
+f 506/271/451 504/272/451 462/263/451
+f 504/272/451 502/273/451 462/263/451
+f 502/273/451 500/274/451 462/263/451
+f 500/274/451 498/275/451 462/263/451
+f 498/275/451 496/276/451 462/263/451
+f 496/276/451 494/277/451 462/263/451
+f 494/277/451 492/278/451 462/263/451
+f 492/278/451 490/279/451 462/263/451
+f 490/279/451 488/280/451 462/263/451
+f 488/280/451 486/281/451 462/263/451
+f 486/281/451 484/282/451 462/263/451
+f 484/282/451 482/283/451 462/263/451
+f 482/283/451 480/284/451 462/263/451
+f 480/284/451 478/285/451 462/263/451
+f 478/285/451 476/286/451 462/263/451
+f 476/286/451 474/287/451 462/263/451
+f 474/287/451 472/288/451 462/263/451
+f 472/288/451 470/289/451 462/263/451
+f 470/289/451 468/290/451 462/263/451
+f 468/290/451 466/291/451 462/263/451
+f 466/291/451 464/292/451 462/263/451
+f 520/258/482 458/259/482 457/260/482
+f 518/258/483 520/259/483 519/260/483
+f 459/262/450 461/264/450 519/263/450
+f 461/264/450 463/265/450 519/263/450
+f 463/265/450 465/266/450 519/263/450
+f 465/266/450 467/267/450 519/263/450
+f 467/267/450 469/268/450 519/263/450
+f 469/268/450 471/269/450 519/263/450
+f 471/269/450 473/270/450 519/263/450
+f 473/270/450 475/271/450 519/263/450
+f 475/271/450 477/272/450 519/263/450
+f 477/272/450 479/273/450 519/263/450
+f 479/273/450 481/274/450 519/263/450
+f 481/274/450 483/275/450 519/263/450
+f 483/275/450 485/276/450 519/263/450
+f 485/276/450 487/277/450 519/263/450
+f 487/277/450 489/278/450 519/263/450
+f 489/278/450 491/279/450 519/263/450
+f 491/279/450 493/280/450 519/263/450
+f 493/280/450 495/281/450 519/263/450
+f 495/281/450 497/282/450 519/263/450
+f 497/282/450 499/283/450 519/263/450
+f 499/283/450 501/284/450 519/263/450
+f 501/284/450 503/285/450 519/263/450
+f 503/285/450 505/286/450 519/263/450
+f 505/286/450 507/287/450 519/263/450
+f 507/287/450 509/288/450 519/263/450
+f 509/288/450 511/289/450 519/263/450
+f 511/289/450 513/290/450 519/263/450
+f 513/290/450 515/291/450 519/263/450
+f 515/291/450 517/292/450 519/263/450
diff --git a/examples/datavisualization/graphgallery/data/pipe.obj b/examples/datavisualization/graphgallery/data/pipe.obj
new file mode 100644
index 000000000..6ccbb2860
--- /dev/null
+++ b/examples/datavisualization/graphgallery/data/pipe.obj
@@ -0,0 +1,330 @@
+# Blender v2.66 (sub 0) OBJ File: 'cylinder.blend'
+# www.blender.org
+o Cylinder
+v 0.000000 -1.000000 -1.000000
+v 0.000000 1.000000 -1.000000
+v 0.195090 -1.000000 -0.980785
+v 0.195090 1.000000 -0.980785
+v 0.382683 -1.000000 -0.923880
+v 0.382683 1.000000 -0.923880
+v 0.555570 -1.000000 -0.831470
+v 0.555570 1.000000 -0.831470
+v 0.707107 -1.000000 -0.707107
+v 0.707107 1.000000 -0.707107
+v 0.831470 -1.000000 -0.555570
+v 0.831470 1.000000 -0.555570
+v 0.923880 -1.000000 -0.382683
+v 0.923880 1.000000 -0.382683
+v 0.980785 -1.000000 -0.195090
+v 0.980785 1.000000 -0.195090
+v 1.000000 -1.000000 -0.000000
+v 1.000000 1.000000 -0.000000
+v 0.980785 -1.000000 0.195090
+v 0.980785 1.000000 0.195090
+v 0.923880 -1.000000 0.382683
+v 0.923880 1.000000 0.382683
+v 0.831470 -1.000000 0.555570
+v 0.831470 1.000000 0.555570
+v 0.707107 -1.000000 0.707107
+v 0.707107 1.000000 0.707107
+v 0.555570 -1.000000 0.831470
+v 0.555570 1.000000 0.831470
+v 0.382683 -1.000000 0.923880
+v 0.382683 1.000000 0.923880
+v 0.195090 -1.000000 0.980785
+v 0.195090 1.000000 0.980785
+v -0.000000 -1.000000 1.000000
+v -0.000000 1.000000 1.000000
+v -0.195091 -1.000000 0.980785
+v -0.195091 1.000000 0.980785
+v -0.382684 -1.000000 0.923879
+v -0.382684 1.000000 0.923879
+v -0.555571 -1.000000 0.831469
+v -0.555571 1.000000 0.831469
+v -0.707107 -1.000000 0.707106
+v -0.707107 1.000000 0.707106
+v -0.831470 -1.000000 0.555570
+v -0.831470 1.000000 0.555570
+v -0.923880 -1.000000 0.382683
+v -0.923880 1.000000 0.382683
+v -0.980785 -1.000000 0.195089
+v -0.980785 1.000000 0.195089
+v -1.000000 -1.000000 -0.000001
+v -1.000000 1.000000 -0.000001
+v -0.980785 -1.000000 -0.195091
+v -0.980785 1.000000 -0.195091
+v -0.923879 -1.000000 -0.382684