Compare commits

...

50 Commits

Author SHA1 Message Date
Norman Barker
1f52a62fe3
Merge 412fb2574742bab74d76dcaaa5b3cbdf0e5b4e6d into 2e07d9829f38ea12661050415d09e28675c961bd 2025-10-01 11:30:58 +02:00
Nyall Dawson
2e07d9829f Fix build 2025-10-01 18:00:57 +10:00
Nyall Dawson
7d31cc1dab Run code_cleanup script 2025-10-01 18:00:57 +10:00
qgis-bot
e28d12d058 auto sipify 🍺 2025-10-01 05:54:45 +00:00
Denis Rouzaud
0f1c0de0f8
Merge pull request #63214 from nyalldawson/annotation3d
Support rendering annotation layer markers as 3d billboards
2025-10-01 07:52:11 +02:00
qgis-bot
82d7ab9b62 auto sipify 🍺 2025-10-01 03:49:17 +00:00
Nyall Dawson
8bc1f639bb Update tests 2025-10-01 13:46:41 +10:00
Nyall Dawson
928bdd8cc4 Fix tooltip formatting for console actions
The tooltip text should be in bold, not the shortcut
2025-10-01 13:46:41 +10:00
Nyall Dawson
1db5d1d246 Add common actions for run script, run selection
Allows these shortcuts to be customised
2025-10-01 13:46:41 +10:00
Nyall Dawson
1e3167082c Use correct shortcut in context menu for console script editor
And add reformat code action to menu
2025-10-01 13:46:41 +10:00
Nyall Dawson
c6accd8517 Fix more misleading names
These objects are actions, not buttons
2025-10-01 13:46:41 +10:00
Nyall Dawson
41962a0a24 Use correct user-set shortcuts for console script editor code actions
And rename member variable for correctness
2025-10-01 13:46:41 +10:00
Nyall Dawson
0fc8b37487 Set shortcut for script editor toggle comment action 2025-10-01 13:46:41 +10:00
Nyall Dawson
1c53ab415b Use registered shortcuts for code editor comment/reformat actions 2025-10-01 13:46:41 +10:00
Nyall Dawson
579a818ce7 Add sequenceForCommonAction 2025-10-01 13:46:41 +10:00
Nyall Dawson
5ed5f51121 [api] Add framework to handle common actions in shortcuts manager
Common actions allow for shortcuts to be registered for actions
which do not yet exist, or which are not associated with a single
global QAction object attached to at the application level. For
example, code editor actions which will be created as children
of individual code editor widgets, but which should have
shortcuts available for user configuration via the shortcuts
manager dialog.
2025-10-01 13:46:41 +10:00
Nyall Dawson
62fbbe6392 Refactor to extract utility function 2025-10-01 13:46:41 +10:00
Nyall Dawson
ac1260d159 Handle shortcut manager sections which don't have trailing "/" 2025-10-01 13:46:41 +10:00
Nyall Dawson
d9a4c80aa9 Rename method to more generic name 2025-10-01 13:46:41 +10:00
Nyall Dawson
af0208c974
Refine z range logic 2025-10-01 12:47:45 +10:00
Nyall Dawson
a01770e5cb
[feature] Support rendering annotation layer markers as 3d billboards
This commit adds a new option for showing annotation layers in 3d maps.

From the annotation layer properties, 3d tab, users can set the layer
to render as 3d billboards. When activated, all MARKERS from the layer
will be shown as floating billboard symbols above the 3d map. (lines,
polygons, and text items from the layer are ignored).

Users have control over the terrain clamping and offset for the
billboards, and whether or not "callout" lines should be shown (vertical
lines joining the billboard to the corresponding location on the
terrain surface)
2025-10-01 10:56:50 +10:00
Nyall Dawson
d63653925c Fix 3d build 2025-10-01 10:54:09 +10:00
Jan Caha
85e00b755b do not prefil htmlmetadata on creation, it can be fetched later when the currentChanged is triggered on mOptStackedWidget 2025-10-01 10:32:00 +10:00
Jan Caha
206cf49e41 use single query to get the information 2025-10-01 10:32:00 +10:00
Jan Caha
5cf505c584 line breaks for html 2025-10-01 10:32:00 +10:00
Jan Caha
a92a318f99 fix condition 2025-10-01 10:32:00 +10:00
Jan Caha
22d6b83351 replace /n with </br> to fix html 2025-10-01 10:32:00 +10:00
Jan Caha
1f7f070325 fix error 2025-10-01 10:32:00 +10:00
Jan Caha
9cddecd8be fix error
Co-authored-by: Stefanos Natsis <uclaros@gmail.com>
2025-10-01 10:32:00 +10:00
Jan Caha
9b58c9d973 add table comment 2025-10-01 10:32:00 +10:00
Jan Caha
6df907a02b review comments 2025-10-01 10:32:00 +10:00
Jan Caha
7108812a7b fix version 2025-10-01 10:32:00 +10:00
Jan Caha
45f0e33759 remove unused variable 2025-10-01 10:32:00 +10:00
Jan Caha
3e06b3d151 fix 2025-10-01 10:32:00 +10:00
Jan Caha
8063554822 remove const 2025-10-01 10:32:00 +10:00
Jan Caha
fb9429fb20 simplify args use 2025-10-01 10:32:00 +10:00
Jan Caha
9d369ed282 translate strings 2025-10-01 10:32:00 +10:00
Jan Caha
621be995f6 rename function 2025-10-01 10:32:00 +10:00
Jan Caha
4d6f204063 add more htmlMetadata to the postgres provider 2025-10-01 10:32:00 +10:00
Jan Caha
36ec7fbcce move dumpVariantMap to qgspostgresutils 2025-10-01 10:32:00 +10:00
Germán Carrillo
88d443ac8f [tests] Add tests for setting extent in 'Export Map to Image/PDF' dialogs, both with normal behavior and with locking the scale activated 2025-10-01 10:20:18 +10:00
Germán Carrillo
656959f5f2 [app] Export Map to image/PDF: Add option to lock the scale while setting the extent
+ Currently, setting the extent using current map view, layer extent, or drawn on canvas buttons will always reset the scale to match canvas scale.
+ This commit makes it possible to use a lock button to keep the scale as is in the dialog when setting the extent by any of the aforementioned methods.
2025-10-01 10:20:18 +10:00
qgis-bot
0dbabdf1e1 auto sipify 🍺 2025-10-01 00:19:43 +00:00
Damiano Lombardi
7a36d05a37 Move enum DevToolsNodeRole to Qgis namespace 2025-10-01 10:17:00 +10:00
Damiano Lombardi
c0a3055b6e Explicitly forbid copy for QgsDevToolsModelGroup 2025-10-01 10:17:00 +10:00
Damiano Lombardi
d553c935c3 Move networklogger to gui 2025-10-01 10:17:00 +10:00
Nyall Dawson
143949770a Update src/gui/processing/qgsprocessingoutputdestinationwidget.cpp 2025-10-01 10:15:12 +10:00
Alexander Bruy
43ae0b6706 change default folder for Processing outputs to $HOME/processing to make
it more visible and accessible
2025-10-01 10:15:12 +10:00
Alexander Bruy
e6746493b9 provide default value when reading output folder setting to generate
destination path in Processing (fix #61965)
2025-10-01 10:15:12 +10:00
Norman Barker
412fb25747 support STAC assets from other cloud optimized data types / blob stores 2025-07-23 18:23:38 -05:00
147 changed files with 3108 additions and 646 deletions

View File

@ -562,6 +562,9 @@ if(WITH_CORE)
if (PDAL_VERSION_MAJOR GREATER 3 OR (PDAL_VERSION_MAJOR EQUAL 2 AND PDAL_VERSION_MINOR GREATER_EQUAL 5))
set(PDAL_2_5_OR_HIGHER TRUE)
endif()
if (PDAL_VERSION_MAJOR GREATER 3 OR (PDAL_VERSION_MAJOR EQUAL 2 AND PDAL_VERSION_MINOR GREATER_EQUAL 9))
set(PDAL_2_9_OR_HIGHER TRUE)
endif()
endif()
if (WITH_DRACO)

View File

@ -6,6 +6,7 @@
%Include auto_generated/qgs3dtypes.sip
%Include auto_generated/qgs3dmapcanvas.sip
%Include auto_generated/qgsabstractvectorlayer3drenderer.sip
%Include auto_generated/qgsannotationlayer3drenderer.sip
%Include auto_generated/qgscameracontroller.sip
%Include auto_generated/qgscamerapose.sip
%Include auto_generated/qgslayoutitem3dmap.sip

View File

@ -0,0 +1,9 @@
# The following has been generated automatically from src/3d/qgsannotationlayer3drenderer.h
try:
QgsAnnotationLayer3DRendererMetadata.__overridden_methods__ = ['createRenderer']
except (NameError, AttributeError):
pass
try:
QgsAnnotationLayer3DRenderer.__overridden_methods__ = ['type', 'clone', 'writeXml', 'readXml', 'resolveReferences']
except (NameError, AttributeError):
pass

View File

@ -0,0 +1,154 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/qgsannotationlayer3drenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/
// this is needed for the "convert to subclass" code below to compile
%ModuleHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
class QgsAnnotationLayer3DRendererMetadata : Qgs3DRendererAbstractMetadata
{
%Docstring(signature="appended")
Metadata for annotation layer 3D renderer to allow creation of its
instances from XML.
.. warning::
This is not considered stable API, and may change in future QGIS releases. It is
exposed to the Python bindings as a tech preview only.
.. versionadded:: 4.0
%End
%TypeHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
public:
QgsAnnotationLayer3DRendererMetadata();
virtual QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) /Factory/;
%Docstring
Creates an instance of a 3D renderer based on a DOM element with
renderer configuration
%End
};
class QgsAnnotationLayer3DRenderer : QgsAbstract3DRenderer
{
%Docstring(signature="appended")
3D renderers for annotation layers.
.. versionadded:: 4.0
%End
%TypeHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
%ConvertToSubClassCode
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
sipType = sipType_QgsAnnotationLayer3DRenderer;
else
sipType = nullptr;
%End
public:
QgsAnnotationLayer3DRenderer();
void setLayer( QgsAnnotationLayer *layer );
%Docstring
Sets the annotation layer associated with the renderer.
.. seealso:: :py:func:`layer`
%End
QgsAnnotationLayer *layer() const;
%Docstring
Returns the annotation layer associated with the renderer.
.. seealso:: :py:func:`setLayer`
%End
virtual QString type() const;
virtual QgsAnnotationLayer3DRenderer *clone() const /Factory/;
virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
virtual void resolveReferences( const QgsProject &project );
Qgis::AltitudeClamping altitudeClamping() const;
%Docstring
Returns the altitude clamping method, which determines the vertical
position of annotations.
.. seealso:: :py:func:`setAltitudeClamping`
%End
void setAltitudeClamping( Qgis::AltitudeClamping clamping );
%Docstring
Sets the altitude ``clamping`` method, which determines the vertical
position of annotations.
.. seealso:: :py:func:`altitudeClamping`
%End
double zOffset() const;
%Docstring
Returns the z offset, which is a fixed offset amount which should be
added to z values for the annotations.
.. seealso:: :py:func:`setZOffset`
%End
void setZOffset( double offset );
%Docstring
Sets the z ``offset``, which is a fixed offset amount which will be
added to z values for the annotations.
.. seealso:: :py:func:`zOffset`
%End
bool showCalloutLines() const;
%Docstring
Returns ``True`` if callout lines are shown, vertically joining the
annotations to the terrain.
.. seealso:: :py:func:`setShowCalloutLines`
%End
void setShowCalloutLines( bool show );
%Docstring
Sets whether callout lines are shown, vertically joining the annotations
to the terrain.
.. seealso:: :py:func:`showCalloutLines`
%End
private:
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/qgsannotationlayer3drenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/

View File

@ -246,6 +246,22 @@ QgsAbstractVectorLayer3DRenderer.setTilingSettings: src/3d/qgsabstractvectorlaye
QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89
QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97
QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76
QgsAnnotationLayer3DRenderer.altitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L100
QgsAnnotationLayer3DRenderer.clone: src/3d/qgsannotationlayer3drenderer.h#L88
QgsAnnotationLayer3DRenderer.layer: src/3d/qgsannotationlayer3drenderer.h#L85
QgsAnnotationLayer3DRenderer.readXml: src/3d/qgsannotationlayer3drenderer.h#L92
QgsAnnotationLayer3DRenderer.resolveReferences: src/3d/qgsannotationlayer3drenderer.h#L93
QgsAnnotationLayer3DRenderer.setAltitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L107
QgsAnnotationLayer3DRenderer.setLayer: src/3d/qgsannotationlayer3drenderer.h#L78
QgsAnnotationLayer3DRenderer.setShowCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L135
QgsAnnotationLayer3DRenderer.setZOffset: src/3d/qgsannotationlayer3drenderer.h#L121
QgsAnnotationLayer3DRenderer.showCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L128
QgsAnnotationLayer3DRenderer.type: src/3d/qgsannotationlayer3drenderer.h#L87
QgsAnnotationLayer3DRenderer.writeXml: src/3d/qgsannotationlayer3drenderer.h#L91
QgsAnnotationLayer3DRenderer.zOffset: src/3d/qgsannotationlayer3drenderer.h#L114
QgsAnnotationLayer3DRenderer: src/3d/qgsannotationlayer3drenderer.h#L59
QgsAnnotationLayer3DRendererMetadata.createRenderer: src/3d/qgsannotationlayer3drenderer.h#L50
QgsAnnotationLayer3DRendererMetadata: src/3d/qgsannotationlayer3drenderer.h#L44
QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360
QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89
QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368

View File

@ -262,7 +262,7 @@ if (WITH_3D)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/3d/project.py.in ${CMAKE_CURRENT_BINARY_DIR}/3d/project.py @ONLY)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml.temp" "${toml}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml.temp ${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml)
SET(SIP_CONCAT_PARTS 10) # 3D doesn't have enough .sip files to fill 15+ .cpp files
SET(SIP_CONCAT_PARTS 8) # 3D doesn't have enough .sip files to fill 15+ .cpp files
GENERATE_SIP_PYTHON_MODULE_CODE(qgis._3d_p 3d/3d.sip "${sip_files_3d}" cpp_files)
BUILD_SIP_PYTHON_MODULE(qgis._3d_p 3d/3d.sip ${cpp_files} "" qgis_core qgis_3d)
endif()

View File

@ -6,6 +6,7 @@
%Include auto_generated/qgs3dtypes.sip
%Include auto_generated/qgs3dmapcanvas.sip
%Include auto_generated/qgsabstractvectorlayer3drenderer.sip
%Include auto_generated/qgsannotationlayer3drenderer.sip
%Include auto_generated/qgscameracontroller.sip
%Include auto_generated/qgscamerapose.sip
%Include auto_generated/qgslayoutitem3dmap.sip

View File

@ -0,0 +1,9 @@
# The following has been generated automatically from src/3d/qgsannotationlayer3drenderer.h
try:
QgsAnnotationLayer3DRendererMetadata.__overridden_methods__ = ['createRenderer']
except (NameError, AttributeError):
pass
try:
QgsAnnotationLayer3DRenderer.__overridden_methods__ = ['type', 'clone', 'writeXml', 'readXml', 'resolveReferences']
except (NameError, AttributeError):
pass

View File

@ -0,0 +1,154 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/qgsannotationlayer3drenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/
// this is needed for the "convert to subclass" code below to compile
%ModuleHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
class QgsAnnotationLayer3DRendererMetadata : Qgs3DRendererAbstractMetadata
{
%Docstring(signature="appended")
Metadata for annotation layer 3D renderer to allow creation of its
instances from XML.
.. warning::
This is not considered stable API, and may change in future QGIS releases. It is
exposed to the Python bindings as a tech preview only.
.. versionadded:: 4.0
%End
%TypeHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
public:
QgsAnnotationLayer3DRendererMetadata();
virtual QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) /Factory/;
%Docstring
Creates an instance of a 3D renderer based on a DOM element with
renderer configuration
%End
};
class QgsAnnotationLayer3DRenderer : QgsAbstract3DRenderer
{
%Docstring(signature="appended")
3D renderers for annotation layers.
.. versionadded:: 4.0
%End
%TypeHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
%ConvertToSubClassCode
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
sipType = sipType_QgsAnnotationLayer3DRenderer;
else
sipType = nullptr;
%End
public:
QgsAnnotationLayer3DRenderer();
void setLayer( QgsAnnotationLayer *layer );
%Docstring
Sets the annotation layer associated with the renderer.
.. seealso:: :py:func:`layer`
%End
QgsAnnotationLayer *layer() const;
%Docstring
Returns the annotation layer associated with the renderer.
.. seealso:: :py:func:`setLayer`
%End
virtual QString type() const;
virtual QgsAnnotationLayer3DRenderer *clone() const /Factory/;
virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
virtual void resolveReferences( const QgsProject &project );
Qgis::AltitudeClamping altitudeClamping() const;
%Docstring
Returns the altitude clamping method, which determines the vertical
position of annotations.
.. seealso:: :py:func:`setAltitudeClamping`
%End
void setAltitudeClamping( Qgis::AltitudeClamping clamping );
%Docstring
Sets the altitude ``clamping`` method, which determines the vertical
position of annotations.
.. seealso:: :py:func:`altitudeClamping`
%End
double zOffset() const;
%Docstring
Returns the z offset, which is a fixed offset amount which should be
added to z values for the annotations.
.. seealso:: :py:func:`setZOffset`
%End
void setZOffset( double offset );
%Docstring
Sets the z ``offset``, which is a fixed offset amount which will be
added to z values for the annotations.
.. seealso:: :py:func:`zOffset`
%End
bool showCalloutLines() const;
%Docstring
Returns ``True`` if callout lines are shown, vertically joining the
annotations to the terrain.
.. seealso:: :py:func:`setShowCalloutLines`
%End
void setShowCalloutLines( bool show );
%Docstring
Sets whether callout lines are shown, vertically joining the annotations
to the terrain.
.. seealso:: :py:func:`showCalloutLines`
%End
private:
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/qgsannotationlayer3drenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/

View File

@ -246,6 +246,22 @@ QgsAbstractVectorLayer3DRenderer.setTilingSettings: src/3d/qgsabstractvectorlaye
QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89
QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97
QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76
QgsAnnotationLayer3DRenderer.altitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L100
QgsAnnotationLayer3DRenderer.clone: src/3d/qgsannotationlayer3drenderer.h#L88
QgsAnnotationLayer3DRenderer.layer: src/3d/qgsannotationlayer3drenderer.h#L85
QgsAnnotationLayer3DRenderer.readXml: src/3d/qgsannotationlayer3drenderer.h#L92
QgsAnnotationLayer3DRenderer.resolveReferences: src/3d/qgsannotationlayer3drenderer.h#L93
QgsAnnotationLayer3DRenderer.setAltitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L107
QgsAnnotationLayer3DRenderer.setLayer: src/3d/qgsannotationlayer3drenderer.h#L78
QgsAnnotationLayer3DRenderer.setShowCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L135
QgsAnnotationLayer3DRenderer.setZOffset: src/3d/qgsannotationlayer3drenderer.h#L121
QgsAnnotationLayer3DRenderer.showCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L128
QgsAnnotationLayer3DRenderer.type: src/3d/qgsannotationlayer3drenderer.h#L87
QgsAnnotationLayer3DRenderer.writeXml: src/3d/qgsannotationlayer3drenderer.h#L91
QgsAnnotationLayer3DRenderer.zOffset: src/3d/qgsannotationlayer3drenderer.h#L114
QgsAnnotationLayer3DRenderer: src/3d/qgsannotationlayer3drenderer.h#L59
QgsAnnotationLayer3DRendererMetadata.createRenderer: src/3d/qgsannotationlayer3drenderer.h#L50
QgsAnnotationLayer3DRendererMetadata: src/3d/qgsannotationlayer3drenderer.h#L44
QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360
QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89
QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368

View File

@ -11742,6 +11742,25 @@ Qgis.RasterProcessingParameterCapability.baseClass = Qgis
Qgis.RasterProcessingParameterCapabilities = lambda flags=0: Qgis.RasterProcessingParameterCapability(flags)
Qgis.RasterProcessingParameterCapabilities.baseClass = Qgis
RasterProcessingParameterCapabilities = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.DevToolsNodeRole.Status.__doc__ = "Request status role"
Qgis.DevToolsNodeRole.Id.__doc__ = "Request ID role"
Qgis.DevToolsNodeRole.ElapsedTime.__doc__ = "Elapsed time"
Qgis.DevToolsNodeRole.MaximumTime.__doc__ = "Maximum encountered elapsed time"
Qgis.DevToolsNodeRole.Sort.__doc__ = "Sort order role"
Qgis.DevToolsNodeRole.__doc__ = """Dev tools node custom data roles.
.. versionadded:: 4.0
* ``Status``: Request status role
* ``Id``: Request ID role
* ``ElapsedTime``: Elapsed time
* ``MaximumTime``: Maximum encountered elapsed time
* ``Sort``: Sort order role
"""
# --
Qgis.DevToolsNodeRole.baseClass = Qgis
try:
Qgis.__attribute_docs__ = {'QGIS_DEV_VERSION': 'The development version', 'DEFAULT_SEARCH_RADIUS_MM': 'Identify search radius in mm', 'DEFAULT_MAPTOPIXEL_THRESHOLD': 'Default threshold between map coordinates and device coordinates for map2pixel simplification', 'DEFAULT_HIGHLIGHT_COLOR': 'Default highlight color. The transparency is expected to only be applied to polygon\nfill. Lines and outlines are rendered opaque.', 'DEFAULT_HIGHLIGHT_BUFFER_MM': 'Default highlight buffer in mm.', 'DEFAULT_HIGHLIGHT_MIN_WIDTH_MM': 'Default highlight line/stroke minimum width in mm.', 'SCALE_PRECISION': 'Fudge factor used to compare two scales. The code is often going from scale to scale\ndenominator. So it looses precision and, when a limit is inclusive, can lead to errors.\nTo avoid that, use this factor instead of using <= or >=.\n\n.. deprecated:: 3.40\n\n No longer used by QGIS and will be removed in QGIS 4.0.', 'DEFAULT_Z_COORDINATE': 'Default Z coordinate value.\nThis value have to be assigned to the Z coordinate for the vertex.', 'DEFAULT_M_COORDINATE': 'Default M coordinate value.\nThis value have to be assigned to the M coordinate for the vertex.\n\n.. versionadded:: 3.20', 'UI_SCALE_FACTOR': 'UI scaling factor. This should be applied to all widget sizes obtained from font metrics,\nto account for differences in the default font sizes across different platforms.', 'DEFAULT_SNAP_TOLERANCE': 'Default snapping distance tolerance.', 'DEFAULT_SNAP_UNITS': 'Default snapping distance units.', 'USER_CRS_START_ID': 'Minimum ID number for a user-defined projection.', 'DEFAULT_POINT_SIZE': 'The default size (in millimeters) for point marker symbols', 'DEFAULT_LINE_WIDTH': 'The default width (in millimeters) for line symbols', 'DEFAULT_SEGMENT_EPSILON': 'Default snapping tolerance for segments'}
Qgis.__annotations__ = {'QGIS_DEV_VERSION': str, 'DEFAULT_SEARCH_RADIUS_MM': float, 'DEFAULT_MAPTOPIXEL_THRESHOLD': float, 'DEFAULT_HIGHLIGHT_COLOR': 'QColor', 'DEFAULT_HIGHLIGHT_BUFFER_MM': float, 'DEFAULT_HIGHLIGHT_MIN_WIDTH_MM': float, 'SCALE_PRECISION': float, 'DEFAULT_Z_COORDINATE': float, 'DEFAULT_M_COORDINATE': float, 'UI_SCALE_FACTOR': float, 'DEFAULT_SNAP_TOLERANCE': float, 'DEFAULT_SNAP_UNITS': 'Qgis.MapToolUnit', 'USER_CRS_START_ID': int, 'DEFAULT_POINT_SIZE': float, 'DEFAULT_LINE_WIDTH': float, 'DEFAULT_SEGMENT_EPSILON': float}

View File

@ -3446,6 +3446,15 @@ The development version
typedef QFlags<Qgis::RasterProcessingParameterCapability> RasterProcessingParameterCapabilities;
enum class DevToolsNodeRole
{
Status,
Id,
ElapsedTime,
MaximumTime,
Sort,
};
static const double DEFAULT_SEARCH_RADIUS_MM;
static const float DEFAULT_MAPTOPIXEL_THRESHOLD;

View File

@ -1,15 +1,15 @@
Qgis.defaultProjectScales: src/core/qgis.h#L6171
Qgis.defaultProjectScales: src/core/qgis.h#L6185
Qgis.devVersion: src/core/qgis.h#L90
Qgis.geoNone: src/core/qgis.h#L6223
Qgis.geoProj4: src/core/qgis.h#L6253
Qgis.geoWkt: src/core/qgis.h#L6244
Qgis.geographicCrsAuthId: src/core/qgis.h#L6233
Qgis.geosVersion: src/core/qgis.h#L6206
Qgis.geosVersionInt: src/core/qgis.h#L6178
Qgis.geosVersionMajor: src/core/qgis.h#L6185
Qgis.geosVersionMinor: src/core/qgis.h#L6192
Qgis.geosVersionPatch: src/core/qgis.h#L6199
Qgis.hasQtWebkit: src/core/qgis.h#L6213
Qgis.geoNone: src/core/qgis.h#L6237
Qgis.geoProj4: src/core/qgis.h#L6267
Qgis.geoWkt: src/core/qgis.h#L6258
Qgis.geographicCrsAuthId: src/core/qgis.h#L6247
Qgis.geosVersion: src/core/qgis.h#L6220
Qgis.geosVersionInt: src/core/qgis.h#L6192
Qgis.geosVersionMajor: src/core/qgis.h#L6199
Qgis.geosVersionMinor: src/core/qgis.h#L6206
Qgis.geosVersionPatch: src/core/qgis.h#L6213
Qgis.hasQtWebkit: src/core/qgis.h#L6227
Qgis.releaseName: src/core/qgis.h#L80
Qgis.version: src/core/qgis.h#L66
Qgis.versionInt: src/core/qgis.h#L73

View File

@ -0,0 +1,18 @@
# The following has been generated automatically from src/gui/qgsshortcutsmanager.h
# monkey patching scoped based enum
QgsShortcutsManager.CommonAction.CodeToggleComment.__doc__ = "Toggle code comments"
QgsShortcutsManager.CommonAction.CodeReformat.__doc__ = "Reformat code"
QgsShortcutsManager.CommonAction.CodeRunScript.__doc__ = "Run script"
QgsShortcutsManager.CommonAction.CodeRunSelection.__doc__ = "Run selection from script"
QgsShortcutsManager.CommonAction.__doc__ = """Contains common actions which are used across a variety of classes.
.. versionadded:: 4.0
* ``CodeToggleComment``: Toggle code comments
* ``CodeReformat``: Reformat code
* ``CodeRunScript``: Run script
* ``CodeRunSelection``: Run selection from script
"""
# --
QgsShortcutsManager.CommonAction.baseClass = QgsShortcutsManager

View File

@ -23,6 +23,14 @@ rather accessed through :py:func:`QgsGui.shortcutsManager()`.
#include "qgsshortcutsmanager.h"
%End
public:
enum class CommonAction
{
CodeToggleComment,
CodeReformat,
CodeRunScript,
CodeRunSelection,
};
QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" );
%Docstring
Constructor for QgsShortcutsManager.
@ -36,6 +44,8 @@ Constructor for QgsShortcutsManager.
QGIS actions.
%End
~QgsShortcutsManager();
void registerAllChildren( QObject *object, bool recursive = false, const QString &section = QString() );
%Docstring
Automatically registers all QActions and QShortcuts which are children
@ -105,6 +115,16 @@ in GUI.
.. seealso:: :py:func:`unregisterAction`
.. seealso:: :py:func:`registerAllChildActions`
%End
void initializeCommonAction( QAction *action, CommonAction commonAction );
%Docstring
Initializes an ``action`` as a common action.
This automatically configures the ``action`` to use the properties for
the common action, such as setting the action's tooltip and shortcut.
.. versionadded:: 4.0
%End
bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString &section = QString() );
@ -290,6 +310,14 @@ if no shortcut is associated.
.. seealso:: :py:func:`objectForSequence`
.. seealso:: :py:func:`actionForSequence`
%End
QKeySequence sequenceForCommonAction( CommonAction action ) const;
%Docstring
Returns the key sequence which is associated with a common ``action``,
or an empty sequence if no shortcut is assigned to that action.
.. versionadded:: 4.0
%End
QAction *actionByName( const QString &name ) const;

View File

@ -6977,28 +6977,30 @@ QgsShapeburstFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwid
QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426
QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427
QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L236
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L221
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L163
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L171
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L155
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L213
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L262
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L254
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L94
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L70
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L81
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L59
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L106
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L180
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L196
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L204
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L188
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L246
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L243
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L229
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L116
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L126
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L267
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L246
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L188
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L196
QgsShortcutsManager.initializeCommonAction: src/gui/qgsshortcutsmanager.h#L119
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L180
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L238
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L293
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L285
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L109
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L85
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L96
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L74
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L131
QgsShortcutsManager.sequenceForCommonAction: src/gui/qgsshortcutsmanager.h#L260
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L205
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L221
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L229
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L213
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L277
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L274
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L254
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L141
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L151
QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36
QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264
QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271

View File

@ -19,5 +19,4 @@ __author__ = "Salvatore Larosa"
__date__ = "September 2012"
__copyright__ = "(C) 2012, Salvatore Larosa"
from .console import show_console # NOQA
from .console import init_options_widget
from .console import show_console, init_console # NOQA

View File

@ -61,6 +61,7 @@ from qgis.gui import (
QgsGui,
QgsApplicationExitBlockerInterface,
QgsCodeEditorDockWidget,
QgsShortcutsManager,
)
from functools import partial
@ -106,8 +107,11 @@ def console_displayhook(obj):
_console_output = obj
def init_options_widget():
"""called from QGIS to add the console options widget"""
def init_console():
"""
Called from QGIS to initialize the console related options and shortcuts,
before the dock is shown
"""
global _options_factory
_options_factory.setTitle(QCoreApplication.translate("PythonConsole", "Python"))
iface.registerOptionsWidgetFactory(_options_factory)
@ -221,169 +225,137 @@ class PythonConsoleWidget(QWidget):
# Action for Open File
openFileBt = QCoreApplication.translate("PythonConsole", "Open Script…")
self.openFileButton = QAction(self)
self.openFileButton.setCheckable(False)
self.openFileButton.setEnabled(True)
self.openFileButton.setIcon(
self.open_file_action = QAction(self)
self.open_file_action.setIcon(
QgsApplication.getThemeIcon("mActionScriptOpen.svg")
)
self.openFileButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.openFileButton.setIconVisibleInMenu(True)
self.openFileButton.setToolTip(openFileBt + " <b>Ctrl+O</b>")
self.openFileButton.setText(openFileBt)
self.open_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.open_file_action.setIconVisibleInMenu(True)
self.open_file_action.setToolTip(f"<b>{openFileBt}</b> (Ctrl+O)")
self.open_file_action.setText(openFileBt)
openExtEditorBt = QCoreApplication.translate(
"PythonConsole", "Open in External Editor"
)
self.openInEditorButton = QAction(self)
self.openInEditorButton.setCheckable(False)
self.openInEditorButton.setEnabled(True)
self.openInEditorButton.setIcon(
self.open_in_editor_action = QAction(self)
self.open_in_editor_action.setIcon(
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
)
self.openInEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.openInEditorButton.setIconVisibleInMenu(True)
self.openInEditorButton.setToolTip(openExtEditorBt)
self.openInEditorButton.setText(openExtEditorBt)
self.open_in_editor_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.open_in_editor_action.setIconVisibleInMenu(True)
self.open_in_editor_action.setToolTip(openExtEditorBt)
self.open_in_editor_action.setText(openExtEditorBt)
# Action for Save File
saveFileBt = QCoreApplication.translate("PythonConsole", "Save")
self.saveFileButton = QAction(self)
self.saveFileButton.setCheckable(False)
self.saveFileButton.setEnabled(False)
self.saveFileButton.setIcon(QgsApplication.getThemeIcon("mActionFileSave.svg"))
self.saveFileButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.saveFileButton.setIconVisibleInMenu(True)
self.saveFileButton.setToolTip(saveFileBt + " <b>Ctrl+S</b>")
self.saveFileButton.setText(saveFileBt)
self.save_file_action = QAction(self)
self.save_file_action.setEnabled(False)
self.save_file_action.setIcon(
QgsApplication.getThemeIcon("mActionFileSave.svg")
)
self.save_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.save_file_action.setIconVisibleInMenu(True)
self.save_file_action.setToolTip(f"<b>{saveFileBt}</b> (Ctrl+S)")
self.save_file_action.setText(saveFileBt)
# Action for Save File As
saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…")
self.saveAsFileButton = QAction(self)
self.saveAsFileButton.setCheckable(False)
self.saveAsFileButton.setEnabled(True)
self.saveAsFileButton.setIcon(
self.save_as_file_action = QAction(self)
self.save_as_file_action.setIcon(
QgsApplication.getThemeIcon("mActionFileSaveAs.svg")
)
self.saveAsFileButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.saveAsFileButton.setIconVisibleInMenu(True)
self.saveAsFileButton.setToolTip(saveAsFileBt + " <b>Ctrl+Shift+S</b>")
self.saveAsFileButton.setText(saveAsFileBt)
self.save_as_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.save_as_file_action.setIconVisibleInMenu(True)
self.save_as_file_action.setToolTip(f"<b>{saveAsFileBt}</b> (Ctrl+Shift+S)")
self.save_as_file_action.setText(saveAsFileBt)
# Action Cut
cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut")
self.cutEditorButton = QAction(self)
self.cutEditorButton.setCheckable(False)
self.cutEditorButton.setEnabled(True)
self.cutEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg"))
self.cutEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.cutEditorButton.setIconVisibleInMenu(True)
self.cutEditorButton.setToolTip(cutEditorBt + " <b>Ctrl+X</b>")
self.cutEditorButton.setText(cutEditorBt)
self.cut_action = QAction(self)
self.cut_action.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg"))
self.cut_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.cut_action.setIconVisibleInMenu(True)
self.cut_action.setToolTip(f"<b>{cutEditorBt}</b> (Ctrl+X)")
self.cut_action.setText(cutEditorBt)
# Action Copy
copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy")
self.copyEditorButton = QAction(self)
self.copyEditorButton.setCheckable(False)
self.copyEditorButton.setEnabled(True)
self.copyEditorButton.setIcon(
QgsApplication.getThemeIcon("mActionEditCopy.svg")
)
self.copyEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.copyEditorButton.setIconVisibleInMenu(True)
self.copyEditorButton.setToolTip(copyEditorBt + " <b>Ctrl+C</b>")
self.copyEditorButton.setText(copyEditorBt)
self.copy_action = QAction(self)
self.copy_action.setIcon(QgsApplication.getThemeIcon("mActionEditCopy.svg"))
self.copy_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.copy_action.setIconVisibleInMenu(True)
self.copy_action.setToolTip(f"<b>{copyEditorBt}</b> (Ctrl+C)")
self.copy_action.setText(copyEditorBt)
# Action Paste
pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste")
self.pasteEditorButton = QAction(self)
self.pasteEditorButton.setCheckable(False)
self.pasteEditorButton.setEnabled(True)
self.pasteEditorButton.setIcon(
QgsApplication.getThemeIcon("mActionEditPaste.svg")
)
self.pasteEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.pasteEditorButton.setIconVisibleInMenu(True)
self.pasteEditorButton.setToolTip(pasteEditorBt + " <b>Ctrl+V</b>")
self.pasteEditorButton.setText(pasteEditorBt)
self.paste_action = QAction(self)
self.paste_action.setIcon(QgsApplication.getThemeIcon("mActionEditPaste.svg"))
self.paste_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.paste_action.setIconVisibleInMenu(True)
self.paste_action.setToolTip(f"<b>{pasteEditorBt}</b> (Ctrl+V)")
self.paste_action.setText(pasteEditorBt)
# Action Run Script (subprocess)
runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run Script")
self.runScriptEditorButton = QAction(self)
self.runScriptEditorButton.setCheckable(False)
self.runScriptEditorButton.setEnabled(True)
self.runScriptEditorButton.setIcon(
QgsApplication.getThemeIcon("mActionStart.svg")
self.run_script_action = QAction(self)
self.run_script_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
self.run_script_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.run_script_action.setIconVisibleInMenu(True)
QgsGui.shortcutsManager().initializeCommonAction(
self.run_script_action, QgsShortcutsManager.CommonAction.CodeRunScript
)
self.runScriptEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.runScriptEditorButton.setIconVisibleInMenu(True)
self.runScriptEditorButton.setToolTip(
runScriptEditorBt + " <b>Ctrl+Shift+E</b>"
)
self.runScriptEditorButton.setText(runScriptEditorBt)
# Action Run Selected
runSelectedEditorBt = QCoreApplication.translate(
"PythonConsole", "Run Selected"
)
self.runSelectedEditorButton = QAction(self)
self.runSelectedEditorButton.setCheckable(False)
self.runSelectedEditorButton.setEnabled(True)
self.runSelectedEditorButton.setIcon(
self.run_selection_action = QAction(self)
self.run_selection_action.setIcon(
QgsApplication.getThemeIcon("mActionRunSelected.svg")
)
self.runSelectedEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.runSelectedEditorButton.setIconVisibleInMenu(True)
self.runSelectedEditorButton.setToolTip(runSelectedEditorBt + " <b>Ctrl+E</b>")
self.runSelectedEditorButton.setShortcut("Ctrl+E")
self.runSelectedEditorButton.setText(runSelectedEditorBt)
self.run_selection_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.run_selection_action.setIconVisibleInMenu(True)
QgsGui.shortcutsManager().initializeCommonAction(
self.run_selection_action, QgsShortcutsManager.CommonAction.CodeRunSelection
)
# Action Toggle comment
toggleText = QCoreApplication.translate("PythonConsole", "Toggle Comment")
self.toggleCommentEditorButton = QAction(self)
self.toggleCommentEditorButton.setCheckable(False)
self.toggleCommentEditorButton.setEnabled(True)
self.toggleCommentEditorButton.setIcon(
self.toggle_comment_action = QAction(self)
self.toggle_comment_action.setIcon(
QgsApplication.getThemeIcon(
"console/iconCommentEditorConsole.svg",
self.palette().color(QPalette.ColorRole.WindowText),
),
)
self.toggleCommentEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.toggleCommentEditorButton.setIconVisibleInMenu(True)
self.toggleCommentEditorButton.setToolTip(toggleText + " <b>Ctrl+:</b>")
self.toggleCommentEditorButton.setText(toggleText)
self.toggle_comment_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.toggle_comment_action.setIconVisibleInMenu(True)
QgsGui.shortcutsManager().initializeCommonAction(
self.toggle_comment_action,
QgsShortcutsManager.CommonAction.CodeToggleComment,
)
# Action Format code
reformatCodeText = QCoreApplication.translate("PythonConsole", "Reformat Code")
self.reformatCodeEditorButton = QAction(self)
self.reformatCodeEditorButton.setCheckable(False)
self.reformatCodeEditorButton.setEnabled(True)
self.reformatCodeEditorButton.setIcon(
self.reformat_code_action = QAction(self)
self.reformat_code_action.setIcon(
QgsApplication.getThemeIcon("console/iconFormatCode.svg")
)
self.reformatCodeEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.reformatCodeEditorButton.setIconVisibleInMenu(True)
self.reformatCodeEditorButton.setToolTip(
reformatCodeText + " <b>Ctrl+Alt+F</b>"
self.reformat_code_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.reformat_code_action.setIconVisibleInMenu(True)
QgsGui.shortcutsManager().initializeCommonAction(
self.reformat_code_action, QgsShortcutsManager.CommonAction.CodeReformat
)
self.reformatCodeEditorButton.setShortcut("Ctrl+Alt+F")
self.reformatCodeEditorButton.setText(reformatCodeText)
# Action for Object browser
objList = QCoreApplication.translate("PythonConsole", "Object Inspector…")
self.objectListButton = QAction(self)
self.objectListButton.setCheckable(True)
self.objectListButton.setEnabled(
self.object_inspector_action = QAction(self)
self.object_inspector_action.setCheckable(True)
self.object_inspector_action.setEnabled(
QgsSettings().value("pythonConsole/enableObjectInsp", False, type=bool)
)
self.objectListButton.setIcon(
self.object_inspector_action.setIcon(
QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg")
)
self.objectListButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.objectListButton.setIconVisibleInMenu(True)
self.objectListButton.setToolTip(objList)
self.objectListButton.setText(objList)
self.object_inspector_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.object_inspector_action.setIconVisibleInMenu(True)
self.object_inspector_action.setToolTip(objList)
self.object_inspector_action.setText(objList)
# Action for Find text
findText = QCoreApplication.translate("PythonConsole", "Find Text")
@ -395,7 +367,7 @@ class PythonConsoleWidget(QWidget):
)
self.find_text_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.find_text_action.setIconVisibleInMenu(True)
self.find_text_action.setToolTip(findText + " <b>Ctrl+F</b>")
self.find_text_action.setToolTip(f"<b>{findText}</b> (Ctrl+F)")
self.find_text_action.setText(findText)
self.tabEditorWidget.search_bar_toggled.connect(
@ -407,75 +379,65 @@ class PythonConsoleWidget(QWidget):
# Action Show Editor
showEditor = QCoreApplication.translate("PythonConsole", "Show Editor")
self.showEditorButton = QAction(self)
self.showEditorButton.setEnabled(True)
self.showEditorButton.setCheckable(True)
self.showEditorButton.setIcon(
self.show_editor_action = QAction(self)
self.show_editor_action.setCheckable(True)
self.show_editor_action.setIcon(
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
)
self.showEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.showEditorButton.setIconVisibleInMenu(True)
self.showEditorButton.setToolTip(showEditor)
self.showEditorButton.setText(showEditor)
self.show_editor_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.show_editor_action.setIconVisibleInMenu(True)
self.show_editor_action.setToolTip(showEditor)
self.show_editor_action.setText(showEditor)
# Action for Clear button
clearBt = QCoreApplication.translate("PythonConsole", "Clear Console")
self.clearButton = QAction(self)
self.clearButton.setCheckable(False)
self.clearButton.setEnabled(True)
self.clearButton.setIcon(
self.clear_action = QAction(self)
self.clear_action.setIcon(
QgsApplication.getThemeIcon("console/iconClearConsole.svg")
)
self.clearButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.clearButton.setIconVisibleInMenu(True)
self.clearButton.setToolTip(clearBt)
self.clearButton.setText(clearBt)
self.clear_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.clear_action.setIconVisibleInMenu(True)
self.clear_action.setToolTip(clearBt)
self.clear_action.setText(clearBt)
# Action for settings
optionsBt = QCoreApplication.translate("PythonConsole", "Options…")
self.optionsButton = QAction(self)
self.optionsButton.setCheckable(False)
self.optionsButton.setEnabled(True)
self.optionsButton.setIcon(
self.options_action = QAction(self)
self.options_action.setIcon(
QgsApplication.getThemeIcon("console/iconSettingsConsole.svg")
)
self.optionsButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.optionsButton.setIconVisibleInMenu(True)
self.optionsButton.setToolTip(optionsBt)
self.optionsButton.setText(optionsBt)
self.options_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.options_action.setIconVisibleInMenu(True)
self.options_action.setToolTip(optionsBt)
self.options_action.setText(optionsBt)
# Action for Run script
runBt = QCoreApplication.translate("PythonConsole", "Run Command")
self.runButton = QAction(self)
self.runButton.setCheckable(False)
self.runButton.setEnabled(True)
self.runButton.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
self.runButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.runButton.setIconVisibleInMenu(True)
self.runButton.setToolTip(runBt)
self.runButton.setText(runBt)
self.run_action = QAction(self)
self.run_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
self.run_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.run_action.setIconVisibleInMenu(True)
self.run_action.setToolTip(runBt)
self.run_action.setText(runBt)
# Help button
self.helpConsoleAction = QAction(self)
self.helpConsoleAction.setEnabled(True)
self.helpConsoleAction.setText(
self.help_console_action = QAction(self)
self.help_console_action.setText(
QCoreApplication.translate("PythonConsole", "Python Console Help")
)
self.helpAPIAction = QAction(self)
self.helpAPIAction.setEnabled(True)
self.helpAPIAction.setText(
self.help_api_action = QAction(self)
self.help_api_action.setText(
QCoreApplication.translate("PythonConsole", "PyQGIS API Documentation")
)
self.helpCookbookAction = QAction(self)
self.helpCookbookAction.setEnabled(True)
self.helpCookbookAction.setText(
self.help_cookbook_action = QAction(self)
self.help_cookbook_action.setText(
QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook")
)
self.helpMenu = QMenu(self)
self.helpMenu.addAction(self.helpConsoleAction)
self.helpMenu.addAction(self.helpAPIAction)
self.helpMenu.addAction(self.helpCookbookAction)
self.helpMenu.addAction(self.help_console_action)
self.helpMenu.addAction(self.help_api_action)
self.helpMenu.addAction(self.help_cookbook_action)
helpBt = QCoreApplication.translate("PythonConsole", "Help…")
self.helpButton = QToolButton(self)
@ -495,12 +457,12 @@ class PythonConsoleWidget(QWidget):
self.toolBar.setIconSize(icon_size)
self.toolBar.setMovable(False)
self.toolBar.setFloatable(False)
self.toolBar.addAction(self.clearButton)
self.toolBar.addAction(self.runButton)
self.toolBar.addAction(self.clear_action)
self.toolBar.addAction(self.run_action)
self.toolBar.addSeparator()
self.toolBar.addAction(self.showEditorButton)
self.toolBar.addAction(self.show_editor_action)
self.toolBar.addSeparator()
self.toolBar.addAction(self.optionsButton)
self.toolBar.addAction(self.options_action)
self.toolBar.addWidget(self.helpButton)
self.toolBar.addSeparator()
self.toolBar.addWidget(parent.dockToggleButton())
@ -513,25 +475,25 @@ class PythonConsoleWidget(QWidget):
self.toolBarEditor.setIconSize(icon_size)
self.toolBarEditor.setMovable(False)
self.toolBarEditor.setFloatable(False)
self.toolBarEditor.addAction(self.openFileButton)
self.toolBarEditor.addAction(self.openInEditorButton)
self.toolBarEditor.addAction(self.open_file_action)
self.toolBarEditor.addAction(self.open_in_editor_action)
self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.saveFileButton)
self.toolBarEditor.addAction(self.saveAsFileButton)
self.toolBarEditor.addAction(self.save_file_action)
self.toolBarEditor.addAction(self.save_as_file_action)
self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.runScriptEditorButton)
self.toolBarEditor.addAction(self.runSelectedEditorButton)
self.toolBarEditor.addAction(self.run_script_action)
self.toolBarEditor.addAction(self.run_selection_action)
self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.cutEditorButton)
self.toolBarEditor.addAction(self.copyEditorButton)
self.toolBarEditor.addAction(self.pasteEditorButton)
self.toolBarEditor.addAction(self.cut_action)
self.toolBarEditor.addAction(self.copy_action)
self.toolBarEditor.addAction(self.paste_action)
self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.find_text_action)
self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.toggleCommentEditorButton)
self.toolBarEditor.addAction(self.reformatCodeEditorButton)
self.toolBarEditor.addAction(self.toggle_comment_action)
self.toolBarEditor.addAction(self.reformat_code_action)
self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.objectListButton)
self.toolBarEditor.addAction(self.object_inspector_action)
self.widgetButton = QWidget()
sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
@ -585,25 +547,25 @@ class PythonConsoleWidget(QWidget):
# ------------ Signal -------------------------------
self.objectListButton.toggled.connect(self.toggleObjectListWidget)
self.toggleCommentEditorButton.triggered.connect(self.toggleComment)
self.reformatCodeEditorButton.triggered.connect(self.reformatCode)
self.runScriptEditorButton.triggered.connect(self.runScriptEditor)
self.runSelectedEditorButton.triggered.connect(self.runSelectedEditor)
self.cutEditorButton.triggered.connect(self.cutEditor)
self.copyEditorButton.triggered.connect(self.copyEditor)
self.pasteEditorButton.triggered.connect(self.pasteEditor)
self.showEditorButton.toggled.connect(self.toggleEditor)
self.clearButton.triggered.connect(self.shell_output.clearConsole)
self.optionsButton.triggered.connect(self.openSettings)
self.runButton.triggered.connect(self.shell.entered)
self.openFileButton.triggered.connect(self.openScriptFile)
self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor)
self.saveFileButton.triggered.connect(self.saveScriptFile)
self.saveAsFileButton.triggered.connect(self.saveAsScriptFile)
self.helpConsoleAction.triggered.connect(self.openHelpConsole)
self.helpAPIAction.triggered.connect(self.openHelpAPI)
self.helpCookbookAction.triggered.connect(self.openHelpCookbook)
self.object_inspector_action.toggled.connect(self.toggleObjectListWidget)
self.toggle_comment_action.triggered.connect(self.toggleComment)
self.reformat_code_action.triggered.connect(self.reformatCode)
self.run_script_action.triggered.connect(self.runScriptEditor)
self.run_selection_action.triggered.connect(self.runSelectedEditor)
self.cut_action.triggered.connect(self.cutEditor)
self.copy_action.triggered.connect(self.copyEditor)
self.paste_action.triggered.connect(self.pasteEditor)
self.show_editor_action.toggled.connect(self.toggleEditor)
self.clear_action.triggered.connect(self.shell_output.clearConsole)
self.options_action.triggered.connect(self.openSettings)
self.run_action.triggered.connect(self.shell.entered)
self.open_file_action.triggered.connect(self.openScriptFile)
self.open_in_editor_action.triggered.connect(self.openScriptFileExtEditor)
self.save_file_action.triggered.connect(self.saveScriptFile)
self.save_as_file_action.triggered.connect(self.saveAsScriptFile)
self.help_console_action.triggered.connect(self.openHelpConsole)
self.help_api_action.triggered.connect(self.openHelpAPI)
self.help_cookbook_action.triggered.connect(self.openHelpCookbook)
self.listClassMethod.itemClicked.connect(self.onClickGoToLine)
if iface is not None:

View File

@ -33,7 +33,13 @@ from operator import itemgetter
from pathlib import Path
from qgis.core import Qgis, QgsApplication, QgsBlockingNetworkRequest, QgsSettings
from qgis.gui import QgsCodeEditorPython, QgsCodeEditorWidget, QgsMessageBar
from qgis.gui import (
QgsCodeEditorPython,
QgsCodeEditorWidget,
QgsGui,
QgsMessageBar,
QgsShortcutsManager,
)
from qgis.PyQt.Qsci import QsciScintilla
from qgis.PyQt.QtCore import (
@ -159,15 +165,6 @@ class Editor(QgsCodeEditorPython):
syntaxCheckAction.setShortcut("Ctrl+4")
menu.addAction(syntaxCheckAction)
runSelected = QAction(
QgsApplication.getThemeIcon("mActionRunSelected.svg"), # spellok
QCoreApplication.translate("PythonConsole", "Run Selected"),
menu,
)
runSelected.triggered.connect(self.runSelectedCode) # spellok
runSelected.setShortcut("Ctrl+E") # spellok
menu.addAction(runSelected) # spellok
word = self.selectedText() or self.wordAtPoint(e.pos())
if word:
context_help_action = QAction(
@ -185,13 +182,24 @@ class Editor(QgsCodeEditorPython):
context_help_action.setShortcut(QKeySequence.StandardKey.HelpContents)
menu.addAction(context_help_action)
start_action = QAction(
QgsApplication.getThemeIcon("mActionStart.svg"),
QCoreApplication.translate("PythonConsole", "Run Script"),
menu,
run_selection_action = QAction(menu)
run_selection_action.setIcon(
QgsApplication.getThemeIcon("mActionRunSelected.svg"),
)
run_selection_action.triggered.connect(self.runSelectedCode)
QgsGui.shortcutsManager().initializeCommonAction(
run_selection_action,
QgsShortcutsManager.CommonAction.CodeRunSelection,
)
menu.addAction(run_selection_action)
start_action = QAction(self)
start_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
start_action.triggered.connect(self.runScriptCode)
start_action.setShortcut("Ctrl+Shift+E")
QgsGui.shortcutsManager().initializeCommonAction(
start_action,
QgsShortcutsManager.CommonAction.CodeRunScript,
)
menu.addAction(start_action)
menu.addSeparator()
@ -258,18 +266,31 @@ class Editor(QgsCodeEditorPython):
menu.addAction(selectAllAction)
menu.addSeparator()
toggle_comment_action = QAction(
toggle_comment_action = QAction(menu)
toggle_comment_action.setIcon(
QgsApplication.getThemeIcon(
"console/iconCommentEditorConsole.svg",
self.palette().color(QPalette.ColorRole.WindowText),
),
QCoreApplication.translate("PythonConsole", "Toggle Comment"),
menu,
)
)
toggle_comment_action.triggered.connect(self.toggleComment)
toggle_comment_action.setShortcut("Ctrl+:")
QgsGui.shortcutsManager().initializeCommonAction(
toggle_comment_action,
QgsShortcutsManager.CommonAction.CodeToggleComment,
)
menu.addAction(toggle_comment_action)
reformat_code_action = QAction(menu)
reformat_code_action.setIcon(
QgsApplication.getThemeIcon("console/iconFormatCode.svg")
)
reformat_code_action.triggered.connect(self.reformatCode)
QgsGui.shortcutsManager().initializeCommonAction(
reformat_code_action,
QgsShortcutsManager.CommonAction.CodeReformat,
)
menu.addAction(reformat_code_action)
menu.addSeparator()
gist_menu = QMenu(self)
gist_menu.setTitle(
@ -299,14 +320,14 @@ class Editor(QgsCodeEditorPython):
syntaxCheckAction.setEnabled(False)
pasteAction.setEnabled(False)
cutAction.setEnabled(False)
runSelected.setEnabled(False) # spellok
run_selection_action.setEnabled(False)
copyAction.setEnabled(False)
selectAllAction.setEnabled(False)
undoAction.setEnabled(False)
redoAction.setEnabled(False)
showCodeInspection.setEnabled(False)
if self.hasSelectedText():
runSelected.setEnabled(True) # spellok
run_selection_action.setEnabled(True)
copyAction.setEnabled(True)
cutAction.setEnabled(True)
if not self.text() == "":
@ -326,17 +347,17 @@ class Editor(QgsCodeEditorPython):
listObj = self.console_widget.listClassMethod
if listObj.isVisible():
listObj.hide()
self.console_widget.objectListButton.setChecked(False)
self.console_widget.object_inspector_action.setChecked(False)
else:
listObj.show()
self.console_widget.objectListButton.setChecked(True)
self.console_widget.object_inspector_action.setChecked(True)
def shareOnGist(self, is_public):
self.code_editor_widget.shareOnGist(is_public)
def hideEditor(self):
self.console_widget.splitterObj.hide()
self.console_widget.showEditorButton.setChecked(False)
self.console_widget.show_editor_action.setChecked(False)
def createTempFile(self):
name = tempfile.NamedTemporaryFile(delete=False).name
@ -475,7 +496,7 @@ class Editor(QgsCodeEditorPython):
)
self.tab_widget.setTabToolTip(index, self.code_editor_widget.filePath())
self.setModified(False)
self.console_widget.saveFileButton.setEnabled(False)
self.console_widget.save_file_action.setEnabled(False)
self.console_widget.updateTabListScript(
self.code_editor_widget.filePath(), action="append"
)
@ -720,7 +741,7 @@ class EditorTabWidget(QTabWidget):
# New Editor button
self.newTabButton = QToolButton()
txtToolTipNewTab = QCoreApplication.translate("PythonConsole", "New Editor")
self.newTabButton.setToolTip(txtToolTipNewTab + " <b>Ctrl+T</b>")
self.newTabButton.setToolTip(f"<b>{txtToolTipNewTab}</b> (Ctrl+T)")
self.newTabButton.setAutoRaise(True)
self.newTabButton.setIcon(
QgsApplication.getThemeIcon("console/iconNewTabEditorConsole.svg")
@ -810,7 +831,7 @@ class EditorTabWidget(QTabWidget):
def enableSaveIfModified(self, tab):
tabWidget = self.widget(tab)
if tabWidget:
self.console_widget.saveFileButton.setEnabled(tabWidget.isModified())
self.console_widget.save_file_action.setEnabled(tabWidget.isModified())
def enableToolBarEditor(self, enable):
if self.topFrame.isVisible():
@ -864,7 +885,7 @@ class EditorTabWidget(QTabWidget):
index = self.indexOf(tab)
s = self.tabText(index)
self.setTabTitle(index, f"*{s}" if modified else re.sub(r"^(\*)", "", s))
self.console_widget.saveFileButton.setEnabled(modified)
self.console_widget.save_file_action.setEnabled(modified)
def setTabTitle(self, tab, title):
self.setTabText(tab, title)
@ -1071,10 +1092,9 @@ class EditorTabWidget(QTabWidget):
objInspectorEnabled = QgsSettings().value(
"pythonConsole/enableObjectInsp", False, type=bool
)
listObj = self.console_widget.objectListButton
if self.console_widget.listClassMethod.isVisible():
listObj.setChecked(objInspectorEnabled)
listObj.setEnabled(objInspectorEnabled)
self.console_widget.object_inspector_action.setChecked(objInspectorEnabled)
self.console_widget.object_inspector_action.setEnabled(objInspectorEnabled)
if objInspectorEnabled:
cW = self.currentWidget()
if cW and not self.console_widget.listClassMethod.isVisible():

View File

@ -348,7 +348,7 @@ class ShellOutputScintilla(QgsCodeEditorPython):
Ed = self.console_widget.splitterObj
if not Ed.isVisible():
Ed.show()
self.console_widget.showEditorButton.setChecked(True)
self.console_widget.show_editor_action.setChecked(True)
self.shell_editor.setFocus()
def copy(self):

View File

@ -11645,6 +11645,25 @@ Qgis.RasterProcessingParameterCapability.__doc__ = """Capabilities of a raster l
Qgis.RasterProcessingParameterCapability.baseClass = Qgis
Qgis.RasterProcessingParameterCapabilities.baseClass = Qgis
RasterProcessingParameterCapabilities = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.DevToolsNodeRole.Status.__doc__ = "Request status role"
Qgis.DevToolsNodeRole.Id.__doc__ = "Request ID role"
Qgis.DevToolsNodeRole.ElapsedTime.__doc__ = "Elapsed time"
Qgis.DevToolsNodeRole.MaximumTime.__doc__ = "Maximum encountered elapsed time"
Qgis.DevToolsNodeRole.Sort.__doc__ = "Sort order role"
Qgis.DevToolsNodeRole.__doc__ = """Dev tools node custom data roles.
.. versionadded:: 4.0
* ``Status``: Request status role
* ``Id``: Request ID role
* ``ElapsedTime``: Elapsed time
* ``MaximumTime``: Maximum encountered elapsed time
* ``Sort``: Sort order role
"""
# --
Qgis.DevToolsNodeRole.baseClass = Qgis
from enum import Enum

View File

@ -3446,6 +3446,15 @@ The development version
typedef QFlags<Qgis::RasterProcessingParameterCapability> RasterProcessingParameterCapabilities;
enum class DevToolsNodeRole
{
Status,
Id,
ElapsedTime,
MaximumTime,
Sort,
};
static const double DEFAULT_SEARCH_RADIUS_MM;
static const float DEFAULT_MAPTOPIXEL_THRESHOLD;

View File

@ -1,15 +1,15 @@
Qgis.defaultProjectScales: src/core/qgis.h#L6171
Qgis.defaultProjectScales: src/core/qgis.h#L6185
Qgis.devVersion: src/core/qgis.h#L90
Qgis.geoNone: src/core/qgis.h#L6223
Qgis.geoProj4: src/core/qgis.h#L6253
Qgis.geoWkt: src/core/qgis.h#L6244
Qgis.geographicCrsAuthId: src/core/qgis.h#L6233
Qgis.geosVersion: src/core/qgis.h#L6206
Qgis.geosVersionInt: src/core/qgis.h#L6178
Qgis.geosVersionMajor: src/core/qgis.h#L6185
Qgis.geosVersionMinor: src/core/qgis.h#L6192
Qgis.geosVersionPatch: src/core/qgis.h#L6199
Qgis.hasQtWebkit: src/core/qgis.h#L6213
Qgis.geoNone: src/core/qgis.h#L6237
Qgis.geoProj4: src/core/qgis.h#L6267
Qgis.geoWkt: src/core/qgis.h#L6258
Qgis.geographicCrsAuthId: src/core/qgis.h#L6247
Qgis.geosVersion: src/core/qgis.h#L6220
Qgis.geosVersionInt: src/core/qgis.h#L6192
Qgis.geosVersionMajor: src/core/qgis.h#L6199
Qgis.geosVersionMinor: src/core/qgis.h#L6206
Qgis.geosVersionPatch: src/core/qgis.h#L6213
Qgis.hasQtWebkit: src/core/qgis.h#L6227
Qgis.releaseName: src/core/qgis.h#L80
Qgis.version: src/core/qgis.h#L66
Qgis.versionInt: src/core/qgis.h#L73

View File

@ -0,0 +1,18 @@
# The following has been generated automatically from src/gui/qgsshortcutsmanager.h
# monkey patching scoped based enum
QgsShortcutsManager.CommonAction.CodeToggleComment.__doc__ = "Toggle code comments"
QgsShortcutsManager.CommonAction.CodeReformat.__doc__ = "Reformat code"
QgsShortcutsManager.CommonAction.CodeRunScript.__doc__ = "Run script"
QgsShortcutsManager.CommonAction.CodeRunSelection.__doc__ = "Run selection from script"
QgsShortcutsManager.CommonAction.__doc__ = """Contains common actions which are used across a variety of classes.
.. versionadded:: 4.0
* ``CodeToggleComment``: Toggle code comments
* ``CodeReformat``: Reformat code
* ``CodeRunScript``: Run script
* ``CodeRunSelection``: Run selection from script
"""
# --
QgsShortcutsManager.CommonAction.baseClass = QgsShortcutsManager

View File

@ -23,6 +23,14 @@ rather accessed through :py:func:`QgsGui.shortcutsManager()`.
#include "qgsshortcutsmanager.h"
%End
public:
enum class CommonAction
{
CodeToggleComment,
CodeReformat,
CodeRunScript,
CodeRunSelection,
};
QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" );
%Docstring
Constructor for QgsShortcutsManager.
@ -36,6 +44,8 @@ Constructor for QgsShortcutsManager.
QGIS actions.
%End
~QgsShortcutsManager();
void registerAllChildren( QObject *object, bool recursive = false, const QString &section = QString() );
%Docstring
Automatically registers all QActions and QShortcuts which are children
@ -105,6 +115,16 @@ in GUI.
.. seealso:: :py:func:`unregisterAction`
.. seealso:: :py:func:`registerAllChildActions`
%End
void initializeCommonAction( QAction *action, CommonAction commonAction );
%Docstring
Initializes an ``action`` as a common action.
This automatically configures the ``action`` to use the properties for
the common action, such as setting the action's tooltip and shortcut.
.. versionadded:: 4.0
%End
bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString &section = QString() );
@ -290,6 +310,14 @@ if no shortcut is associated.
.. seealso:: :py:func:`objectForSequence`
.. seealso:: :py:func:`actionForSequence`
%End
QKeySequence sequenceForCommonAction( CommonAction action ) const;
%Docstring
Returns the key sequence which is associated with a common ``action``,
or an empty sequence if no shortcut is assigned to that action.
.. versionadded:: 4.0
%End
QAction *actionByName( const QString &name ) const;

View File

@ -6977,28 +6977,30 @@ QgsShapeburstFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwid
QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426
QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427
QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L236
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L221
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L163
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L171
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L155
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L213
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L262
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L254
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L94
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L70
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L81
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L59
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L106
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L180
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L196
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L204
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L188
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L246
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L243
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L229
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L116
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L126
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L267
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L246
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L188
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L196
QgsShortcutsManager.initializeCommonAction: src/gui/qgsshortcutsmanager.h#L119
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L180
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L238
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L293
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L285
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L109
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L85
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L96
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L74
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L131
QgsShortcutsManager.sequenceForCommonAction: src/gui/qgsshortcutsmanager.h#L260
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L205
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L221
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L229
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L213
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L277
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L274
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L254
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L141
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L151
QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36
QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264
QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271

View File

@ -30,7 +30,7 @@ from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QPalette
from qgis.PyQt.QtWidgets import QMessageBox, QFileDialog, QVBoxLayout
from qgis.gui import QgsGui, QgsErrorDialog, QgsCodeEditorWidget
from qgis.gui import QgsGui, QgsErrorDialog, QgsCodeEditorWidget, QgsShortcutsManager
from qgis.core import (
QgsApplication,
QgsFileUtils,
@ -122,6 +122,9 @@ class ScriptEditorDialog(BASE, WIDGET):
self.palette().color(QPalette.ColorRole.WindowText),
)
)
QgsGui.shortcutsManager().initializeCommonAction(
self.actionToggleComment, QgsShortcutsManager.CommonAction.CodeToggleComment
)
# Connect signals and slots
self.actionOpenScript.triggered.connect(self.openScript)

View File

@ -41,7 +41,7 @@ def userFolder():
def defaultOutputFolder():
folder = os.path.join(userFolder(), "outputs")
folder = os.path.join(QDir.homePath(), "processing")
if not QDir(folder).exists():
QDir().mkpath(folder)

View File

@ -8,6 +8,8 @@ set(QGIS_3D_SRCS
qgsaabb.cpp
qgsabstract3dengine.cpp
qgsabstractvectorlayer3drenderer.cpp
qgsannotationlayer3drenderer.cpp
qgsannotationlayerchunkloader_p.cpp
qgs3danimationsettings.cpp
qgs3dexportobject.cpp
qgs3dmapexportsettings.cpp
@ -154,6 +156,8 @@ set(QGIS_3D_HDRS
qgsaabb.h
qgsabstract3dengine.h
qgsabstractvectorlayer3drenderer.h
qgsannotationlayer3drenderer.h
qgsannotationlayerchunkloader_p.h
qgscameracontroller.h
qgscamerapose.h
qgsgeotransform.h

View File

@ -27,6 +27,7 @@
#include "qgsmeshlayer3drenderer.h"
#include "qgspointcloudlayer3drenderer.h"
#include "qgstiledscenelayer3drenderer.h"
#include "qgsannotationlayer3drenderer.h"
#include "qgs3dsymbolregistry.h"
#include "qgspoint3dsymbol.h"
#include "qgsline3dsymbol.h"
@ -68,6 +69,7 @@ void Qgs3D::initialize()
QgsApplication::renderer3DRegistry()->addRenderer( new QgsMeshLayer3DRendererMetadata );
QgsApplication::renderer3DRegistry()->addRenderer( new QgsPointCloudLayer3DRendererMetadata );
QgsApplication::renderer3DRegistry()->addRenderer( new QgsTiledSceneLayer3DRendererMetadata );
QgsApplication::renderer3DRegistry()->addRenderer( new QgsAnnotationLayer3DRendererMetadata );
QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "point" ), QObject::tr( "Point" ), &QgsPoint3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForPoint3DSymbol ) );
QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "line" ), QObject::tr( "Line" ), &QgsLine3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForLine3DSymbol ) );

View File

@ -45,6 +45,7 @@
#include "qgsapplication.h"
#include "qgsaabb.h"
#include "qgsabstract3dengine.h"
#include "qgsannotationlayer.h"
#include "qgs3dmapsettings.h"
#include "qgs3dutils.h"
#include "qgsabstract3drenderer.h"
@ -66,6 +67,7 @@
#include "qgsterraingenerator.h"
#include "qgstiledscenelayer.h"
#include "qgstiledscenelayer3drenderer.h"
#include "qgsannotationlayer3drenderer.h"
#include "qgsdirectionallightsettings.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayer3drenderer.h"
@ -746,6 +748,11 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer );
tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) );
}
else if ( layer->type() == Qgis::LayerType::Annotation && renderer->type() == QLatin1String( "annotation" ) )
{
auto annotationLayerRenderer = qgis::down_cast<QgsAnnotationLayer3DRenderer *>( renderer );
annotationLayerRenderer->setLayer( qobject_cast<QgsAnnotationLayer *>( layer ) );
}
Qt3DCore::QEntity *newEntity = renderer->createEntity( &mMap );
if ( newEntity )

View File

@ -1161,7 +1161,7 @@ QgsCameraPose Qgs3DUtils::lineSegmentToCameraPose( const QgsVector3D &startPoint
std::unique_ptr<Qt3DRender::QCamera> Qgs3DUtils::copyCamera( Qt3DRender::QCamera *cam )
{
std::unique_ptr<Qt3DRender::QCamera> copy = std::make_unique<Qt3DRender::QCamera>();
auto copy = std::make_unique<Qt3DRender::QCamera>();
copy->setPosition( cam->position() );
copy->setViewCenter( cam->viewCenter() );
copy->setUpVector( cam->upVector() );

View File

@ -0,0 +1,155 @@
/***************************************************************************
qgsannotationlayer3drenderer.cpp
--------------------------------------
Date : January 2020
Copyright : (C) 2020 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsannotationlayer3drenderer.h"
#include "qgsannotationlayer.h"
#include "qgsannotationlayerchunkloader_p.h"
#include "qgis.h"
//
// QgsAnnotationLayer3DRendererMetadata
//
QgsAnnotationLayer3DRendererMetadata::QgsAnnotationLayer3DRendererMetadata()
: Qgs3DRendererAbstractMetadata( QStringLiteral( "annotation" ) )
{
}
QgsAbstract3DRenderer *QgsAnnotationLayer3DRendererMetadata::createRenderer( QDomElement &elem, const QgsReadWriteContext &context )
{
auto r = std::make_unique< QgsAnnotationLayer3DRenderer >();
r->readXml( elem, context );
return r.release();
}
//
// QgsAnnotationLayer3DRenderer
//
QgsAnnotationLayer3DRenderer::QgsAnnotationLayer3DRenderer() = default;
void QgsAnnotationLayer3DRenderer::setLayer( QgsAnnotationLayer *layer )
{
mLayerRef = QgsMapLayerRef( layer );
}
QgsAnnotationLayer *QgsAnnotationLayer3DRenderer::layer() const
{
return qobject_cast<QgsAnnotationLayer *>( mLayerRef.layer );
}
void QgsAnnotationLayer3DRenderer::resolveReferences( const QgsProject &project )
{
mLayerRef.resolve( &project );
}
bool QgsAnnotationLayer3DRenderer::showCalloutLines() const
{
return mShowCalloutLines;
}
void QgsAnnotationLayer3DRenderer::setShowCalloutLines( bool show )
{
mShowCalloutLines = show;
}
void QgsAnnotationLayer3DRenderer::setCalloutLineColor( const QColor &color )
{
mCalloutLineColor = color;
}
QColor QgsAnnotationLayer3DRenderer::calloutLineColor() const
{
return mCalloutLineColor;
}
void QgsAnnotationLayer3DRenderer::setCalloutLineWidth( double width )
{
mCalloutLineWidth = width;
}
double QgsAnnotationLayer3DRenderer::calloutLineWidth() const
{
return mCalloutLineWidth;
}
QString QgsAnnotationLayer3DRenderer::type() const
{
return "annotation";
}
QgsAnnotationLayer3DRenderer *QgsAnnotationLayer3DRenderer::clone() const
{
auto r = std::make_unique< QgsAnnotationLayer3DRenderer >();
r->mLayerRef = mLayerRef;
r->mAltClamping = mAltClamping;
r->mZOffset = mZOffset;
r->mShowCalloutLines = mShowCalloutLines;
r->mCalloutLineColor = mCalloutLineColor;
r->mCalloutLineWidth = mCalloutLineWidth;
return r.release();
}
Qt3DCore::QEntity *QgsAnnotationLayer3DRenderer::createEntity( Qgs3DMapSettings *map ) const
{
QgsAnnotationLayer *l = layer();
if ( !l )
return nullptr;
// For some cases we start with a maximal z range because we can't know this upfront, as it potentially involves terrain heights.
// This range will be refined after populating the nodes to the actual z range of the generated chunks nodes.
// Assuming the vertical height is in meter, then it's extremely unlikely that a real vertical
// height will exceed this amount!
constexpr double MINIMUM_ANNOTATION_Z_ESTIMATE = -100000;
constexpr double MAXIMUM_ANNOTATION_Z_ESTIMATE = 100000;
double minimumZ = MINIMUM_ANNOTATION_Z_ESTIMATE;
double maximumZ = MAXIMUM_ANNOTATION_Z_ESTIMATE;
switch ( mAltClamping )
{
case Qgis::AltitudeClamping::Absolute:
// special case where we DO know the exact z range upfront!
minimumZ = mZOffset;
maximumZ = mZOffset;
break;
case Qgis::AltitudeClamping::Relative:
case Qgis::AltitudeClamping::Terrain:
break;
}
return new QgsAnnotationLayerChunkedEntity( map, l, mAltClamping, mZOffset, mShowCalloutLines, mCalloutLineColor, mCalloutLineWidth, minimumZ, maximumZ );
}
void QgsAnnotationLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext & ) const
{
QDomDocument doc = elem.ownerDocument();
elem.setAttribute( QStringLiteral( "layer" ), mLayerRef.layerId );
elem.setAttribute( QStringLiteral( "clamping" ), qgsEnumValueToKey( mAltClamping ) );
elem.setAttribute( QStringLiteral( "offset" ), mZOffset );
if ( mShowCalloutLines )
elem.setAttribute( QStringLiteral( "callouts" ), QStringLiteral( "1" ) );
}
void QgsAnnotationLayer3DRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext & )
{
mLayerRef = QgsMapLayerRef( elem.attribute( QStringLiteral( "layer" ) ) );
mAltClamping = qgsEnumKeyToValue( elem.attribute( QStringLiteral( "clamping" ) ), Qgis::AltitudeClamping::Relative );
mZOffset = elem.attribute( QStringLiteral( "offset" ), QString::number( DEFAULT_Z_OFFSET ) ).toDouble();
mShowCalloutLines = elem.attribute( QStringLiteral( "callouts" ), QStringLiteral( "0" ) ).toInt();
}

View File

@ -0,0 +1,185 @@
/***************************************************************************
qgsannotationlayer3drenderer.h
--------------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSANNOTATIONLAYER3DRENDERER_H
#define QGSANNOTATIONLAYER3DRENDERER_H
#include "qgis_3d.h"
#include "qgis_sip.h"
#include "qgs3drendererregistry.h"
#include "qgsabstract3drenderer.h"
#include "qgsmaplayerref.h"
class QgsAnnotationLayer;
#ifdef SIP_RUN
// this is needed for the "convert to subclass" code below to compile
% ModuleHeaderCode
#include "qgsannotationlayer3drenderer.h"
% End
#endif
/**
* \ingroup core
* \brief Metadata for annotation layer 3D renderer to allow creation of its instances from XML.
*
* \warning This is not considered stable API, and may change in future QGIS releases. It is
* exposed to the Python bindings as a tech preview only.
*
* \since QGIS 4.0
*/
class _3D_EXPORT QgsAnnotationLayer3DRendererMetadata : public Qgs3DRendererAbstractMetadata
{
public:
QgsAnnotationLayer3DRendererMetadata();
//! Creates an instance of a 3D renderer based on a DOM element with renderer configuration
QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) override SIP_FACTORY;
};
/**
* \ingroup qgis_3d
* \brief 3D renderers for annotation layers.
*
* \since QGIS 4.0
*/
class _3D_EXPORT QgsAnnotationLayer3DRenderer : public QgsAbstract3DRenderer
{
#ifdef SIP_RUN
SIP_CONVERT_TO_SUBCLASS_CODE
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
sipType = sipType_QgsAnnotationLayer3DRenderer;
else
sipType = nullptr;
SIP_END
#endif
public:
QgsAnnotationLayer3DRenderer();
/**
* Sets the annotation layer associated with the renderer.
*
* \see layer()
*/
void setLayer( QgsAnnotationLayer *layer );
/**
* Returns the annotation layer associated with the renderer.
*
* \see setLayer()
*/
QgsAnnotationLayer *layer() const;
QString type() const override;
QgsAnnotationLayer3DRenderer *clone() const override SIP_FACTORY;
Qt3DCore::QEntity *createEntity( Qgs3DMapSettings *map ) const override SIP_SKIP;
void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
void resolveReferences( const QgsProject &project ) override;
/**
* Returns the altitude clamping method, which determines the vertical position of annotations.
*
* \see setAltitudeClamping()
*/
Qgis::AltitudeClamping altitudeClamping() const { return mAltClamping; }
/**
* Sets the altitude \a clamping method, which determines the vertical position of annotations.
*
* \see altitudeClamping()
*/
void setAltitudeClamping( Qgis::AltitudeClamping clamping ) { mAltClamping = clamping; }
/**
* Returns the z offset, which is a fixed offset amount which should be added to z values for the annotations.
*
* \see setZOffset()
*/
double zOffset() const { return mZOffset; }
/**
* Sets the z \a offset, which is a fixed offset amount which will be added to z values for the annotations.
*
* \see zOffset()
*/
void setZOffset( double offset ) { mZOffset = offset; }
/**
* Returns TRUE if callout lines are shown, vertically joining the annotations to the terrain.
*
* \see setShowCalloutLines()
*/
bool showCalloutLines() const;
/**
* Sets whether callout lines are shown, vertically joining the annotations to the terrain.
*
* \see showCalloutLines()
*/
void setShowCalloutLines( bool show );
// TODO -- consider exposing via QgsSimpleLineMaterialSettings, for now, for testing only
/**
* Sets the callout line \a color.
*
* \see calloutLineColor()
* \note Not available in Python bindings
*/
SIP_SKIP void setCalloutLineColor( const QColor &color );
/**
* Returns the callout line color.
*
* \see setCalloutLineColor()
* \note Not available in Python bindings
*/
SIP_SKIP QColor calloutLineColor() const;
/**
* Sets the callout line \a width.
*
* \see calloutLineWidth()
* \note Not available in Python bindings
*/
SIP_SKIP void setCalloutLineWidth( double width );
/**
* Returns the callout line width.
*
* \see setCalloutLineWidth()
* \note Not available in Python bindings
*/
SIP_SKIP double calloutLineWidth() const;
private:
#ifdef SIP_RUN
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
#endif
static constexpr double DEFAULT_Z_OFFSET = 50;
QgsMapLayerRef mLayerRef;
Qgis::AltitudeClamping mAltClamping = Qgis::AltitudeClamping::Relative;
double mZOffset = DEFAULT_Z_OFFSET;
bool mShowCalloutLines = true;
QColor mCalloutLineColor { 0, 0, 0 };
double mCalloutLineWidth = 2;
};
#endif // QGSANNOTATIONLAYER3DRENDERER_H

View File

@ -0,0 +1,396 @@
/***************************************************************************
qgsannotationlayerchunkloader_p.cpp
--------------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsannotationlayerchunkloader_p.h"
#include "moc_qgsannotationlayerchunkloader_p.cpp"
#include "qgs3dutils.h"
#include "qgsannotationitem.h"
#include "qgstessellatedpolygongeometry.h"
#include "qgschunknode.h"
#include "qgseventtracing.h"
#include "qgslogger.h"
#include "qgsannotationlayer.h"
#include "qgsabstract3dsymbol.h"
#include "qgsabstractterrainsettings.h"
#include "qgsannotationmarkeritem.h"
#include "qgsbillboardgeometry.h"
#include "qgspoint3dbillboardmaterial.h"
#include "qgsgeotransform.h"
#include "qgsexpressioncontextutils.h"
#include "qgstextureatlasgenerator.h"
#include "qgslinevertexdata_p.h"
#include "qgslinematerial_p.h"
#include <QtConcurrent>
#include <Qt3DCore/QTransform>
///@cond PRIVATE
QgsAnnotationLayerChunkLoader::QgsAnnotationLayerChunkLoader( const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node )
: QgsChunkLoader( node )
, mFactory( factory )
, mRenderContext( factory->mRenderContext )
{
}
struct Billboard
{
QVector3D position;
int textureId;
};
void QgsAnnotationLayerChunkLoader::start()
{
QgsChunkNode *node = chunk();
if ( node->level() < mFactory->mLeafLevel )
{
QTimer::singleShot( 0, this, &QgsAnnotationLayerChunkLoader::finished );
return;
}
QgsAnnotationLayer *layer = mFactory->mLayer;
mLayerName = mFactory->mLayer->name();
// only a subset of data to be queried
const QgsRectangle rect = node->box3D().toRectangle();
// origin for coordinates of the chunk - it is kind of arbitrary, but it should be
// picked so that the coordinates are relatively small to avoid numerical precision issues
mChunkOrigin = QgsVector3D( rect.center().x(), rect.center().y(), 0 );
QgsExpressionContext exprContext;
exprContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
mRenderContext.setExpressionContext( exprContext );
QgsCoordinateTransform layerToMapTransform( layer->crs(), mRenderContext.crs(), mRenderContext.transformContext() );
QgsRectangle layerExtent;
try
{
layerExtent = layerToMapTransform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
}
catch ( QgsCsException &e )
{
QgsDebugError( QStringLiteral( "Error transforming annotation layer extent to 3d map extent: %1" ).arg( e.what() ) );
return;
}
const double zOffset = mFactory->mZOffset;
const Qgis::AltitudeClamping altitudeClamping = mFactory->mClamping;
bool showCallouts = mFactory->mShowCallouts;
// see logic from QgsAnnotationLayerRenderer
const QStringList itemsList = layer->queryIndex( layerExtent );
QSet< QString > itemIds( itemsList.begin(), itemsList.end() );
// we also have NO choice but to clone ALL non-indexed items (i.e. those with a scale-dependent bounding box)
// since these won't be in the layer's spatial index, and it's too expensive to determine their actual bounding box
// upfront (we are blocking the main thread right now!)
// TODO -- come up with some brilliant way to avoid this and also index scale-dependent items ;)
itemIds.unite( layer->mNonIndexedItems );
mItemsToRender.reserve( itemIds.size() );
std::transform( itemIds.begin(), itemIds.end(), std::back_inserter( mItemsToRender ), [layer]( const QString &id ) -> std::unique_ptr< QgsAnnotationItem > {
return std::unique_ptr< QgsAnnotationItem >( layer->item( id )->clone() );
} );
//
// this will be run in a background thread
//
mFutureWatcher = new QFutureWatcher<void>( this );
connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
const QFuture<void> future = QtConcurrent::run( [this, rect, layerToMapTransform, zOffset, altitudeClamping, showCallouts] {
const QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "Annotation layer chunk load" ) );
std::vector< Billboard > billboards;
billboards.reserve( mItemsToRender.size() );
QVector< QImage > textures;
textures.reserve( mItemsToRender.size() );
for ( const std::unique_ptr< QgsAnnotationItem > &item : std::as_const( mItemsToRender ) )
{
if ( mCanceled )
break;
QgsAnnotationItem *annotation = item.get();
if ( !annotation->enabled() )
continue;
if ( QgsAnnotationMarkerItem *marker = dynamic_cast< QgsAnnotationMarkerItem * >( annotation ) )
{
if ( marker->symbol() )
{
QgsPointXY p = marker->geometry();
try
{
const QgsPointXY mapPoint = layerToMapTransform.transform( p );
if ( !rect.contains( mapPoint ) )
continue;
double z = 0;
const float terrainZ = ( altitudeClamping == Qgis::AltitudeClamping::Absolute && !showCallouts ) ? 0 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator() ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
: 0.f;
switch ( altitudeClamping )
{
case Qgis::AltitudeClamping::Absolute:
z = zOffset;
break;
case Qgis::AltitudeClamping::Terrain:
z = terrainZ;
break;
case Qgis::AltitudeClamping::Relative:
z = terrainZ + zOffset;
break;
}
Billboard billboard;
billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
billboard.textureId = textures.size();
textures.append( QgsPoint3DBillboardMaterial::renderSymbolToImage( marker->symbol(), mRenderContext ) );
billboards.emplace_back( std::move( billboard ) );
if ( showCallouts )
{
mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
}
mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
}
catch ( QgsCsException &e )
{
QgsDebugError( e.what() );
}
}
}
}
// free memory
mItemsToRender.clear();
const QgsTextureAtlas atlas = QgsTextureAtlasGenerator::createFromImages( textures, 2048 );
if ( atlas.isValid() )
{
mBillboardAtlas = atlas.renderAtlasTexture();
mBillboardPositions.reserve( static_cast< int >( billboards.size() ) );
for ( Billboard &billboard : billboards )
{
const QRect textureRect = atlas.rect( billboard.textureId );
QgsBillboardGeometry::BillboardAtlasData geometry;
geometry.position = billboard.position;
geometry.textureAtlasOffset = QVector2D( static_cast< float >( textureRect.left() ) / static_cast< float>( mBillboardAtlas.width() ), 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mBillboardAtlas.height() ) ) );
geometry.textureAtlasSize = QVector2D( static_cast< float >( textureRect.width() ) / static_cast< float>( mBillboardAtlas.width() ), static_cast< float>( textureRect.height() ) / static_cast< float>( mBillboardAtlas.height() ) );
mBillboardPositions.append( geometry );
}
}
else
{
QgsDebugError( QStringLiteral( "Error encountered building texture atlas" ) );
mBillboardAtlas = QImage();
}
} );
// emit finished() as soon as the handler is populated with features
mFutureWatcher->setFuture( future );
}
QgsAnnotationLayerChunkLoader::~QgsAnnotationLayerChunkLoader()
{
if ( mFutureWatcher && !mFutureWatcher->isFinished() )
{
disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
mFutureWatcher->waitForFinished();
}
}
void QgsAnnotationLayerChunkLoader::cancel()
{
mCanceled = true;
}
Qt3DCore::QEntity *QgsAnnotationLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
{
if ( mNode->level() < mFactory->mLeafLevel )
{
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent ); // dummy entity
entity->setObjectName( mLayerName + "_CONTAINER_" + mNode->tileId().text() );
return entity;
}
if ( mBillboardPositions.empty() )
{
// an empty node, so we return no entity. This tags the node as having no data and effectively removes it.
// we just make sure first that its initial estimated vertical range does not affect its parents' bboxes calculation
mNode->setExactBox3D( QgsBox3D() );
mNode->updateParentBoundingBoxesRecursively();
return nullptr;
}
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
entity->setObjectName( mLayerName + "_" + mNode->tileId().text() );
QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
billboardGeometry->setBillboardData( mBillboardPositions );
Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
billboardGeometryRenderer->setGeometry( billboardGeometry );
billboardGeometryRenderer->setVertexCount( billboardGeometry->count() );
QgsPoint3DBillboardMaterial *billboardMaterial = new QgsPoint3DBillboardMaterial( QgsPoint3DBillboardMaterial::Mode::AtlasTexture );
billboardMaterial->setTexture2DFromImage( mBillboardAtlas );
QgsGeoTransform *billboardTransform = new QgsGeoTransform;
billboardTransform->setGeoTranslation( mChunkOrigin );
Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
billboardEntity->addComponent( billboardMaterial );
billboardEntity->addComponent( billboardTransform );
billboardEntity->addComponent( billboardGeometryRenderer );
billboardEntity->setParent( entity );
if ( mFactory->mShowCallouts )
{
QgsLineVertexData lineData;
lineData.withAdjacency = true;
lineData.geocentricCoordinates = false; // mMapSettings->sceneMode() == Qgis::SceneMode::Globe;
lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, mRenderContext, mChunkOrigin );
for ( const QgsLineString &line : mCalloutLines )
{
lineData.addLineString( line, 0, false );
}
QgsLineMaterial *mat = new QgsLineMaterial;
mat->setLineColor( mFactory->mCalloutLineColor );
mat->setLineWidth( mFactory->mCalloutLineWidth );
Qt3DCore::QEntity *calloutEntity = new Qt3DCore::QEntity;
calloutEntity->setObjectName( parent->objectName() + "_CALLOUTS" );
// geometry renderer
Qt3DRender::QGeometryRenderer *calloutRenderer = new Qt3DRender::QGeometryRenderer;
calloutRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
calloutRenderer->setGeometry( lineData.createGeometry( calloutEntity ) );
calloutRenderer->setVertexCount( lineData.indexes.count() );
calloutRenderer->setPrimitiveRestartEnabled( true );
calloutRenderer->setRestartIndexValue( 0 );
// make entity
calloutEntity->addComponent( calloutRenderer );
calloutEntity->addComponent( mat );
calloutEntity->setParent( billboardEntity );
}
// fix the vertical range of the node from the estimated vertical range to the true range
if ( mZMin != std::numeric_limits<float>::max() && mZMax != std::numeric_limits<float>::lowest() )
{
QgsBox3D box = mNode->box3D();
box.setZMinimum( mZMin );
box.setZMaximum( mZMax );
mNode->setExactBox3D( box );
mNode->updateParentBoundingBoxesRecursively();
}
return entity;
}
///////////////
QgsAnnotationLayerChunkLoaderFactory::QgsAnnotationLayerChunkLoaderFactory( const Qgs3DRenderContext &context, QgsAnnotationLayer *layer, int leafLevel, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax )
: mRenderContext( context )
, mLayer( layer )
, mLeafLevel( leafLevel )
, mClamping( clamping )
, mZOffset( zOffset )
, mShowCallouts( showCallouts )
, mCalloutLineColor( calloutLineColor )
, mCalloutLineWidth( calloutLineWidth )
{
if ( context.crs().type() == Qgis::CrsType::Geocentric )
{
// TODO: add support for handling of annotation layers
// (we're using dummy quadtree here to make sure the empty extent does not break the scene completely)
QgsDebugError( QStringLiteral( "Annotation layers in globe scenes are not supported yet!" ) );
setupQuadtree( QgsBox3D( -1e7, -1e7, -1e7, 1e7, 1e7, 1e7 ), -1, leafLevel );
return;
}
QgsBox3D rootBox3D( context.extent(), zMin, zMax );
// add small padding to avoid clipping of point features located at the edge of the bounding box
rootBox3D.grow( 1.0 );
setupQuadtree( rootBox3D, -1, leafLevel ); // negative root error means that the node does not contain anything
}
QgsChunkLoader *QgsAnnotationLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
{
return new QgsAnnotationLayerChunkLoader( this, node );
}
///////////////
QgsAnnotationLayerChunkedEntity::QgsAnnotationLayerChunkedEntity( Qgs3DMapSettings *map, QgsAnnotationLayer *layer, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax )
: QgsChunkedEntity( map,
-1, // max. allowed screen error (negative tau means that we need to go until leaves are reached)
new QgsAnnotationLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), layer, 3, clamping, zOffset, showCallouts, calloutLineColor, calloutLineWidth, zMin, zMax ), true )
{
mTransform = new Qt3DCore::QTransform;
if ( applyTerrainOffset() )
{
mTransform->setTranslation( QVector3D( 0.0f, 0.0f, static_cast<float>( map->terrainSettings()->elevationOffset() ) ) );
}
this->addComponent( mTransform );
connect( map, &Qgs3DMapSettings::terrainSettingsChanged, this, &QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged );
}
QgsAnnotationLayerChunkedEntity::~QgsAnnotationLayerChunkedEntity()
{
// cancel / wait for jobs
cancelActiveJobs();
}
// if the AltitudeClamping is `Absolute`, do not apply the offset
bool QgsAnnotationLayerChunkedEntity::applyTerrainOffset() const
{
if ( auto loaderFactory = static_cast<QgsAnnotationLayerChunkLoaderFactory *>( mChunkLoaderFactory ) )
{
return loaderFactory->mClamping != Qgis::AltitudeClamping::Absolute;
}
return true;
}
void QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged()
{
QgsDebugMsgLevel( QStringLiteral( "QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged" ), 2 );
float newOffset = static_cast<float>( qobject_cast<Qgs3DMapSettings *>( sender() )->terrainSettings()->elevationOffset() );
if ( !applyTerrainOffset() )
{
newOffset = 0.0;
}
mTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
}
/// @endcond

View File

@ -0,0 +1,146 @@
/***************************************************************************
qgsannotationlayerchunkloader_p.h
--------------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSANNOTATIONLAYERCHUNKLOADER_P_H
#define QGSANNOTATIONLAYERCHUNKLOADER_P_H
///@cond PRIVATE
//
// W A R N I N G
// -------------
//
// This file is not part of the QGIS API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
#include "qgschunkloader.h"
#include "qgschunkedentity.h"
#include "qgs3drendercontext.h"
#include "qgsbillboardgeometry.h"
#include <QImage>
#define SIP_NO_FILE
class QgsAnnotationLayer;
class QgsAnnotationItem;
namespace Qt3DCore
{
class QTransform;
}
#include <QFutureWatcher>
/**
* \ingroup qgis_3d
* \brief This loader factory is responsible for creation of loaders of QgsAnnotationLayerChunkedEntity.
*
* \since QGIS 4.0
*/
class QgsAnnotationLayerChunkLoaderFactory : public QgsQuadtreeChunkLoaderFactory
{
Q_OBJECT
public:
//! Constructs the factory
QgsAnnotationLayerChunkLoaderFactory( const Qgs3DRenderContext &context, QgsAnnotationLayer *layer, int leafLevel, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax );
//! Creates loader for the given chunk node. Ownership of the returned is passed to the caller.
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override;
Qgs3DRenderContext mRenderContext;
QgsAnnotationLayer *mLayer = nullptr;
int mLeafLevel = 0;
Qgis::AltitudeClamping mClamping = Qgis::AltitudeClamping::Relative;
double mZOffset = 0;
bool mShowCallouts = false;
QColor mCalloutLineColor;
double mCalloutLineWidth = 2;
};
/**
* \ingroup qgis_3d
* \brief This loader class is responsible for async loading of data for QgsAnnotationLayerChunkedEntity
* and creation of final 3D entity from the data previously prepared in a worker thread.
*
* \since QGIS 4.0
*/
class QgsAnnotationLayerChunkLoader : public QgsChunkLoader
{
Q_OBJECT
public:
//! Constructs the loader
QgsAnnotationLayerChunkLoader( const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node );
~QgsAnnotationLayerChunkLoader() override;
void start() override;
virtual void cancel() override;
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent ) override;
private:
const QgsAnnotationLayerChunkLoaderFactory *mFactory = nullptr;
Qgs3DRenderContext mRenderContext;
bool mCanceled = false;
QFutureWatcher<void> *mFutureWatcher = nullptr;
QString mLayerName;
QgsVector3D mChunkOrigin;
std::vector< std::unique_ptr< QgsAnnotationItem > > mItemsToRender;
QVector< QgsBillboardGeometry::BillboardAtlasData > mBillboardPositions;
QVector< QgsLineString > mCalloutLines;
QImage mBillboardAtlas;
double mZMin = std::numeric_limits< double >::max();
double mZMax = std::numeric_limits< double >::lowest();
};
/**
* \ingroup qgis_3d
* \brief 3D entity used for rendering of annotation layers.
*
* Internally it uses QgsAnnotationLayerChunkLoaderFactory and
* QgsAnnotationLayerChunkLoader to do the actual work
* of loading and creating 3D sub-entities for the layer.
*
* \since QGIS 4.0
*/
class QgsAnnotationLayerChunkedEntity : public QgsChunkedEntity
{
Q_OBJECT
public:
//! Constructs the entity.
explicit QgsAnnotationLayerChunkedEntity( Qgs3DMapSettings *map, QgsAnnotationLayer *layer, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax );
~QgsAnnotationLayerChunkedEntity();
private slots:
void onTerrainElevationOffsetChanged();
private:
Qt3DCore::QTransform *mTransform = nullptr;
bool applyTerrainOffset() const;
friend class TestQgsChunkedEntity;
};
/// @endcond
#endif // QGSANNOTATIONLAYERCHUNKLOADER_P_H

View File

@ -296,7 +296,7 @@ QgsPointCloudLayer *QgsPdalAlgorithmBase::parameterAsPointCloudLayer( const QVar
return nullptr;
// if COPC provider, return as it is
if ( layer->dataProvider()->name() == QStringLiteral( "copc" ) )
if ( layer->dataProvider()->name() == QLatin1String( "copc" ) )
{
return layer;
}

View File

@ -94,7 +94,7 @@ void QgsGeometryCheckAngleAlgorithm::initAlgorithm( const QVariantMap &configura
QStringLiteral( "ERRORS" ), QObject::tr( "Small angle errors" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -97,7 +97,7 @@ void QgsGeometryCheckAreaAlgorithm::initAlgorithm( const QVariantMap &configurat
QStringLiteral( "OUTPUT" ), QObject::tr( "Small polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -98,7 +98,7 @@ void QgsGeometryCheckContainedAlgorithm::initAlgorithm( const QVariantMap &confi
QStringLiteral( "OUTPUT" ), QObject::tr( "Contained features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);

View File

@ -88,7 +88,7 @@ void QgsGeometryCheckDangleAlgorithm::initAlgorithm( const QVariantMap &configur
QStringLiteral( "OUTPUT" ), QObject::tr( "Dangle-end features" ), Qgis::ProcessingSourceType::VectorLine, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -87,7 +87,7 @@ void QgsGeometryCheckDegeneratePolygonAlgorithm::initAlgorithm( const QVariantMa
QStringLiteral( "OUTPUT" ), QObject::tr( "Degenerate polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 );
auto tolerance = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
tolerance->setHelp( QObject::tr( "The \"Tolerance\" advanced parameter defines the numerical precision of geometric operations, "
"given as an integer n, meaning that any difference smaller than 10⁻ⁿ (in map units) is considered zero." ) );

View File

@ -94,7 +94,7 @@ void QgsGeometryCheckDuplicateAlgorithm::initAlgorithm( const QVariantMap &confi
QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicate geometries" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -89,7 +89,7 @@ void QgsGeometryCheckDuplicateNodesAlgorithm::initAlgorithm( const QVariantMap &
QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicated vertices features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -92,7 +92,7 @@ void QgsGeometryCheckFollowBoundariesAlgorithm::initAlgorithm( const QVariantMap
QStringLiteral( "REF_LAYER" ), QObject::tr( "Reference layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -108,7 +108,7 @@ void QgsGeometryCheckGapAlgorithm::initAlgorithm( const QVariantMap &configurati
QStringLiteral( "OUTPUT" ), QObject::tr( "Gap features" ), Qgis::ProcessingSourceType::VectorPolygon
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -94,7 +94,7 @@ void QgsGeometryCheckHoleAlgorithm::initAlgorithm( const QVariantMap &configurat
QStringLiteral( "OUTPUT" ), QObject::tr( "Polygons with holes" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -88,7 +88,7 @@ void QgsGeometryCheckLineIntersectionAlgorithm::initAlgorithm( const QVariantMap
QStringLiteral( "OUTPUT" ), QObject::tr( "Intersecting feature" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -94,7 +94,7 @@ void QgsGeometryCheckLineLayerIntersectionAlgorithm::initAlgorithm( const QVaria
QStringLiteral( "OUTPUT" ), QObject::tr( "Line intersecting other layer features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -95,7 +95,7 @@ void QgsGeometryCheckMissingVertexAlgorithm::initAlgorithm( const QVariantMap &c
QStringLiteral( "OUTPUT" ), QObject::tr( "Missing vertices features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -93,7 +93,7 @@ void QgsGeometryCheckMultipartAlgorithm::initAlgorithm( const QVariantMap &confi
QStringLiteral( "OUTPUT" ), QObject::tr( "One-part geometry features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -92,7 +92,7 @@ void QgsGeometryCheckOverlapAlgorithm::initAlgorithm( const QVariantMap &configu
QStringLiteral( "MIN_OVERLAP_AREA" ), QObject::tr( "Minimum overlap area" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -88,7 +88,7 @@ void QgsGeometryCheckPointCoveredByLineAlgorithm::initAlgorithm( const QVariantM
QStringLiteral( "ERRORS" ), QObject::tr( "Points not covered by a line" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -88,7 +88,7 @@ void QgsGeometryCheckPointInPolygonAlgorithm::initAlgorithm( const QVariantMap &
QStringLiteral( "ERRORS" ), QObject::tr( "Points outside polygons errors" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -93,7 +93,7 @@ void QgsGeometryCheckSegmentLengthAlgorithm::initAlgorithm( const QVariantMap &c
QStringLiteral( "MIN_SEGMENT_LENGTH" ), QObject::tr( "Minimum segment length" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -89,7 +89,7 @@ void QgsGeometryCheckSelfContactAlgorithm::initAlgorithm( const QVariantMap &con
QStringLiteral( "OUTPUT" ), QObject::tr( "Self contact features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -91,7 +91,7 @@ void QgsGeometryCheckSelfIntersectionAlgorithm::initAlgorithm( const QVariantMap
QStringLiteral( "OUTPUT" ), QObject::tr( "Self-intersecting features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -100,7 +100,7 @@ void QgsGeometryCheckSliverPolygonAlgorithm::initAlgorithm( const QVariantMap &c
QStringLiteral( "MAX_AREA" ), QObject::tr( "Maximum area (map units squared)" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -196,11 +196,11 @@ QVariantMap QgsClimbAlgorithm::processAlgorithm( const QVariantMap &parameters,
if ( !noGeometry.empty() )
{
feedback->pushInfo( QObject::tr( "The following features do not have geometry: %1" ).arg( noGeometry.join( QStringLiteral( ", " ) ) ) );
feedback->pushInfo( QObject::tr( "The following features do not have geometry: %1" ).arg( noGeometry.join( QLatin1String( ", " ) ) ) );
}
if ( !noZValue.empty() )
{
feedback->pushInfo( QObject::tr( "The following points do not have Z value: %1" ).arg( noZValue.join( QStringLiteral( ", " ) ) ) );
feedback->pushInfo( QObject::tr( "The following points do not have Z value: %1" ).arg( noZValue.join( QLatin1String( ", " ) ) ) );
}
QVariantMap results;

View File

@ -107,7 +107,7 @@ void QgsFixGeometryAngleAlgorithm::initAlgorithm( const QVariantMap &configurati
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing small angles" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -120,7 +120,7 @@ void QgsFixGeometryAreaAlgorithm::initAlgorithm( const QVariantMap &configuratio
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from merging small polygons" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -104,7 +104,7 @@ void QgsFixGeometryDuplicateNodesAlgorithm::initAlgorithm( const QVariantMap &co
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing duplicate vertices" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -108,7 +108,7 @@ void QgsFixGeometryGapAlgorithm::initAlgorithm( const QVariantMap &configuration
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing gaps" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -104,7 +104,7 @@ void QgsFixGeometryHoleAlgorithm::initAlgorithm( const QVariantMap &configuratio
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing holes" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -105,7 +105,7 @@ void QgsFixGeometryMissingVertexAlgorithm::initAlgorithm( const QVariantMap &con
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing border vertices" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -91,7 +91,7 @@ void QgsFixGeometryMultipartAlgorithm::initAlgorithm( const QVariantMap &configu
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing multiparts" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -99,7 +99,7 @@ void QgsFixGeometryOverlapAlgorithm::initAlgorithm( const QVariantMap &configura
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing overlaps" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -129,7 +129,7 @@ void QgsFixGeometrySelfIntersectionAlgorithm::initAlgorithm( const QVariantMap &
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing self-intersections" ), Qgis::ProcessingSourceType::VectorPoint
) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
);
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -90,7 +90,7 @@ Qgis::ProcessingFeatureSourceFlags QgsRemovePartsByAreaAlgorithm::sourceFlags()
void QgsRemovePartsByAreaAlgorithm::initParameters( const QVariantMap & )
{
std::unique_ptr< QgsProcessingParameterArea > minArea = std::make_unique< QgsProcessingParameterArea >( QStringLiteral( "MIN_AREA" ), QObject::tr( "Remove parts with area less than" ), 0.0, QStringLiteral( "INPUT" ), false, 0 );
auto minArea = std::make_unique< QgsProcessingParameterArea >( QStringLiteral( "MIN_AREA" ), QObject::tr( "Remove parts with area less than" ), 0.0, QStringLiteral( "INPUT" ), false, 0 );
minArea->setIsDynamic( true );
minArea->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "MIN_AREA" ), QObject::tr( "Remove parts with area less than" ), QgsPropertyDefinition::DoublePositive ) );
minArea->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );

View File

@ -90,7 +90,7 @@ Qgis::ProcessingFeatureSourceFlags QgsRemovePartsByLengthAlgorithm::sourceFlags(
void QgsRemovePartsByLengthAlgorithm::initParameters( const QVariantMap & )
{
std::unique_ptr< QgsProcessingParameterDistance > minLength = std::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "MIN_LENGTH" ), QObject::tr( "Remove parts with lengths less than" ), 0.0, QStringLiteral( "INPUT" ), false, 0 );
auto minLength = std::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "MIN_LENGTH" ), QObject::tr( "Remove parts with lengths less than" ), 0.0, QStringLiteral( "INPUT" ), false, 0 );
minLength->setIsDynamic( true );
minLength->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "MIN_LENGTH" ), QObject::tr( "Remove parts with length less than" ), QgsPropertyDefinition::DoublePositive ) );
minLength->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );

View File

@ -0,0 +1,199 @@
/***************************************************************************
qgsannotationlayer3drendererwidget.cpp
------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsannotationlayer3drendererwidget.h"
#include "moc_qgsannotationlayer3drendererwidget.cpp"
#include "qgsapplication.h"
#include "qgsannotationlayer.h"
#include "qgsannotationlayer3drenderer.h"
#include <QBoxLayout>
#include <QCheckBox>
QgsAnnotationLayer3DRendererWidget::QgsAnnotationLayer3DRendererWidget( QgsAnnotationLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
: QgsMapLayerConfigWidget( layer, canvas, parent )
{
setPanelTitle( tr( "3D View" ) );
setObjectName( QStringLiteral( "mOptsPage_3DView" ) );
setupUi( this );
mComboRendererType->addItem( QgsApplication::getThemeIcon( QStringLiteral( "rendererNullSymbol.svg" ) ), tr( "No Symbols" ), QVariant::fromValue( RendererType::None ) );
mComboRendererType->addItem( QgsApplication::getThemeIcon( QStringLiteral( "rendererSingleSymbol.svg" ) ), tr( "3D Billboards" ), QVariant::fromValue( RendererType::Billboards ) );
mComboRendererType->setCurrentIndex( mComboRendererType->findData( QVariant::fromValue( RendererType::None ) ) );
mStackedWidget->setCurrentWidget( mPageNoRenderer );
connect( mComboRendererType, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsAnnotationLayer3DRendererWidget::rendererTypeChanged );
mComboClamping->addItem( tr( "Relative to Terrain" ), QVariant::fromValue( Qgis::AltitudeClamping::Relative ) );
mComboClamping->addItem( tr( "Absolute" ), QVariant::fromValue( Qgis::AltitudeClamping::Absolute ) );
connect( mComboClamping, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAnnotationLayer3DRendererWidget::clampingChanged );
mComboClamping->setCurrentIndex( mComboClamping->findData( QVariant::fromValue( Qgis::AltitudeClamping::Relative ) ) );
clampingChanged();
connect( mOffsetZSpinBox, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [this]( double ) {
if ( !mBlockChanges )
emit widgetChanged();
} );
connect( mCheckShowCallouts, &QCheckBox::toggled, this, [this] {
if ( !mBlockChanges )
emit widgetChanged();
} );
syncToLayer( layer );
}
void QgsAnnotationLayer3DRendererWidget::setRenderer( const QgsAnnotationLayer3DRenderer *renderer )
{
mBlockChanges++;
mRenderer.reset( renderer ? renderer->clone() : nullptr );
if ( renderer )
{
mComboRendererType->setCurrentIndex( mComboRendererType->findData( QVariant::fromValue( RendererType::Billboards ) ) );
mComboClamping->setCurrentIndex( mComboClamping->findData( QVariant::fromValue( renderer->altitudeClamping() ) ) );
mOffsetZSpinBox->setValue( renderer->zOffset() );
mCheckShowCallouts->setChecked( renderer->showCalloutLines() );
}
else
{
mComboRendererType->setCurrentIndex( mComboRendererType->findData( QVariant::fromValue( RendererType::None ) ) );
}
mBlockChanges--;
}
std::unique_ptr< QgsAnnotationLayer3DRenderer > QgsAnnotationLayer3DRendererWidget::renderer()
{
const RendererType type = mComboRendererType->currentData().value< RendererType >();
switch ( type )
{
case RendererType::None:
return nullptr;
break;
case RendererType::Billboards:
{
auto renderer = std::make_unique< QgsAnnotationLayer3DRenderer >();
renderer->setAltitudeClamping( mComboClamping->currentData().value< Qgis::AltitudeClamping >() );
renderer->setZOffset( mOffsetZSpinBox->value() );
renderer->setShowCalloutLines( mCheckShowCallouts->isChecked() );
return renderer;
}
}
BUILTIN_UNREACHABLE
}
void QgsAnnotationLayer3DRendererWidget::apply()
{
std::unique_ptr< QgsAnnotationLayer3DRenderer > r = renderer();
mLayer->setRenderer3D( r.release() );
}
void QgsAnnotationLayer3DRendererWidget::rendererTypeChanged()
{
const RendererType type = mComboRendererType->currentData().value< RendererType >();
switch ( type )
{
case RendererType::None:
mStackedWidget->setCurrentWidget( mPageNoRenderer );
break;
case RendererType::Billboards:
mStackedWidget->setCurrentWidget( mPageBillboards );
break;
}
if ( !mBlockChanges )
emit widgetChanged();
}
void QgsAnnotationLayer3DRendererWidget::clampingChanged()
{
switch ( mComboClamping->currentData().value< Qgis::AltitudeClamping >() )
{
case Qgis::AltitudeClamping::Absolute:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg( tr( "All billboards will be placed at the same elevation." ), tr( "The terrain height will be ignored." ) )
);
break;
case Qgis::AltitudeClamping::Relative:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p>" ).arg( tr( "Billboard elevation is relative to terrain height." ) )
);
break;
case Qgis::AltitudeClamping::Terrain:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg( tr( "Billboard elevation will be taken directly from the terrain height." ), tr( "Billboards will be placed directly on the terrain." ) )
);
break;
}
if ( !mBlockChanges )
emit widgetChanged();
}
void QgsAnnotationLayer3DRendererWidget::syncToLayer( QgsMapLayer *layer )
{
mLayer = layer;
QgsAbstract3DRenderer *r = layer->renderer3D();
if ( r && r->type() == QLatin1String( "annotation" ) )
{
QgsAnnotationLayer3DRenderer *annotationRenderer = qgis::down_cast<QgsAnnotationLayer3DRenderer *>( r );
setRenderer( annotationRenderer );
}
else
{
setRenderer( nullptr );
}
}
//
// QgsAnnotationLayer3DRendererWidgetFactory
//
QgsAnnotationLayer3DRendererWidgetFactory::QgsAnnotationLayer3DRendererWidgetFactory( QObject *parent )
: QObject( parent )
{
setIcon( QIcon( ":/images/themes/default/3d.svg" ) );
setTitle( tr( "3D View" ) );
}
QgsMapLayerConfigWidget *QgsAnnotationLayer3DRendererWidgetFactory::createWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, bool dockWidget, QWidget *parent ) const
{
Q_UNUSED( dockWidget )
QgsAnnotationLayer *annotationLayer = qobject_cast<QgsAnnotationLayer *>( layer );
if ( !annotationLayer )
return nullptr;
return new QgsAnnotationLayer3DRendererWidget( annotationLayer, canvas, parent );
}
bool QgsAnnotationLayer3DRendererWidgetFactory::supportLayerPropertiesDialog() const
{
return true;
}
bool QgsAnnotationLayer3DRendererWidgetFactory::supportsStyleDock() const
{
return true;
}
bool QgsAnnotationLayer3DRendererWidgetFactory::supportsLayer( QgsMapLayer *layer ) const
{
return layer->type() == Qgis::LayerType::Annotation;
}
QString QgsAnnotationLayer3DRendererWidgetFactory::layerPropertiesPagePositionHint() const
{
return QStringLiteral( "mOptsPage_Rendering" );
}

View File

@ -0,0 +1,75 @@
/***************************************************************************
qgsannotationlayer3drendererwidget.h
------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSANNOTATIONLAYER3DRENDERERWIDGET_H
#define QGSANNOTATIONLAYER3DRENDERERWIDGET_H
#include <memory>
#include "qgsmaplayerconfigwidget.h"
#include "qgsmaplayerconfigwidgetfactory.h"
#include "ui_qgsannotationlayer3drendererwidget.h"
class QgsAnnotationLayer;
class QgsAnnotationLayer3DRenderer;
class QgsMapCanvas;
//! Widget for configuration of 3D renderer of an annotation layer
class QgsAnnotationLayer3DRendererWidget : public QgsMapLayerConfigWidget, private Ui::QgsAnnotationLayer3dRendererWidgetBase
{
Q_OBJECT
public:
enum class RendererType
{
None,
Billboards,
};
Q_ENUM( RendererType )
explicit QgsAnnotationLayer3DRendererWidget( QgsAnnotationLayer *layer, QgsMapCanvas *canvas, QWidget *parent = nullptr );
void syncToLayer( QgsMapLayer *layer ) final;
//! no transfer of ownership
void setRenderer( const QgsAnnotationLayer3DRenderer *renderer );
std::unique_ptr< QgsAnnotationLayer3DRenderer > renderer();
public slots:
void apply() override;
private slots:
void rendererTypeChanged();
void clampingChanged();
private:
int mBlockChanges = 0;
std::unique_ptr<QgsAnnotationLayer3DRenderer> mRenderer;
};
class QgsAnnotationLayer3DRendererWidgetFactory : public QObject, public QgsMapLayerConfigWidgetFactory
{
Q_OBJECT
public:
explicit QgsAnnotationLayer3DRendererWidgetFactory( QObject *parent = nullptr );
QgsMapLayerConfigWidget *createWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, bool dockWidget, QWidget *parent ) const override;
bool supportLayerPropertiesDialog() const override;
bool supportsStyleDock() const override;
bool supportsLayer( QgsMapLayer *layer ) const override;
QString layerPropertiesPagePositionHint() const override;
};
#endif // QGSANNOTATIONLAYER3DRENDERERWIDGET_H

View File

@ -168,11 +168,6 @@ set(QGIS_APP_SRCS
browser/qgsinbuiltdataitemproviders.cpp
devtools/qgsappdevtoolutils.cpp
devtools/qgsdevtoolsmodelnode.cpp
devtools/networklogger/qgsnetworklogger.cpp
devtools/networklogger/qgsnetworkloggernode.cpp
devtools/networklogger/qgsnetworkloggerpanelwidget.cpp
devtools/networklogger/qgsnetworkloggerwidgetfactory.cpp
devtools/profiler/qgsprofilerpanelwidget.cpp
devtools/profiler/qgsprofilerwidgetfactory.cpp
devtools/documentation/qgsdocumentationpanelwidget.cpp
@ -358,6 +353,7 @@ if (WITH_3D)
3d/qgs3dmodelsourcelineedit.cpp
3d/qgs3dnavigationwidget.cpp
3d/qgs3doptions.cpp
3d/qgsannotationlayer3drendererwidget.cpp
3d/qgsgoochmaterialwidget.cpp
3d/qgslightswidget.cpp
3d/qgsline3dsymbolwidget.cpp
@ -507,7 +503,6 @@ target_include_directories(qgis_app PUBLIC
${CMAKE_SOURCE_DIR}/src/app
${CMAKE_SOURCE_DIR}/src/app/decorations
${CMAKE_SOURCE_DIR}/src/app/devtools
${CMAKE_SOURCE_DIR}/src/app/devtools/networklogger
${CMAKE_SOURCE_DIR}/src/app/georeferencer
${CMAKE_SOURCE_DIR}/src/app/labeling
${CMAKE_SOURCE_DIR}/src/app/layout

View File

@ -141,7 +141,7 @@ void QgsAppQueryLogger::removeRequestRows( const QList<int> &rows )
for ( int row : std::as_const( res ) )
{
int popId = data( index( row, 0, QModelIndex() ), QgsDevToolsModelNode::RoleId ).toInt();
int popId = data( index( row, 0, QModelIndex() ), static_cast<int>( Qgis::DevToolsNodeRole::Id ) ).toInt();
mQueryGroups.remove( popId );
beginRemoveRows( QModelIndex(), row, row );
@ -217,11 +217,11 @@ QVariant QgsAppQueryLogger::data( const QModelIndex &index, int role ) const
switch ( role )
{
case Qt::DisplayRole:
case QgsDevToolsModelNode::RoleElapsedTime:
case QgsDevToolsModelNode::RoleSort:
return node->data( QgsDevToolsModelNode::RoleElapsedTime );
case static_cast<int>( Qgis::DevToolsNodeRole::ElapsedTime ):
case static_cast<int>( Qgis::DevToolsNodeRole::Sort ):
return node->data( static_cast<int>( Qgis::DevToolsNodeRole::ElapsedTime ) );
case QgsDevToolsModelNode::RoleMaximumTime:
case static_cast<int>( Qgis::DevToolsNodeRole::MaximumTime ):
return mMaxCost;
default:

View File

@ -88,7 +88,7 @@ QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const
case Qt::DisplayRole:
return QStringLiteral( "%1 %2" ).arg( QString::number( mQueryId ), mSql );
case QgsDevToolsModelNode::RoleSort:
case static_cast<int>( Qgis::DevToolsNodeRole::Sort ):
return mQueryId;
case Qt::ToolTipRole:
@ -118,13 +118,13 @@ QVariant QgsDatabaseQueryLoggerQueryGroup::data( int role ) const
#endif
}
case RoleStatus:
case static_cast<int>( Qgis::DevToolsNodeRole::Status ):
return static_cast<int>( mStatus );
case RoleId:
case static_cast<int>( Qgis::DevToolsNodeRole::Id ):
return mQueryId;
case RoleElapsedTime:
case static_cast<int>( Qgis::DevToolsNodeRole::ElapsedTime ):
return mElapsed;
case Qt::ForegroundRole:

View File

@ -48,7 +48,7 @@ QgsDatabaseQueryLoggerTreeView::QgsDatabaseQueryLoggerTreeView( QgsAppQueryLogge
setFont( QFontDatabase::systemFont( QFontDatabase::FixedFont ) );
mProxyModel = new QgsDatabaseQueryLoggerProxyModel( mLogger, this );
mProxyModel->setSortRole( QgsDevToolsModelNode::RoleSort );
mProxyModel->setSortRole( static_cast<int>( Qgis::DevToolsNodeRole::Sort ) );
setModel( mProxyModel );
connect( mProxyModel, &QAbstractItemModel::rowsInserted, this, [this]( const QModelIndex &parent, int first, int last ) {
@ -168,7 +168,7 @@ QgsDatabaseQueryLoggerPanelWidget::QgsDatabaseQueryLoggerPanelWidget( QgsAppQuer
setupUi( this );
mTreeView = new QgsDatabaseQueryLoggerTreeView( mLogger );
mTreeView->setItemDelegateForColumn( 1, new QueryCostDelegate( QgsDevToolsModelNode::RoleElapsedTime, QgsDevToolsModelNode::RoleMaximumTime, mTreeView ) );
mTreeView->setItemDelegateForColumn( 1, new QueryCostDelegate( static_cast<int>( Qgis::DevToolsNodeRole::ElapsedTime ), static_cast<int>( Qgis::DevToolsNodeRole::MaximumTime ), mTreeView ) );
mTreeView->setSortingEnabled( true );
mTreeView->sortByColumn( 0, Qt::SortOrder::AscendingOrder );

View File

@ -327,11 +327,14 @@ void QgsAppLayerHandling::postProcessAddedLayers( const QList<QgsMapLayer *> &la
}
break;
}
case Qgis::LayerType::Annotation:
postProcessAddedLayer( layer );
break;
case Qgis::LayerType::Raster:
case Qgis::LayerType::Plugin:
case Qgis::LayerType::Mesh:
case Qgis::LayerType::VectorTile:
case Qgis::LayerType::Annotation:
case Qgis::LayerType::PointCloud:
case Qgis::LayerType::Group:
case Qgis::LayerType::TiledScene:

View File

@ -156,6 +156,7 @@
#include "qgsmeshlayer3drendererwidget.h"
#include "qgspointcloudlayer3drendererwidget.h"
#include "qgstiledscenelayer3drendererwidget.h"
#include "qgsannotationlayer3drendererwidget.h"
#include "qgs3dapputils.h"
#include "qgs3doptions.h"
#include "qgsmapviewsmanager.h"
@ -450,10 +451,10 @@
#include "qgsbearingnumericformat.h"
#include "qgsprojectdisplaysettings.h"
#include "qgstemporalcontrollerdockwidget.h"
#include "qgsnetworklogger.h"
#include "qgsuserprofilemanager.h"
#include "qgsuserprofile.h"
#include "qgsnetworkloggerwidgetfactory.h"
#include "devtools/networklogger/qgsnetworklogger.h"
#include "devtools/networklogger/qgsnetworkloggerwidgetfactory.h"
#include "devtools/querylogger/qgsappquerylogger.h"
#include "devtools/querylogger/qgsqueryloggerwidgetfactory.h"
#include "devtools/profiler/qgsprofilerwidgetfactory.h"
@ -1329,12 +1330,6 @@ QgisApp::QgisApp( QSplashScreen *splash, AppOptions options, const QString &root
registerMapLayerPropertiesFactory( new QgsVectorLayerDigitizingPropertiesFactory( this ) );
registerMapLayerPropertiesFactory( new QgsPointCloudRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsTiledSceneRendererWidgetFactory( this ) );
#ifdef HAVE_3D
registerMapLayerPropertiesFactory( new QgsVectorLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsMeshLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsPointCloudLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsTiledSceneLayer3DRendererWidgetFactory( this ) );
#endif
registerMapLayerPropertiesFactory( new QgsPointCloudElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsRasterElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsVectorElevationPropertiesWidgetFactory( this ) );
@ -1342,6 +1337,13 @@ QgisApp::QgisApp( QSplashScreen *splash, AppOptions options, const QString &root
registerMapLayerPropertiesFactory( new QgsMeshElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsAnnotationItemPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsLayerTreeGroupPropertiesWidgetFactory( this ) );
#ifdef HAVE_3D
registerMapLayerPropertiesFactory( new QgsVectorLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsMeshLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsPointCloudLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsTiledSceneLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsAnnotationLayer3DRendererWidgetFactory( this ) );
#endif
mMapStyleWidget = new QgsLayerStylingWidget( mMapCanvas, mInfoBar, mMapLayerPanelFactories );
mMapStylingDock->setWidget( mMapStyleWidget );
@ -3247,7 +3249,7 @@ void QgisApp::showStyleManager()
void QgisApp::initPythonConsoleOptions()
{
QgsPythonRunner::run( QStringLiteral( "import console" ) );
QgsPythonRunner::run( QStringLiteral( "console.init_options_widget()" ) );
QgsPythonRunner::run( QStringLiteral( "console.init_console()" ) );
}
void QgisApp::showPythonDialog()
@ -7953,6 +7955,7 @@ void QgisApp::createAnnotationLayer()
// layer should be created at top of layer tree
QgsProject::instance()->addMapLayer( layer, false );
QgsProject::instance()->layerTreeRoot()->insertLayer( 0, layer );
QgsAppLayerHandling::postProcessAddedLayers( { layer } );
}
void QgisApp::setCadDockVisible( bool visible )

View File

@ -93,6 +93,7 @@ QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, co
connect( mOutputHeightSpinBox, &QSpinBox::editingFinished, this, [this] { updateOutputHeight( mOutputHeightSpinBox->value() ); } );
connect( mExtentGroupBox, &QgsExtentGroupBox::extentChanged, this, &QgsMapSaveDialog::updateExtent );
connect( mScaleWidget, &QgsScaleWidget::scaleChanged, this, &QgsMapSaveDialog::updateScale );
connect( mLockScale, &QToolButton::toggled, this, &QgsMapSaveDialog::lockScaleChanged );
connect( mLockAspectRatio, &QgsRatioLockButton::lockChanged, this, &QgsMapSaveDialog::lockChanged );
updateOutputSize();
@ -222,23 +223,38 @@ void QgsMapSaveDialog::updateExtent( const QgsRectangle &extent )
{
int currentDpi = 0;
// reset scale to properly sync output width and height when extent set using
// current map view, layer extent, or drawn on canvas buttons
// If extent set using current map view, layer extent, or drawn on canvas buttons
if ( mExtentGroupBox->extentState() != QgsExtentGroupBox::UserExtent )
{
currentDpi = mDpi;
QgsMapSettings ms = mMapCanvas->mapSettings();
ms.setRotation( 0 );
mDpi = static_cast<int>( std::round( ms.outputDpi() ) );
mSize.setWidth( ms.outputSize().width() * extent.width() / ms.visibleExtent().width() );
mSize.setHeight( ms.outputSize().height() * extent.height() / ms.visibleExtent().height() );
whileBlocking( mScaleWidget )->setScale( ms.scale() );
if ( currentDpi != mDpi )
// reset scale to properly sync output width and height
if ( !mLockScale->isChecked() )
{
updateDpi( currentDpi );
currentDpi = mDpi;
QgsMapSettings ms = mMapCanvas->mapSettings();
ms.setRotation( 0 );
mDpi = static_cast<int>( std::round( ms.outputDpi() ) );
mSize.setWidth( ms.outputSize().width() * extent.width() / ms.visibleExtent().width() );
mSize.setHeight( ms.outputSize().height() * extent.height() / ms.visibleExtent().height() );
whileBlocking( mScaleWidget )->setScale( ms.scale() );
if ( currentDpi != mDpi )
{
updateDpi( currentDpi );
}
}
else // Update size, leave scale untouched
{
QgsScaleCalculator calculator;
calculator.setMapUnits( mExtentGroupBox->currentCrs().mapUnits() );
calculator.setDpi( mDpi );
calculator.setMethod( QgsProject::instance()->scaleMethod() );
QSizeF newSize = calculator.calculateImageSize( extent, mScaleWidget->scale() );
mSize.setWidth( static_cast<int>( newSize.width() ) );
mSize.setHeight( static_cast<int>( newSize.height() ) );
}
}
else
@ -246,6 +262,7 @@ void QgsMapSaveDialog::updateExtent( const QgsRectangle &extent )
mSize.setWidth( mSize.width() * extent.width() / mExtent.width() );
mSize.setHeight( mSize.height() * extent.height() / mExtent.height() );
}
updateOutputSize();
checkOutputSize();
@ -391,6 +408,11 @@ void QgsMapSaveDialog::lockChanged( const bool locked )
}
}
void QgsMapSaveDialog::lockScaleChanged( bool locked )
{
mScaleWidget->setEnabled( !locked );
}
void QgsMapSaveDialog::copyToClipboard()
{
QgsMapSettings ms = QgsMapSettings();

View File

@ -82,8 +82,9 @@ class APP_EXPORT QgsMapSaveDialog : public QDialog, private Ui::QgsMapSaveDialog
private slots:
void onAccepted();
void updatePdfExportWarning();
void lockScaleChanged( bool locked );
void showHelp();
private:
void lockChanged( bool locked );
@ -109,9 +110,7 @@ class APP_EXPORT QgsMapSaveDialog : public QDialog, private Ui::QgsMapSaveDialog
QString mInfoDetails;
private slots:
void showHelp();
friend class TestQgsMapSaveDialog;
};
#endif // QGSMAPSAVEDIALOG_H

View File

@ -157,7 +157,7 @@ void QgsAuthPlanetaryComputerEdit::updateServerType( int indx )
lblTenantId->setVisible( isPro );
leTenantId->setVisible( isPro );
const QString openHelp = tr( "Use this server type for %1 - the data are publicly accessible and do not require an account." ).arg( QStringLiteral( "<a href=\"https://planetarycomputer.microsoft.com/\">https://planetarycomputer.microsoft.com/</a>" ) );
const QString openHelp = tr( "Use this server type for %1 - the data are publicly accessible and do not require an account." ).arg( QLatin1String( "<a href=\"https://planetarycomputer.microsoft.com/\">https://planetarycomputer.microsoft.com/</a>" ) );
const QString proHelp = tr(
"Use this server type for <a href=\"https://learn.microsoft.com/en-us/azure/planetary-computer/get-started-planetary-computer\">Planetary Computer Pro</a> instances.<br/>"
"The Directory (tenant) and Application (client) IDs can be found in your organization's Microsoft Entra ID main and application page respectively.<br/>"

View File

@ -241,6 +241,7 @@ class CORE_EXPORT QgsAnnotationLayer : public QgsMapLayer
QgsMapLayerRef mLinkedLayer;
friend class QgsAnnotationLayerRenderer;
friend class QgsAnnotationLayerChunkLoader;
};

View File

@ -4627,7 +4627,7 @@ QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int ver
// insert \a result geometry (obtain by the chamfer/fillet operation) back into original \a inputPoly polygon
auto updatePolygon = []( const QgsPolygon * inputPoly, QgsAbstractGeometry * result, int modifiedRing ) -> std::unique_ptr<QgsPolygon>
{
std::unique_ptr<QgsPolygon> newPoly = std::make_unique<QgsPolygon>();
auto newPoly = std::make_unique<QgsPolygon>();
for ( int ringIndex = 0; ringIndex < inputPoly->numInteriorRings() + 1; ++ringIndex )
{
if ( ringIndex == modifiedRing )
@ -4656,7 +4656,7 @@ QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int ver
{
if ( modifiedPart >= 0 )
{
std::unique_ptr<QgsMultiLineString> newMultiLine = std::make_unique<QgsMultiLineString>();
auto newMultiLine = std::make_unique<QgsMultiLineString>();
int partIndex = 0;
for ( QgsMultiLineString::part_iterator partIte = inputMultiLine->parts_begin(); partIte != inputMultiLine->parts_end(); ++partIte )
{
@ -4686,7 +4686,7 @@ QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int ver
// geomType == Qgis::GeometryType::Polygon
if ( modifiedPart >= 0 )
{
std::unique_ptr<QgsMultiPolygon> newMultiPoly = std::make_unique<QgsMultiPolygon>();
auto newMultiPoly = std::make_unique<QgsMultiPolygon>();
int partIndex = 0;
for ( QgsAbstractGeometry::part_iterator partIte = inputMultiPoly->parts_begin(); partIte != inputMultiPoly->parts_end(); ++partIte )
{

View File

@ -972,7 +972,7 @@ bool QgsLayoutItemElevationProfile::readPropertiesFromElement( const QDomElement
QDomElement sourceElement = sourcesElement.firstChildElement();
while ( !sourceElement.isNull() )
{
if ( sourceElement.tagName() == QStringLiteral( "profileCustomSource" ) )
if ( sourceElement.tagName() == QLatin1String( "profileCustomSource" ) )
{
const QString sourceId = sourceElement.attribute( QStringLiteral( "id" ) );
if ( QgsAbstractProfileSource *profileSource = QgsApplication::profileSourceRegistry()->findSourceById( sourceId ) )
@ -980,7 +980,7 @@ bool QgsLayoutItemElevationProfile::readPropertiesFromElement( const QDomElement
mSources.append( profileSource );
}
}
else if ( sourceElement.tagName() == QStringLiteral( "profileLayerSource" ) )
else if ( sourceElement.tagName() == QLatin1String( "profileLayerSource" ) )
{
QgsMapLayerRef ref;
ref.readXml( sourceElement, context );

View File

@ -403,7 +403,7 @@ inline std::unique_ptr<Chain> Problem::chain( int seed )
{
// A lot of conflict : make them inactive and store chain
std::unique_ptr< Chain > newChain = std::make_unique< Chain >();
auto newChain = std::make_unique< Chain >();
newChain->degree = currentChain.size() + 1 + conflicts.size();
newChain->feat.resize( newChain->degree );
newChain->label.resize( newChain->degree );

View File

@ -44,7 +44,7 @@ bool QgsVectorLayerXyPlotDataGatherer::run()
gatheredSeries.reserve( mSeriesDetails.size() );
for ( int i = 0; i < mSeriesDetails.size(); i++ )
{
std::unique_ptr<QgsXyPlotSeries> series = std::make_unique<QgsXyPlotSeries>();
auto series = std::make_unique<QgsXyPlotSeries>();
gatheredSeries.emplace_back( std::move( series ) );
gatheredSeriesCategoriesSum << QMap<QString, double>();
}

View File

@ -663,11 +663,11 @@ void QgsProject::registerTranslatableObjects( QgsTranslationContext *translation
translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueRelation" ) )
if ( field.editorWidgetSetup().type() == QLatin1String( "ValueRelation" ) )
{
translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
}
if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueMap" ) )
if ( field.editorWidgetSetup().type() == QLatin1String( "ValueMap" ) )
{
if ( field.editorWidgetSetup().config().value( QStringLiteral( "map" ) ).canConvert<QList<QVariant>>() )
{
@ -4003,11 +4003,11 @@ bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &pro
if ( e.isNull() )
{
e = propertiesElem.firstChildElement( QStringLiteral( "properties" ) );
while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QStringLiteral( "Paths" ) )
while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QLatin1String( "Paths" ) )
e = e.nextSiblingElement( QStringLiteral( "properties" ) );
e = e.firstChildElement( QStringLiteral( "properties" ) );
while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QStringLiteral( "Absolute" ) )
while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QLatin1String( "Absolute" ) )
e = e.nextSiblingElement( QStringLiteral( "properties" ) );
}
else

View File

@ -365,7 +365,7 @@ bool QgsProjectPropertyKey::readXml( const QDomNode &keyNode )
const QDomNode subkey = subkeys.item( i );
QString name;
if ( subkey.nodeName() == QStringLiteral( "properties" ) &&
if ( subkey.nodeName() == QLatin1String( "properties" ) &&
subkey.hasAttributes() && // if we have attributes
subkey.isElement() && // and we're an element
subkey.toElement().hasAttribute( QStringLiteral( "name" ) ) ) // and we have a "name" attribute

View File

@ -6080,6 +6080,20 @@ class CORE_EXPORT Qgis
Q_DECLARE_FLAGS( RasterProcessingParameterCapabilities, RasterProcessingParameterCapability )
Q_FLAG( RasterProcessingParameterCapabilities )
/**
* Dev tools node custom data roles.
* \since QGIS 4.0
*/
enum class DevToolsNodeRole
{
Status = Qt::UserRole + 1, //!< Request status role
Id, //!< Request ID role
ElapsedTime, //!< Elapsed time
MaximumTime, //!< Maximum encountered elapsed time
Sort, //!< Sort order role
};
Q_ENUM( DevToolsNodeRole )
/**
* Identify search radius in mm
*/

View File

@ -129,7 +129,7 @@ QString QgsCommandLineUtils::allVersions( )
versionString += QStringLiteral( "SFCGAL version %1\n" ).arg( sfcgalVersionCompiled );
}
#else
versionString += QStringLiteral( "No support for SFCGAL\n" );
versionString += QLatin1String( "No support for SFCGAL\n" );
#endif
// SQLite version

View File

@ -572,7 +572,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
{
case QMetaType::Type::QString:
{
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() )
if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{
if ( encoding )
value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
@ -641,7 +641,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
case QMetaType::Type::QStringList:
{
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() )
if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{
QStringList list;
char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
@ -668,7 +668,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
{
case QMetaType::Type::QString:
{
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() )
if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{
QStringList list;
char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
@ -691,7 +691,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
case QMetaType::Type::Int:
{
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() )
if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{
QVariantList list;
int count = 0;
@ -711,7 +711,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
case QMetaType::Type::Double:
{
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() )
if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{
QVariantList list;
int count = 0;
@ -731,7 +731,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
case QMetaType::Type::LongLong:
{
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() )
if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{
QVariantList list;
int count = 0;

View File

@ -13,6 +13,7 @@
* *
***************************************************************************/
#include "qgsgdalprovider.h"
#include "qgsstacasset.h"
#include <QUrl>
@ -57,10 +58,7 @@ QStringList QgsStacAsset::roles() const
bool QgsStacAsset::isCloudOptimized() const
{
const QString format = formatName();
return format == QLatin1String( "COG" ) ||
format == QLatin1String( "COPC" ) ||
format == QLatin1String( "EPT" );
return !formatName().isEmpty();
}
QString QgsStacAsset::formatName() const
@ -72,6 +70,13 @@ QString QgsStacAsset::formatName() const
return QStringLiteral( "COPC" );
else if ( mHref.endsWith( QLatin1String( "/ept.json" ) ) )
return QStringLiteral( "EPT" );
else if ( mMediaType.contains( QLatin1String( "cloud-optimized" ), Qt::CaseInsensitive ) )
{
// could use GDAL identify, but PDAL does not have the equivalent so split string
const QStringList parts = mMediaType.split( QRegExp( "[/;]+" ), Qt::SkipEmptyParts );
if ( parts.size() > 1 )
return parts[1];
}
return QString();
}
@ -79,12 +84,11 @@ QgsMimeDataUtils::Uri QgsStacAsset::uri() const
{
QgsMimeDataUtils::Uri uri;
QUrl url( href() );
if ( formatName() == QLatin1String( "COG" ) )
if ( isCloudOptimized() )
{
uri.layerType = QStringLiteral( "raster" );
uri.providerKey = QStringLiteral( "gdal" );
if ( href().startsWith( QLatin1String( "http" ), Qt::CaseInsensitive ) ||
href().startsWith( QLatin1String( "ftp" ), Qt::CaseInsensitive ) )
href().startsWith( QLatin1String( "ftp" ), Qt::CaseInsensitive ) )
{
uri.uri = QStringLiteral( "/vsicurl/%1" ).arg( href() );
}
@ -92,26 +96,40 @@ QgsMimeDataUtils::Uri QgsStacAsset::uri() const
{
uri.uri = QStringLiteral( "/vsis3/%1" ).arg( href().mid( 5 ) );
}
else if ( href().startsWith( QLatin1String( "azure://"), Qt::CaseInsensitive ) )
{
uri.uri = QStringLiteral( "/vsiaz/%1" ).arg( href().mid( 8 ) );
}
else if ( href().startsWith( QLatin1String( "gcp://"), Qt::CaseInsensitive ) )
{
uri.uri = QStringLiteral( "/vsigs/%1" ).arg( href().mid( 6 ) );
}
else
{
uri.uri = href();
}
}
else if ( formatName() == QLatin1String( "COPC" ) )
else
{
uri.layerType = QStringLiteral( "pointcloud" );
uri.providerKey = QStringLiteral( "copc" );
uri.uri = href();
return {};
}
else if ( formatName() == QLatin1String( "EPT" ) )
QString errMsg;
if ( ( formatName() == QLatin1String( "COG" ) ) ||
QgsGdalProvider::isValidRasterFileName( uri.uri, errMsg ) )
{
uri.layerType = QStringLiteral( "pointcloud" );
uri.providerKey = QStringLiteral( "ept" );
uri.uri = href();
uri.layerType = QStringLiteral( "raster" );
uri.providerKey = QStringLiteral( "gdal" );
}
else
{
return {};
uri.layerType = QStringLiteral( "pointcloud" );
uri.providerKey = formatName().toLower();
#ifndef PDAL_2_9_OR_HIGHER
// reset and don't use /vsi/ as not supported
uri.uri = href();
#endif
}
uri.name = title().isEmpty() ? url.fileName() : title();

View File

@ -3563,7 +3563,7 @@ bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
QMimeType mimeType;
QString base64data;
if ( mPath.startsWith( QStringLiteral( "base64:" ) ) )
if ( mPath.startsWith( QLatin1String( "base64:" ) ) )
{
base64data = mPath.mid( 7 );
}
@ -3601,7 +3601,7 @@ bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
extGraphElem.appendChild( onlineResElem );
// determine mime type
if ( mPath.startsWith( QStringLiteral( "http://" ) ) || mPath.startsWith( QStringLiteral( "https://" ) ) )
if ( mPath.startsWith( QLatin1String( "http://" ) ) || mPath.startsWith( QLatin1String( "https://" ) ) )
{
// Qt can't guess mime type for remote URLs, and it seems geoserver can handle wrong image mime types
// but not generic ones, so let's hardcode to png.

View File

@ -2708,11 +2708,11 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes
const QDomElement optionsElem = cfgElem.childNodes().at( 0 ).toElement();
QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
// translate widget configuration strings
if ( widgetType == QStringLiteral( "ValueRelation" ) )
if ( widgetType == QLatin1String( "ValueRelation" ) )
{
optionsMap[ QStringLiteral( "Value" ) ] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( layerNode.namedItem( QStringLiteral( "id" ) ).toElement().text(), fieldName ), optionsMap[ QStringLiteral( "Value" ) ].toString() );
}
if ( widgetType == QStringLiteral( "ValueMap" ) )
if ( widgetType == QLatin1String( "ValueMap" ) )
{
if ( optionsMap[ QStringLiteral( "map" ) ].canConvert<QList<QVariant>>() )
{

View File

@ -344,7 +344,7 @@ void QgsVectorTileUtils::loadSprites( const QVariantMap &styleDefinition, QgsMap
{
return sprite;
}
else if ( sprite.startsWith( QLatin1String( "/" ) ) )
else if ( sprite.startsWith( QLatin1Char( '/' ) ) )
{
const QUrl url( styleUrl );
return QStringLiteral( "%1://%2%3" ).arg( url.scheme(), url.host(), sprite );

View File

@ -165,8 +165,13 @@ set(QGIS_GUI_SRCS
codeeditors/qgscodeeditorexpression.cpp
codeeditors/qgscodeeditorwidget.cpp
devtools/qgsdevtoolsmodelnode.cpp
devtools/qgsdevtoolwidget.cpp
devtools/qgsdevtoolwidgetfactory.cpp
devtools/networklogger/qgsnetworklogger.cpp
devtools/networklogger/qgsnetworkloggernode.cpp
devtools/networklogger/qgsnetworkloggerpanelwidget.cpp
devtools/networklogger/qgsnetworkloggerwidgetfactory.cpp
editorwidgets/core/qgseditorconfigwidget.cpp
editorwidgets/core/qgseditorwidgetautoconf.cpp
@ -1164,8 +1169,13 @@ set(QGIS_GUI_HDRS
codeeditors/qgscodeeditorsql.h
codeeditors/qgscodeeditorwidget.h
devtools/qgsdevtoolsmodelnode.h
devtools/qgsdevtoolwidget.h
devtools/qgsdevtoolwidgetfactory.h
devtools/networklogger/qgsnetworklogger.h
devtools/networklogger/qgsnetworkloggernode.h
devtools/networklogger/qgsnetworkloggerpanelwidget.h
devtools/networklogger/qgsnetworkloggerwidgetfactory.h
editorwidgets/core/qgseditorconfigwidget.h
editorwidgets/core/qgseditorwidgetautoconf.h
@ -1889,6 +1899,7 @@ target_include_directories(qgis_gui PUBLIC
${CMAKE_SOURCE_DIR}/src/gui/callouts
${CMAKE_SOURCE_DIR}/src/gui/codeeditors
${CMAKE_SOURCE_DIR}/src/gui/devtools
${CMAKE_SOURCE_DIR}/src/gui/devtools/networklogger
${CMAKE_SOURCE_DIR}/src/gui/editorwidgets
${CMAKE_SOURCE_DIR}/src/gui/editorwidgets/core
${CMAKE_SOURCE_DIR}/src/gui/effects

View File

@ -25,6 +25,7 @@
#include "qgsstringutils.h"
#include "qgsfontutils.h"
#include "qgssettingsentryimpl.h"
#include "qgsshortcutsmanager.h"
#include <QLabel>
#include <QWidget>
@ -252,25 +253,30 @@ void QgsCodeEditor::keyPressEvent( QKeyEvent *event )
}
}
const bool ctrlModifier = event->modifiers() & Qt::ControlModifier;
const bool altModifier = event->modifiers() & Qt::AltModifier;
// Ctrl+Alt+F: reformat code
// check for reformat code sequence
const bool canReformat = languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat;
if ( !isReadOnly() && canReformat && ctrlModifier && altModifier && event->key() == Qt::Key_F )
if ( !isReadOnly() && canReformat )
{
event->accept();
reformatCode();
return;
const QKeySequence reformatCodeSequence = QgsGui::shortcutsManager()->sequenceForCommonAction( QgsShortcutsManager::CommonAction::CodeReformat );
if ( !reformatCodeSequence.isEmpty() && reformatCodeSequence.matches( event->key() | event->modifiers() ) )
{
event->accept();
reformatCode();
return;
}
}
// Toggle comment when user presses Ctrl+:
// Check for toggle comment sequence
const bool canToggle = languageCapabilities() & Qgis::ScriptLanguageCapability::ToggleComment;
if ( !isReadOnly() && canToggle && ctrlModifier && event->key() == Qt::Key_Colon )
if ( !isReadOnly() && canToggle )
{
event->accept();
toggleComment();
return;
const QKeySequence toggleCommentCodeSequence = QgsGui::shortcutsManager()->sequenceForCommonAction( QgsShortcutsManager::CommonAction::CodeToggleComment );
if ( !toggleCommentCodeSequence.isEmpty() && toggleCommentCodeSequence.matches( event->key() | event->modifiers() ) )
{
event->accept();
toggleComment();
return;
}
}
QsciScintilla::keyPressEvent( event );
@ -299,7 +305,7 @@ void QgsCodeEditor::contextMenuEvent( QContextMenuEvent *event )
if ( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat )
{
QAction *reformatAction = new QAction( tr( "Reformat Code" ), menu );
reformatAction->setShortcut( QStringLiteral( "Ctrl+Alt+F" ) );
reformatAction->setShortcut( QgsGui::shortcutsManager()->sequenceForCommonAction( QgsShortcutsManager::CommonAction::CodeReformat ) );
reformatAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "console/iconFormatCode.svg" ) ) );
reformatAction->setEnabled( !isReadOnly() );
connect( reformatAction, &QAction::triggered, this, &QgsCodeEditor::reformatCode );
@ -317,7 +323,7 @@ void QgsCodeEditor::contextMenuEvent( QContextMenuEvent *event )
if ( languageCapabilities() & Qgis::ScriptLanguageCapability::ToggleComment )
{
QAction *toggleCommentAction = new QAction( tr( "Toggle Comment" ), menu );
toggleCommentAction->setShortcut( QStringLiteral( "Ctrl+:" ) );
toggleCommentAction->setShortcut( QgsGui::shortcutsManager()->sequenceForCommonAction( QgsShortcutsManager::CommonAction::CodeToggleComment ) );
toggleCommentAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "console/iconCommentEditorConsole.svg" ), palette().color( QPalette::ColorRole::WindowText ) ) );
toggleCommentAction->setEnabled( !isReadOnly() );
connect( toggleCommentAction, &QAction::triggered, this, &QgsCodeEditor::toggleComment );

View File

@ -215,7 +215,7 @@ void QgsNetworkLogger::removeRequestRows( const QList<int> &rows )
for ( int row : std::as_const( res ) )
{
int popId = data( index( row, 0, QModelIndex() ), QgsDevToolsModelNode::RoleId ).toInt();
int popId = data( index( row, 0, QModelIndex() ), static_cast<int>( Qgis::DevToolsNodeRole::Id ) ).toInt();
mRequestGroups.remove( popId );
beginRemoveRows( QModelIndex(), row, row );

View File

@ -15,9 +15,13 @@
#ifndef QGSNETWORKLOGGER_H
#define QGSNETWORKLOGGER_H
#define SIP_NO_FILE
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QElapsedTimer>
#include "qgis_gui.h"
#include "qgsnetworkaccessmanager.h"
class QgsDevToolsModelNode;
@ -26,14 +30,14 @@ class QgsNetworkLoggerRootNode;
class QAction;
/**
* \ingroup app
* \ingroup gui
* \class QgsNetworkLogger
* \brief Logs network requests from a QgsNetworkAccessManager, converting them
* to a QAbstractItemModel representing the request and response details.
*
* \since QGIS 3.14
*/
class QgsNetworkLogger : public QAbstractItemModel
class GUI_EXPORT QgsNetworkLogger : public QAbstractItemModel
{
Q_OBJECT
@ -120,14 +124,14 @@ class QgsNetworkLogger : public QAbstractItemModel
};
/**
* \ingroup app
* \ingroup gui
* \class QgsNetworkLoggerProxyModel
* \brief A proxy model for filtering QgsNetworkLogger models by url string subsets
* or request status.
*
* \since QGIS 3.14
*/
class QgsNetworkLoggerProxyModel : public QSortFilterProxyModel
class GUI_EXPORT QgsNetworkLoggerProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:

View File

@ -101,10 +101,10 @@ QVariant QgsNetworkLoggerRequestGroup::data( int role ) const
.arg( mUrl.url(), statusToString( mStatus ), QString::number( mHttpStatus ), mContentType, bytes, mStatus == Status::Pending ? QString::number( mTimer.elapsed() / 1000 ) : QString::number( mTotalTime ), QString::number( mReplies ) );
}
case RoleStatus:
case static_cast<int>( Qgis::DevToolsNodeRole::Status ):
return static_cast<int>( mStatus );
case RoleId:
case static_cast<int>( Qgis::DevToolsNodeRole::Id ):
return mRequestId;
case Qt::ForegroundRole:

Some files were not shown because too many files have changed in this diff Show More