From 2b67f5a259cf1407efdea5b3cece7a1c72f35912 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 28 Feb 2018 16:01:04 +0100 Subject: [PATCH 01/31] [FEATURE] Introduction of QGIS Quick library This adds a new library for creation of applications based on Qt Quick framework. It contains reusable QML / Qt Quick components based on QGIS core library. The initial work introduces MapCanvas, FeatureForm, ScaleBar and various other components. To enable compilation of the library, use WITH_QUICK=TRUE Further documentation of the library is located in doc/qgsquick.dox For background information see the associated QEP: https://github.com/qgis/QGIS-Enhancement-Proposals/issues/109 The initial implementation is largely based on the work of Matthias Kuhn and Marco Bernasocchi on QField probject - kudos to them for the great job! --- .gitignore | 2 + CMakeLists.txt | 21 +- cmake/FindQtQmlTools.cmake | 47 + cmake_templates/qgsconfig.h.in | 1 + doc/CMakeLists.txt | 2 + doc/CONTRIBUTORS | 2 + doc/index.dox | 4 + doc/modules.dox | 9 +- doc/qgsquick.dox | 66 ++ python/core/qgsapplication.sip.in | 7 + src/CMakeLists.txt | 3 + src/core/qgsapplication.cpp | 8 + src/core/qgsapplication.h | 7 + src/quickgui/CMakeLists.txt | 148 +++ src/quickgui/images/ic_broken_image_black.svg | 5 + src/quickgui/images/ic_camera_alt_border.svg | 71 ++ src/quickgui/images/ic_check_black.svg | 4 + src/quickgui/images/ic_clear_black.svg | 4 + src/quickgui/images/ic_clear_white.svg | 4 + .../images/ic_delete_forever_white.svg | 5 + src/quickgui/images/ic_navigation_black.svg | 4 + .../images/ic_photo_notavailable_white.svg | 66 ++ src/quickgui/images/ic_save_white.svg | 4 + src/quickgui/images/images.qrc | 13 + src/quickgui/plugin/CMakeLists.txt | 134 +++ .../plugin/editor/qgsquickcheckbox.qml | 48 + .../plugin/editor/qgsquickdatetime.qml | 124 ++ .../editor/qgsquickexternalresource.qml | 78 ++ .../plugin/editor/qgsquicktextedit.qml | 87 ++ .../plugin/editor/qgsquickvaluemap.qml | 110 ++ src/quickgui/plugin/qgsquickfeatureform.qml | 479 ++++++++ .../plugin/qgsquickfeatureformstyling.qml | 47 + src/quickgui/plugin/qgsquickmapcanvas.qml | 140 +++ src/quickgui/plugin/qgsquickmessagelog.qml | 56 + src/quickgui/plugin/qgsquickphotopanel.qml | 221 ++++ src/quickgui/plugin/qgsquickplugin.cpp | 87 ++ src/quickgui/plugin/qgsquickplugin.h | 34 + .../plugin/qgsquickpositionmarker.qml | 158 +++ src/quickgui/plugin/qgsquickscalebar.qml | 105 ++ src/quickgui/plugin/qmldir | 24 + src/quickgui/qgis_quick.h | 33 + src/quickgui/qgsquickattributeformmodel.cpp | 73 ++ src/quickgui/qgsquickattributeformmodel.h | 97 ++ .../qgsquickattributeformmodelbase.cpp | 397 +++++++ src/quickgui/qgsquickattributeformmodelbase.h | 120 ++ .../qgsquickcoordinatetransformer.cpp | 103 ++ src/quickgui/qgsquickcoordinatetransformer.h | 74 ++ src/quickgui/qgsquickfeature.cpp | 49 + src/quickgui/qgsquickfeature.h | 59 + src/quickgui/qgsquickfeaturehighlight.cpp | 85 ++ src/quickgui/qgsquickfeaturehighlight.h | 70 ++ src/quickgui/qgsquickfeaturemodel.cpp | 289 +++++ src/quickgui/qgsquickfeaturemodel.h | 114 ++ src/quickgui/qgsquickhighlightsgnode.cpp | 95 ++ src/quickgui/qgsquickhighlightsgnode.h | 49 + src/quickgui/qgsquickidentifykit.cpp | 289 +++++ src/quickgui/qgsquickidentifykit.h | 109 ++ src/quickgui/qgsquickmapcanvasmap.cpp | 410 +++++++ src/quickgui/qgsquickmapcanvasmap.h | 164 +++ src/quickgui/qgsquickmapsettings.cpp | 229 ++++ src/quickgui/qgsquickmapsettings.h | 143 +++ src/quickgui/qgsquickmaptransform.cpp | 64 ++ src/quickgui/qgsquickmaptransform.h | 62 + src/quickgui/qgsquickmessagelogmodel.cpp | 66 ++ src/quickgui/qgsquickmessagelogmodel.h | 80 ++ src/quickgui/qgsquickpositionkit.cpp | 134 +++ src/quickgui/qgsquickpositionkit.h | 124 ++ src/quickgui/qgsquickscalebarkit.cpp | 119 ++ src/quickgui/qgsquickscalebarkit.h | 97 ++ .../qgsquicksimulatedpositionsource.cpp | 81 ++ .../qgsquicksimulatedpositionsource.h | 82 ++ src/quickgui/qgsquicksubmodel.cpp | 185 +++ src/quickgui/qgsquicksubmodel.h | 77 ++ src/quickgui/qgsquickutils.cpp | 247 ++++ src/quickgui/qgsquickutils.h | 144 +++ tests/src/CMakeLists.txt | 3 + tests/src/quickgui/CMakeLists.txt | 87 ++ tests/src/quickgui/app/CMakeLists.txt | 82 ++ tests/src/quickgui/app/FeaturePanel.qml | 79 ++ tests/src/quickgui/app/main.cpp | 97 ++ tests/src/quickgui/app/main.qml | 134 +++ tests/src/quickgui/app/qml.qrc | 6 + tests/src/quickgui/app/qtquickcontrols2.conf | 15 + tests/src/quickgui/testqgsquickutils.cpp | 104 ++ tests/testdata/quickapp_project.qgs | 1018 +++++++++++++++++ 85 files changed, 8674 insertions(+), 3 deletions(-) create mode 100644 cmake/FindQtQmlTools.cmake create mode 100644 doc/qgsquick.dox create mode 100644 src/quickgui/CMakeLists.txt create mode 100644 src/quickgui/images/ic_broken_image_black.svg create mode 100644 src/quickgui/images/ic_camera_alt_border.svg create mode 100644 src/quickgui/images/ic_check_black.svg create mode 100644 src/quickgui/images/ic_clear_black.svg create mode 100644 src/quickgui/images/ic_clear_white.svg create mode 100644 src/quickgui/images/ic_delete_forever_white.svg create mode 100644 src/quickgui/images/ic_navigation_black.svg create mode 100644 src/quickgui/images/ic_photo_notavailable_white.svg create mode 100644 src/quickgui/images/ic_save_white.svg create mode 100644 src/quickgui/images/images.qrc create mode 100644 src/quickgui/plugin/CMakeLists.txt create mode 100644 src/quickgui/plugin/editor/qgsquickcheckbox.qml create mode 100644 src/quickgui/plugin/editor/qgsquickdatetime.qml create mode 100644 src/quickgui/plugin/editor/qgsquickexternalresource.qml create mode 100644 src/quickgui/plugin/editor/qgsquicktextedit.qml create mode 100644 src/quickgui/plugin/editor/qgsquickvaluemap.qml create mode 100644 src/quickgui/plugin/qgsquickfeatureform.qml create mode 100644 src/quickgui/plugin/qgsquickfeatureformstyling.qml create mode 100644 src/quickgui/plugin/qgsquickmapcanvas.qml create mode 100644 src/quickgui/plugin/qgsquickmessagelog.qml create mode 100644 src/quickgui/plugin/qgsquickphotopanel.qml create mode 100644 src/quickgui/plugin/qgsquickplugin.cpp create mode 100644 src/quickgui/plugin/qgsquickplugin.h create mode 100644 src/quickgui/plugin/qgsquickpositionmarker.qml create mode 100644 src/quickgui/plugin/qgsquickscalebar.qml create mode 100644 src/quickgui/plugin/qmldir create mode 100644 src/quickgui/qgis_quick.h create mode 100644 src/quickgui/qgsquickattributeformmodel.cpp create mode 100644 src/quickgui/qgsquickattributeformmodel.h create mode 100644 src/quickgui/qgsquickattributeformmodelbase.cpp create mode 100644 src/quickgui/qgsquickattributeformmodelbase.h create mode 100644 src/quickgui/qgsquickcoordinatetransformer.cpp create mode 100644 src/quickgui/qgsquickcoordinatetransformer.h create mode 100644 src/quickgui/qgsquickfeature.cpp create mode 100644 src/quickgui/qgsquickfeature.h create mode 100644 src/quickgui/qgsquickfeaturehighlight.cpp create mode 100644 src/quickgui/qgsquickfeaturehighlight.h create mode 100644 src/quickgui/qgsquickfeaturemodel.cpp create mode 100644 src/quickgui/qgsquickfeaturemodel.h create mode 100644 src/quickgui/qgsquickhighlightsgnode.cpp create mode 100644 src/quickgui/qgsquickhighlightsgnode.h create mode 100644 src/quickgui/qgsquickidentifykit.cpp create mode 100644 src/quickgui/qgsquickidentifykit.h create mode 100644 src/quickgui/qgsquickmapcanvasmap.cpp create mode 100644 src/quickgui/qgsquickmapcanvasmap.h create mode 100644 src/quickgui/qgsquickmapsettings.cpp create mode 100644 src/quickgui/qgsquickmapsettings.h create mode 100644 src/quickgui/qgsquickmaptransform.cpp create mode 100644 src/quickgui/qgsquickmaptransform.h create mode 100644 src/quickgui/qgsquickmessagelogmodel.cpp create mode 100644 src/quickgui/qgsquickmessagelogmodel.h create mode 100644 src/quickgui/qgsquickpositionkit.cpp create mode 100644 src/quickgui/qgsquickpositionkit.h create mode 100644 src/quickgui/qgsquickscalebarkit.cpp create mode 100644 src/quickgui/qgsquickscalebarkit.h create mode 100644 src/quickgui/qgsquicksimulatedpositionsource.cpp create mode 100644 src/quickgui/qgsquicksimulatedpositionsource.h create mode 100644 src/quickgui/qgsquicksubmodel.cpp create mode 100644 src/quickgui/qgsquicksubmodel.h create mode 100644 src/quickgui/qgsquickutils.cpp create mode 100644 src/quickgui/qgsquickutils.h create mode 100644 tests/src/quickgui/CMakeLists.txt create mode 100644 tests/src/quickgui/app/CMakeLists.txt create mode 100644 tests/src/quickgui/app/FeaturePanel.qml create mode 100644 tests/src/quickgui/app/main.cpp create mode 100644 tests/src/quickgui/app/main.qml create mode 100644 tests/src/quickgui/app/qml.qrc create mode 100644 tests/src/quickgui/app/qtquickcontrols2.conf create mode 100644 tests/src/quickgui/testqgsquickutils.cpp create mode 100644 tests/testdata/quickapp_project.qgs diff --git a/.gitignore b/.gitignore index 34f7fed68ebd..900ac2d0a1d3 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,8 @@ desktop.ini doc/INSTALL.tex i18n/*.qm Makefile +*.pro.user +*.stash ms-windows/*.exe* ms-windows/Installer-Files/postinstall.bat ms-windows/Installer-Files/preremove.bat diff --git a/CMakeLists.txt b/CMakeLists.txt index a0f6bd6131e0..6e66cca60f8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,8 @@ IF(WITH_CORE) SET (WITH_3D FALSE CACHE BOOL "Determines whether QGIS 3D library should be built") + SET (WITH_QUICK FALSE CACHE BOOL "Determines whether QGIS Quick library should be built") + # server disabled default because it needs FastCGI (which is optional dependency) SET (WITH_SERVER FALSE CACHE BOOL "Determines whether QGIS server should be built") IF(WITH_SERVER) @@ -302,6 +304,15 @@ IF(WITH_CORE) ENDIF (WITH_3D) INCLUDE("cmake/modules/ECMQt4To5Porting.cmake") MESSAGE(STATUS "Found Qt version: ${Qt5Core_VERSION_STRING}") + IF (WITH_QUICK) + FIND_PACKAGE(Qt5Qml REQUIRED) + FIND_PACKAGE(Qt5Quick REQUIRED) + IF(${CMAKE_SYSTEM_NAME} MATCHES "Android") + FIND_PACKAGE(Qt5AndroidExtras) + ELSE(${CMAKE_SYSTEM_NAME} MATCHES "Android") + FIND_PACKAGE(QtQmlTools REQUIRED) + ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Android") + ENDIF (WITH_QUICK) IF(WITH_QTWEBKIT) SET(OPTIONAL_QTWEBKIT ${Qt5WebKitWidgets_LIBRARIES}) @@ -353,6 +364,9 @@ ENDIF(WITH_CORE) # build our version of astyle SET (WITH_ASTYLE FALSE CACHE BOOL "If you plan to contribute you should reindent with scripts/prepare-commit.sh (using 'our' astyle)") +# QML +SET(QML_IMPORT_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" CACHE PATH "QML directory for QML autocomplete") + ############################################################# # testing # whether unit tests should be build @@ -493,6 +507,7 @@ IF (WITH_CORE) SET (DEFAULT_DATA_SUBDIR .) SET (DEFAULT_PLUGIN_SUBDIR plugins) SET (DEFAULT_INCLUDE_SUBDIR include) + SET (DEFAULT_QML_SUBDIR qml) SET (DEFAULT_SERVER_MODULE_SUBDIR server) @@ -563,6 +578,7 @@ IF (WITH_CORE) SET (DEFAULT_PLUGIN_SUBDIR ../PlugIns/qgis) SET (QGIS_PLUGIN_SUBDIR_REV ../../MacOS) SET (DEFAULT_INCLUDE_SUBDIR include/qgis) + SET (DEFAULT_QML_SUBDIR qml) # Set server moodules path to DEFAULT_LIBEXEC_SUBDIR+'/server' SET (DEFAULT_SERVER_MODULE_SUBDIR ${DEFAULT_LIBEXEC_SUBDIR}/server) @@ -590,6 +606,7 @@ IF (WITH_CORE) SET (DEFAULT_LIBEXEC_SUBDIR lib${LIB_SUFFIX}/qgis) SET (DEFAULT_PLUGIN_SUBDIR lib${LIB_SUFFIX}/qgis/plugins) SET (DEFAULT_INCLUDE_SUBDIR include/qgis) + SET (DEFAULT_QML_SUBDIR qml) SET (DEFAULT_SERVER_MODULE_SUBDIR ${DEFAULT_LIBEXEC_SUBDIR}/server) ENDIF (APPLE) @@ -638,12 +655,13 @@ SET (QGIS_LIBEXEC_SUBDIR ${DEFAULT_LIBEXEC_SUBDIR} CACHE STRING "Subdirectory wh SET (QGIS_DATA_SUBDIR ${DEFAULT_DATA_SUBDIR} CACHE STRING "Subdirectory where QGIS data will be installed") SET (QGIS_PLUGIN_SUBDIR ${DEFAULT_PLUGIN_SUBDIR} CACHE STRING "Subdirectory where plugins will be installed") SET (QGIS_INCLUDE_SUBDIR ${DEFAULT_INCLUDE_SUBDIR} CACHE STRING "Subdirectory where header files will be installed") +SET (QGIS_QML_SUBDIR ${DEFAULT_QML_SUBDIR} CACHE STRING "Subdirectory where qml files/libraries will be installed") SET (QGIS_SERVER_MODULE_SUBDIR ${DEFAULT_SERVER_MODULE_SUBDIR} CACHE STRING "Subdirectory where server modules will be installed") # mark *_SUBDIR variables as advanced as this is not something # that an average user would use -MARK_AS_ADVANCED (QGIS_BIN_SUBDIR QGIS_CGIBIN_SUBDIR QGIS_LIB_SUBDIR QGIS_LIBEXEC_SUBDIR QGIS_DATA_SUBDIR QGIS_PLUGIN_SUBDIR QGIS_INCLUDE_SUBDIR) +MARK_AS_ADVANCED (QGIS_BIN_SUBDIR QGIS_CGIBIN_SUBDIR QGIS_LIB_SUBDIR QGIS_LIBEXEC_SUBDIR QGIS_DATA_SUBDIR QGIS_PLUGIN_SUBDIR QGIS_INCLUDE_SUBDIR QGIS_QML_SUBDIR) # full paths for the installation SET (QGIS_BIN_DIR ${QGIS_BIN_SUBDIR}) @@ -653,6 +671,7 @@ SET (QGIS_LIBEXEC_DIR ${QGIS_LIBEXEC_SUBDIR}) SET (QGIS_DATA_DIR ${QGIS_DATA_SUBDIR}) SET (QGIS_PLUGIN_DIR ${QGIS_PLUGIN_SUBDIR}) SET (QGIS_INCLUDE_DIR ${QGIS_INCLUDE_SUBDIR}) +SET (QGIS_QML_DIR ${QGIS_QML_SUBDIR}) SET (QGIS_SERVER_MODULE_DIR ${QGIS_SERVER_MODULE_SUBDIR}) diff --git a/cmake/FindQtQmlTools.cmake b/cmake/FindQtQmlTools.cmake new file mode 100644 index 000000000000..c0cbf50d9daf --- /dev/null +++ b/cmake/FindQtQmlTools.cmake @@ -0,0 +1,47 @@ +# Qt QML Tools +# ~~~~~~~~~~~~ +# +# To generate qmltypes files required by Qt Creator to allow QML code inspection +# (http://doc.qt.io/qtcreator/creator-qml-modules-with-plugins.html#generating-qmltypes-files) +# we need to have installed qmlplugindump unity (shipped with Qt 4.8 and later) +# http://doc.qt.io/qtcreator/creator-qml-modules-with-plugins.html#dumping-plugins-automatically +# +# Find the installed version of qmlplugindump utility. +# FindQtQmlTools should be called after Qt5 has been found +# +# This file defines the following variables: +# +# QMLPLUGINDUMP_FOUND - system has qmlplugindump +# QMLPLUGINDUMP_EXECUTABLE - Path to qmlplugindump executable +# +# Also defines MACRO to create qmltypes file, when QML directory is supplied +# +# Copyright (c) 2017, Peter Petrik +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +MACRO(FIND_QMLPLUGINDUMP) + IF(NOT QMLPLUGINDUMP_EXECUTABLE) + IF (MSVC) + FIND_PROGRAM(QMLPLUGINDUMP_EXECUTABLE qmlplugindump.exe) + ELSE (MSVC) + FIND_PROGRAM(QMLPLUGINDUMP_EXECUTABLE qmlplugindump) + ENDIF (MSVC) + ENDIF(NOT QMLPLUGINDUMP_EXECUTABLE) + + IF (QMLPLUGINDUMP_EXECUTABLE) + SET(QMLPLUGINDUMP_FOUND TRUE) + MESSAGE(STATUS "Found qmlplugindump: ${QMLPLUGINDUMP_EXECUTABLE}") + ELSE() + SET(QMLPLUGINDUMP_FOUND FALSE) + IF (QMLPLUGINDUMP_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find qmlplugindump") + ELSE (QMLPLUGINDUMP_FIND_REQUIRED) + MESSAGE(WARNING "Could not find qmlplugindump") + ENDIF (QMLPLUGINDUMP_FIND_REQUIRED) + ENDIF (QMLPLUGINDUMP_EXECUTABLE) +ENDMACRO(FIND_QMLPLUGINDUMP) + +IF (NOT QMLPLUGINDUMP_FOUND) + FIND_QMLPLUGINDUMP() +ENDIF (NOT QMLPLUGINDUMP_FOUND) diff --git a/cmake_templates/qgsconfig.h.in b/cmake_templates/qgsconfig.h.in index 67bbefe2af8e..60dc74e99382 100644 --- a/cmake_templates/qgsconfig.h.in +++ b/cmake_templates/qgsconfig.h.in @@ -25,6 +25,7 @@ #define QGIS_DATA_SUBDIR "${QGIS_DATA_SUBDIR}" #define QGIS_LIBEXEC_SUBDIR "${QGIS_LIBEXEC_SUBDIR}" #define QGIS_LIB_SUBDIR "${QGIS_LIB_SUBDIR}" +#define QGIS_QML_SUBDIR "${QGIS_QML_SUBDIR}" #define CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" #define CMAKE_SOURCE_DIR "${CMAKE_SOURCE_DIR}" diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 7b1b300de2ec..0fced3aa0086 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -99,6 +99,8 @@ IF(WITH_APIDOC) ${CMAKE_SOURCE_DIR}/src/3d/symbols ${CMAKE_SOURCE_DIR}/src/3d/terrain ${CMAKE_SOURCE_DIR}/src/plugins + ${CMAKE_SOURCE_DIR}/src/quickgui + ${CMAKE_SOURCE_DIR}/src/quickgui/plugin ) IF(WITH_SERVER_PLUGINS) diff --git a/doc/CONTRIBUTORS b/doc/CONTRIBUTORS index 7ef112deb480..b18a2ac09848 100644 --- a/doc/CONTRIBUTORS +++ b/doc/CONTRIBUTORS @@ -71,6 +71,7 @@ Nikos Alexandris Paolo Cavallini Paul Blottiere Paul Ramsey +Peter Petrik Pierre Auckenthaler Raymond Nijssen Richard Duivenvoorde @@ -85,6 +86,7 @@ Tamas Szekeres Tom Russo Tyler Mitchell Vita Cizek +Viktor Sklencar Yann Chemin Yves Jacolin diff --git a/doc/index.dox b/doc/index.dox index a83088653419..688ec146a50d 100644 --- a/doc/index.dox +++ b/doc/index.dox @@ -45,6 +45,10 @@ website: 1.7 and 1.6 +\section qgsquick_docs QgsQuick library documentation + +See \ref qgsquick for information about QGIS Quick (QML) components library + \section index_maillist Mailing Lists For support we encourage you to join our ABISYM( mSystemEnvVars ); diff --git a/src/quickgui/CMakeLists.txt b/src/quickgui/CMakeLists.txt new file mode 100644 index 000000000000..190c301e6d12 --- /dev/null +++ b/src/quickgui/CMakeLists.txt @@ -0,0 +1,148 @@ +############################################################ +# sources +SET(QGIS_QUICK_GUI_MOC_HDRS + qgsquickattributeformmodel.h + qgsquickattributeformmodelbase.h + qgsquickcoordinatetransformer.h + qgsquickfeature.h + qgsquickfeaturemodel.h + qgsquickfeaturehighlight.h + qgsquickidentifykit.h + qgsquickmapcanvasmap.h + qgsquickmapsettings.h + qgsquickmaptransform.h + qgsquickmessagelogmodel.h + qgsquickpositionkit.h + qgsquickscalebarkit.h + qgsquicksimulatedpositionsource.h + qgsquicksubmodel.h + qgsquickutils.h +) + +SET(QGIS_QUICK_GUI_HDRS + qgis_quick.h + qgsquickhighlightsgnode.h +) + +SET(QGIS_QUICK_GUI_SRC + qgsquickattributeformmodel.cpp + qgsquickattributeformmodelbase.cpp + qgsquickcoordinatetransformer.cpp + qgsquickfeature.cpp + qgsquickfeaturemodel.cpp + qgsquickfeaturehighlight.cpp + qgsquickhighlightsgnode.cpp + qgsquickidentifykit.cpp + qgsquickmapcanvasmap.cpp + qgsquickmapsettings.cpp + qgsquickmaptransform.cpp + qgsquickmessagelogmodel.cpp + qgsquickpositionkit.cpp + qgsquickscalebarkit.cpp + qgsquicksimulatedpositionsource.cpp + qgsquicksubmodel.cpp + qgsquickutils.cpp +) + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/core/annotations + ${CMAKE_SOURCE_DIR}/src/core/auth + ${CMAKE_SOURCE_DIR}/src/core/composer + ${CMAKE_SOURCE_DIR}/src/core/fieldformatter + ${CMAKE_SOURCE_DIR}/src/core/geometry + ${CMAKE_SOURCE_DIR}/src/core/layertree + ${CMAKE_SOURCE_DIR}/src/core/layout + ${CMAKE_SOURCE_DIR}/src/core/locator + ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/providers/memory + ${CMAKE_SOURCE_DIR}/src/core/raster + ${CMAKE_SOURCE_DIR}/src/core/scalebar + ${CMAKE_SOURCE_DIR}/src/core/symbology + ${CMAKE_SOURCE_DIR}/src/core/effects + ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/expression + + ${CMAKE_BINARY_DIR}/src/core +) + +INCLUDE_DIRECTORIES(SYSTEM + ${LIBZIP_INCLUDE_DIRS} + ${SPATIALINDEX_INCLUDE_DIR} + ${PROJ_INCLUDE_DIR} + ${GEOS_INCLUDE_DIR} + ${GDAL_INCLUDE_DIR} + ${EXPAT_INCLUDE_DIR} + ${SQLITE3_INCLUDE_DIR} + ${SPATIALITE_INCLUDE_DIR} + ${QCA_INCLUDE_DIR} + ${QTKEYCHAIN_INCLUDE_DIR} +) + +ADD_DEFINITIONS(-DCORE_EXPORT=) + + +SET(QGIS_QUICK_GUI_IMAGE_RCCS ./images/images.qrc) +QT5_ADD_RESOURCES(QGIS_QUICK_GUI_IMAGE_RCC_SRCS ${QGIS_QUICK_GUI_IMAGE_RCCS}) + +############################################################ +# qgis_quick shared library +QT5_WRAP_CPP(QGIS_QUICK_GUI_MOC_SRCS ${QGIS_QUICK_GUI_MOC_HDRS}) +IF(MSVC) + SET_SOURCE_FILES_PROPERTIES(${QGIS_QUICK_GUI_MOC_SRCS} PROPERTIES COMPILE_FLAGS "/wd4512 /wd4996" ) +ELSE(MSVC) + SET_SOURCE_FILES_PROPERTIES(${QGIS_QUICK_GUI_MOC_SRCS} PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations" ) +ENDIF(MSVC) + +ADD_LIBRARY(qgis_quick SHARED + ${QGIS_QUICK_GUI_IMAGE_RCC_SRCS} + ${QGIS_QUICK_GUI_SRC} + ${QGIS_QUICK_GUI_MOC_HDRS} + ${QGIS_QUICK_GUI_MOC_SRCS} + ${QGIS_QUICK_GUI_HDRS}) +TARGET_LINK_LIBRARIES(qgis_quick Qt5::Quick Qt5::Qml Qt5::Xml Qt5::Concurrent Qt5::Positioning qgis_core) +IF(CMAKE_SYSTEM_NAME STREQUAL "Android") + TARGET_LINK_LIBRARIES(qgis_quick Qt5::AndroidExtras) +ENDIF(CMAKE_SYSTEM_NAME STREQUAL "Android") + +TARGET_COMPILE_DEFINITIONS(qgis_quick PRIVATE "-DDQT_NO_FOREACH") + +# Installation +INSTALL(TARGETS qgis_quick + RUNTIME DESTINATION ${QGIS_BIN_DIR} + LIBRARY DESTINATION ${QGIS_LIB_DIR} + ARCHIVE DESTINATION ${QGIS_LIB_DIR} + FRAMEWORK DESTINATION ${QGIS_FW_SUBDIR} + PUBLIC_HEADER DESTINATION ${QGIS_INCLUDE_DIR}) + +IF(NOT APPLE) + INSTALL(FILES ${QGIS_QUICK_GUI_HDRS} ${QGIS_QUICK_GUI_MOC_HDRS} DESTINATION ${QGIS_INCLUDE_DIR}) +ELSE(NOT APPLE) + SET_TARGET_PROPERTIES(qgis_quick PROPERTIES + CLEAN_DIRECT_OUTPUT 1 + FRAMEWORK 1 + FRAMEWORK_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}" + MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/mac/framework.info.plist.in" + MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${COMPLETE_VERSION} + MACOSX_FRAMEWORK_IDENTIFIER org.qgis.qgis3_quick + BUILD_WITH_INSTALL_RPATH TRUE + PUBLIC_HEADER "${QGIS_QUICK_GUI_HDRS};${QGIS_QUICK_GUI_MOC_HDRS}" + LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" + ) + # generated export header does not get copied with PUBLIC_HEADER files + ADD_CUSTOM_COMMAND(TARGET qgis_quick + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy qgis_quick.h + "${QGIS_OUTPUT_DIRECTORY}/${QGIS_LIB_SUBDIR}/qgis_core.framework/Headers" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS qgis_quick.h + ) +ENDIF(NOT APPLE) + +############################################################ +# qgis_quick_plugin module (QML) library +ADD_SUBDIRECTORY(plugin) + diff --git a/src/quickgui/images/ic_broken_image_black.svg b/src/quickgui/images/ic_broken_image_black.svg new file mode 100644 index 000000000000..4c7e52f7807f --- /dev/null +++ b/src/quickgui/images/ic_broken_image_black.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/quickgui/images/ic_camera_alt_border.svg b/src/quickgui/images/ic_camera_alt_border.svg new file mode 100644 index 000000000000..7bd93c3322bb --- /dev/null +++ b/src/quickgui/images/ic_camera_alt_border.svg @@ -0,0 +1,71 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/quickgui/images/ic_check_black.svg b/src/quickgui/images/ic_check_black.svg new file mode 100644 index 000000000000..3fab25f262b0 --- /dev/null +++ b/src/quickgui/images/ic_check_black.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/quickgui/images/ic_clear_black.svg b/src/quickgui/images/ic_clear_black.svg new file mode 100644 index 000000000000..ec8d116194f6 --- /dev/null +++ b/src/quickgui/images/ic_clear_black.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/quickgui/images/ic_clear_white.svg b/src/quickgui/images/ic_clear_white.svg new file mode 100644 index 000000000000..570480017eb8 --- /dev/null +++ b/src/quickgui/images/ic_clear_white.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/quickgui/images/ic_delete_forever_white.svg b/src/quickgui/images/ic_delete_forever_white.svg new file mode 100644 index 000000000000..ff2e74c8fd31 --- /dev/null +++ b/src/quickgui/images/ic_delete_forever_white.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/quickgui/images/ic_navigation_black.svg b/src/quickgui/images/ic_navigation_black.svg new file mode 100644 index 000000000000..20d6b393a80d --- /dev/null +++ b/src/quickgui/images/ic_navigation_black.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/quickgui/images/ic_photo_notavailable_white.svg b/src/quickgui/images/ic_photo_notavailable_white.svg new file mode 100644 index 000000000000..89eaad55ec52 --- /dev/null +++ b/src/quickgui/images/ic_photo_notavailable_white.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/quickgui/images/ic_save_white.svg b/src/quickgui/images/ic_save_white.svg new file mode 100644 index 000000000000..05684dfc3d75 --- /dev/null +++ b/src/quickgui/images/ic_save_white.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/quickgui/images/images.qrc b/src/quickgui/images/images.qrc new file mode 100644 index 000000000000..15efe002a634 --- /dev/null +++ b/src/quickgui/images/images.qrc @@ -0,0 +1,13 @@ + + + ic_broken_image_black.svg + ic_camera_alt_border.svg + ic_check_black.svg + ic_clear_black.svg + ic_clear_white.svg + ic_delete_forever_white.svg + ic_navigation_black.svg + ic_photo_notavailable_white.svg + ic_save_white.svg + + diff --git a/src/quickgui/plugin/CMakeLists.txt b/src/quickgui/plugin/CMakeLists.txt new file mode 100644 index 000000000000..6fc1509c1b51 --- /dev/null +++ b/src/quickgui/plugin/CMakeLists.txt @@ -0,0 +1,134 @@ +############################################################ +# sources + +SET(QGIS_QUICK_PLUGIN_MOC_HDRS + qgsquickplugin.h +) + +SET(QGIS_QUICK_PLUGIN_SRC + qgsquickplugin.cpp +) + +SET(QGIS_QUICK_PLUGIN_RESOURCES + editor/qgsquickcheckbox.qml + editor/qgsquickdatetime.qml + editor/qgsquickexternalresource.qml + editor/qgsquicktextedit.qml + editor/qgsquickvaluemap.qml + qgsquickfeatureform.qml + qgsquickfeatureformstyling.qml + qgsquickmapcanvas.qml + qgsquickmessagelog.qml + qgsquickphotopanel.qml + qgsquickpositionmarker.qml + qgsquickscalebar.qml + qmldir +) + + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/core/annotations + ${CMAKE_SOURCE_DIR}/src/core/auth + ${CMAKE_SOURCE_DIR}/src/core/composer + ${CMAKE_SOURCE_DIR}/src/core/fieldformatter + ${CMAKE_SOURCE_DIR}/src/core/geometry + ${CMAKE_SOURCE_DIR}/src/core/layertree + ${CMAKE_SOURCE_DIR}/src/core/layout + ${CMAKE_SOURCE_DIR}/src/core/locator + ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/providers/memory + ${CMAKE_SOURCE_DIR}/src/core/raster + ${CMAKE_SOURCE_DIR}/src/core/scalebar + ${CMAKE_SOURCE_DIR}/src/core/symbology + ${CMAKE_SOURCE_DIR}/src/core/effects + ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/expression + ${CMAKE_SOURCE_DIR}/src/quickgui + + ${CMAKE_BINARY_DIR}/src/core + ${CMAKE_BINARY_DIR}/src/quickgui +) + +INCLUDE_DIRECTORIES(SYSTEM + ${LIBZIP_INCLUDE_DIRS} + ${SPATIALINDEX_INCLUDE_DIR} + ${PROJ_INCLUDE_DIR} + ${GEOS_INCLUDE_DIR} + ${GDAL_INCLUDE_DIR} + ${EXPAT_INCLUDE_DIR} + ${SQLITE3_INCLUDE_DIR} + ${SPATIALITE_INCLUDE_DIR} + ${QCA_INCLUDE_DIR} + ${QTKEYCHAIN_INCLUDE_DIR} +) + +ADD_DEFINITIONS(-DCORE_EXPORT=) + +############################################################ +# qgis_quick_plugin module (QML) library + +QT5_WRAP_CPP(QGIS_QUICK_PLUGIN_MOC_SRCS ${QGIS_QUICK_PLUGIN_MOC_HDRS}) +IF(MSVC) + SET_SOURCE_FILES_PROPERTIES(${QGIS_QUICK_PLUGIN_MOC_SRCS} PROPERTIES COMPILE_FLAGS "/wd4512 /wd4996" ) +ELSE(MSVC) + SET_SOURCE_FILES_PROPERTIES(${QGIS_QUICK_PLUGIN_MOC_SRCS} PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations" ) +ENDIF(MSVC) + +SET(QGIS_QUICK_PLUGIN_RUNTIME_DIR ${QGIS_OUTPUT_DIRECTORY}/${QGIS_QML_SUBDIR}/QgisQuick) + +ADD_CUSTOM_TARGET(qgis_quick_qml_copy) + +ADD_CUSTOM_COMMAND(TARGET qgis_quick_qml_copy + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${QGIS_QUICK_PLUGIN_RUNTIME_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +FOREACH(qmlfile ${QGIS_QUICK_PLUGIN_RESOURCES}) + ADD_CUSTOM_COMMAND(TARGET qgis_quick_qml_copy + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${qmlfile} ${QGIS_QUICK_PLUGIN_RUNTIME_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${qmlfile} + ) +ENDFOREACH(qmlfile) + +ADD_LIBRARY(qgis_quick_plugin MODULE + ${QGIS_QUICK_PLUGIN_SRC} + ${QGIS_QUICK_PLUGIN_MOC_HDRS} + ${QGIS_QUICK_PLUGIN_MOC_SRCS} + ${QGIS_QUICK_PLUGIN_RESOURCES} +) +TARGET_LINK_LIBRARIES(qgis_quick_plugin qgis_quick) +SET_TARGET_PROPERTIES(qgis_quick_plugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${QGIS_QUICK_PLUGIN_RUNTIME_DIR}) +TARGET_COMPILE_DEFINITIONS(qgis_quick_plugin PRIVATE "-DQUICK_EXPORT=" "-DDQT_NO_FOREACH") +ADD_DEPENDENCIES(qgis_quick_plugin qgis_quick_qml_copy) + +# TODO: temporarily disabled running of qmlplugindump as it was not behaving well (unknown failures when run) +IF(QMLPLUGINDUMP_FOUND AND FALSE) + # Extract QML Types Info from our QML plugin. This is useful for development with Qt Creator as it allows + # Qt Creator understand also the C++ classes registered in the plugin and thus available in QML code + SET(QGIS_QUICK_PLUGIN_TYPEINFO ${QGIS_QUICK_PLUGIN_RUNTIME_DIR}/qgisquick.qmltypes) + ADD_CUSTOM_COMMAND( + TARGET qgis_quick_plugin + COMMAND ${QMLPLUGINDUMP_EXECUTABLE} + ARGS QgisQuick 0.1 ${QGIS_QUICK_PLUGIN_RUNTIME_DIR} --output ${QGIS_QUICK_PLUGIN_TYPEINFO} --noinstantiate + DEPENDS ${QGIS_QUICK_PLUGIN_RESOURCES} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + POST_BUILD + ) +ENDIF() + +# Installation +SET(QUICK_PLUGIN_INSTALL_DIR ${QGIS_QML_DIR}/QgisQuick) +INSTALL(TARGETS qgis_quick_plugin + RUNTIME DESTINATION ${QUICK_PLUGIN_INSTALL_DIR} + LIBRARY DESTINATION ${QUICK_PLUGIN_INSTALL_DIR} +) +INSTALL(FILES ${QGIS_QUICK_PLUGIN_RESOURCES} ${QGIS_QUICK_PLUGIN_TYPEINFO} + DESTINATION ${QUICK_PLUGIN_INSTALL_DIR} +) diff --git a/src/quickgui/plugin/editor/qgsquickcheckbox.qml b/src/quickgui/plugin/editor/qgsquickcheckbox.qml new file mode 100644 index 000000000000..0645056a80ea --- /dev/null +++ b/src/quickgui/plugin/editor/qgsquickcheckbox.qml @@ -0,0 +1,48 @@ +/*************************************************************************** + qgsquickcheckbox.qml + -------------------------------------- + Date : 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Controls 2.2 +import QgisQuick 0.1 as QgsQuick + +// Checkbox for QGIS Attribute Form +// Requires various global properties set to function, see qgsquickfeatureform Loader section +// Do not use directly from Application QML + +Item { + signal valueChanged( var value, bool isNull ) + + height: childrenRect.height + anchors { + right: parent.right + left: parent.left + } + + CheckBox { + property var currentValue: value + + checked: value === config['CheckedState'] + + onCheckedChanged: { + valueChanged( checked ? config['CheckedState'] : config['UncheckedState'], false ) + forceActiveFocus() + } + + // Workaround to get a signal when the value has changed + onCurrentValueChanged: { + checked = currentValue === config['CheckedState'] + } + } +} diff --git a/src/quickgui/plugin/editor/qgsquickdatetime.qml b/src/quickgui/plugin/editor/qgsquickdatetime.qml new file mode 100644 index 000000000000..33aa7bdafeb8 --- /dev/null +++ b/src/quickgui/plugin/editor/qgsquickdatetime.qml @@ -0,0 +1,124 @@ +/*************************************************************************** + qgsquickdatetime.qml + -------------------------------------- + Date : 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.4 as Controls1 +import QgisQuick 0.1 as QgsQuick + +// Calendar for QGIS Attribute Form +// Requires various global properties set to function, see qgsquickfeatureform Loader section +// Do not use directly from Application QML + +Item { + signal valueChanged(var value, bool isNull) + + height: childrenRect.height + anchors { right: parent.right; left: parent.left } + + ColumnLayout { + id: main + property var currentValue: value + + anchors { right: parent.right; left: parent.left } + + Item { + anchors { right: parent.right; left: parent.left } + Layout.minimumHeight: 48 * QgsQuick.Utils.dp + + Rectangle { + anchors.fill: parent + id: backgroundRect + border.color: "#17a81a" + border.width: 2 + color: "#dddddd" + radius: 2 + } + + Label { + id: label + + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + + MouseArea { + anchors.fill: parent + onClicked: { + popup.open() + } + } + + Image { + source: QgsQuick.Utils.getThemeIcon("ic_clear_black") + anchors.left: parent.right + visible: main.currentValue !== undefined && config['allow_null'] + + MouseArea { + anchors.fill: parent + onClicked: { + main.currentValue = undefined + } + } + } + } + } + + Popup { + id: popup + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + parent: ApplicationWindow.overlay + + ColumnLayout { + + Controls1.Calendar { + id: calendar + selectedDate: main.currentValue + weekNumbersVisible: true + focus: false + + onSelectedDateChanged: { + main.currentValue = selectedDate + } + } + + RowLayout { + Button { + text: qsTr( "Ok" ) + Layout.fillWidth: true + + onClicked: popup.close() + } + } + } + } + + onCurrentValueChanged: { + valueChanged(currentValue, main.currentValue === undefined) + if (main.currentValue === undefined) + { + label.text = qsTr('(no date)') + label.color = 'gray' + } + else + { + label.color = 'black' + label.text = new Date(value).toLocaleString(Qt.locale(), config['display_format'] ) + } + } + } +} diff --git a/src/quickgui/plugin/editor/qgsquickexternalresource.qml b/src/quickgui/plugin/editor/qgsquickexternalresource.qml new file mode 100644 index 000000000000..2c26dc3e5b0e --- /dev/null +++ b/src/quickgui/plugin/editor/qgsquickexternalresource.qml @@ -0,0 +1,78 @@ +/*************************************************************************** + qgsquickexternalresource.qml + -------------------------------------- + Date : 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 2.0 +import QgisQuick 0.1 as QgsQuick + +// External Resource (Photo capture) for QGIS Attribute Form +// Requires various global properties set to function, see qgsquickfeatureform Loader section +// Do not use directly from Application QML + +Item { + signal valueChanged(var value, bool isNull) + + property var image: image + + id: fieldItem + anchors.left: parent.left + anchors.right: parent.right + + height: Math.max(image.height, button.height) + + Image { + property var currentValue: value + + id: image + width: 200 * QgsQuick.Utils.dp + autoTransform: true + fillMode: Image.PreserveAspectFit + + Component.onCompleted: image.source = getSource() + + function getSource() { + if (image.status === Image.Error) + return QgsQuick.Utils.getThemeIcon("ic_broken_image_black") + else if (image.currentValue && QgsQuick.Utils.fileExists(homePath + "/" + image.currentValue)) { + return homePath + "/" + image.currentValue + } + else { + return QgsQuick.Utils.getThemeIcon("ic_photo_notavailable_white") + } + } + } + + Button { + id: button + visible: fieldItem.enabled + width: 45 * QgsQuick.Utils.dp + height: 45 * QgsQuick.Utils.dp + + anchors.right: parent.right + anchors.bottom: parent.bottom + + onClicked: { + photoCapturePanel.visible = true + photoCapturePanel.targetDir = homePath + photoCapturePanel.fieldItem = fieldItem + } + + background: Image { + source: QgsQuick.Utils.getThemeIcon("ic_camera_alt_border") + width: button.width + height: button.height + } + } +} diff --git a/src/quickgui/plugin/editor/qgsquicktextedit.qml b/src/quickgui/plugin/editor/qgsquicktextedit.qml new file mode 100644 index 000000000000..21d69d5dcfa4 --- /dev/null +++ b/src/quickgui/plugin/editor/qgsquicktextedit.qml @@ -0,0 +1,87 @@ +/*************************************************************************** + qgsquicktextedit.qml + -------------------------------------- + Date : 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Controls 2.2 +import QtQuick 2.5 +import QgisQuick 0.1 as QgsQuick + +// Text Edit for QGIS Attribute Form +// Requires various global properties set to function, see qgsquickfeatureform Loader section +// Do not use directly from Application QML + +Item { + signal valueChanged(var value, bool isNull) + + height: childrenRect.height + + TextField { + id: textField + height: textArea.height == 0 ? fontMetrics.height + 20 * QgsQuick.Utils.dp : 0 + topPadding: 10 * QgsQuick.Utils.dp + bottomPadding: 10 * QgsQuick.Utils.dp + visible: height !== 0 + anchors.left: parent.left + anchors.right: parent.right + font.pointSize: 14 + + text: value || '' + + inputMethodHints: field.isNumeric || widget == 'Range' ? Qt.ImhFormattedNumbersOnly : Qt.ImhNone + + // Make sure we do not input more characters than allowed for strings + states: [ + State { + name: "limitedTextLengthState" + when: (!field.isNumeric) && (field.length > 0) + PropertyChanges { + target: textField + maximumLength: field.length + } + } + ] + + background: Rectangle { + y: textField.height - height - textField.bottomPadding / 2 + implicitWidth: 120 * QgsQuick.Utils.dp + height: textField.activeFocus ? 2 * QgsQuick.Utils.dp : 1 * QgsQuick.Utils.dp + color: textField.activeFocus ? "#4CAF50" : "#C8E6C9" + } + + onTextChanged: { + valueChanged( text, text == '' ) + } + } + + TextArea { + id: textArea + height: config['IsMultiline'] === true ? undefined : 0 + visible: height !== 0 + anchors.left: parent.left + anchors.right: parent.right + + text: value || '' + textFormat: config['UseHtml'] ? TextEdit.RichText : TextEdit.PlainText + + onEditingFinished: { + valueChanged( text, text == '' ) + } + } + + FontMetrics { + id: fontMetrics + font: textField.font + } +} diff --git a/src/quickgui/plugin/editor/qgsquickvaluemap.qml b/src/quickgui/plugin/editor/qgsquickvaluemap.qml new file mode 100644 index 000000000000..96cd886d0c9f --- /dev/null +++ b/src/quickgui/plugin/editor/qgsquickvaluemap.qml @@ -0,0 +1,110 @@ +/*************************************************************************** + qgsquickvaluemap.qml + -------------------------------------- + Date : 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 +import QgisQuick 0.1 as QgsQuick + +// Value Map for QGIS Attribute Form +// Requires various global properties set to function, see qgsquickfeatureform Loader section +// Do not use directly from Application QML + +Item { + signal valueChanged(var value, bool isNull) + + anchors { + left: parent.left + right: parent.right + rightMargin: 10 * QgsQuick.Utils.dp + } + + height: childrenRect.height + 10 * QgsQuick.Utils.dp + + + + ComboBox { + id: comboBox + + property var reverseConfig: ({}) + property var currentValue: value + + anchors { left: parent.left; right: parent.right } + + currentIndex: find(reverseConfig[value]) + + Component.onCompleted: { + model = Object.keys(config['map']); + for(var key in config['map']) { + reverseConfig[config['map'][key]] = key; + } + currentIndex = find(reverseConfig[value]) + } + + onCurrentTextChanged: { + valueChanged(config['map'][currentText], false) + } + + // Workaround to get a signal when the value has changed + onCurrentValueChanged: { + currentIndex = find(reverseConfig[value]) + } + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + + onClicked: mouse.accepted = false + onPressed: { forceActiveFocus(); mouse.accepted = false; } + onReleased: mouse.accepted = false; + onDoubleClicked: mouse.accepted = false; + onPositionChanged: mouse.accepted = false; + onPressAndHold: mouse.accepted = false; + } + + // [hidpi fixes] + delegate: ItemDelegate { + width: comboBox.width + height: 36 * QgsQuick.Utils.dp + text: modelData + font.weight: comboBox.currentIndex === index ? Font.DemiBold : Font.Normal + font.pointSize: 12 + highlighted: comboBox.highlightedIndex == index + } + + contentItem: Text { + height: 36 * QgsQuick.Utils.dp + text: comboBox.displayText + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Item { + implicitWidth: 120 * QgsQuick.Utils.dp + implicitHeight: 36 * QgsQuick.Utils.dp + + Rectangle { + anchors.fill: parent + id: backgroundRect + border.color: comboBox.pressed ? "#17a81a" : "#21be2b" + border.width: comboBox.visualFocus ? 2 : 1 + color: "#dddddd" + radius: 2 + } + } + // [/hidpi fixes] + } +} diff --git a/src/quickgui/plugin/qgsquickfeatureform.qml b/src/quickgui/plugin/qgsquickfeatureform.qml new file mode 100644 index 000000000000..e685325a55dd --- /dev/null +++ b/src/quickgui/plugin/qgsquickfeatureform.qml @@ -0,0 +1,479 @@ +/*************************************************************************** + qgsquickfeatureform.qml + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 +import QtQml.Models 2.2 +import QtQml 2.2 + +// We use calendar in datetime widget that is not +// yet implemented in Controls 2.2 +import QtQuick.Controls 1.4 as Controls1 + +import QgisQuick 0.1 as QgsQuick + +Item { + signal saved + signal cancelled + signal aboutToSave + + property QgsQuick.AttributeFormModel model + property alias toolbarVisible: toolbar.visible + property bool allowRememberAttribute: false // when adding new feature, add checkbox to be able to save the same value for the next feature as default + property QgsQuick.Project project + + property var saveBtnIcon: QgsQuick.Utils.getThemeIcon( "ic_save_white" ) + property var deleteBtnIcon: QgsQuick.Utils.getThemeIcon( "ic_delete_forever_white" ) + property var closeBtnIcon: QgsQuick.Utils.getThemeIcon( "ic_clear_white" ) + + property FeatureFormStyling style: FeatureFormStyling {} + + function reset() { + master.reset() + } + + id: form + + states: [ + State { + name: "ReadOnly" + }, + State { + name: "Edit" + }, + State { + name: "Add" + } + ] + + /** + * This is a relay to forward private signals to internal components. + */ + QtObject { + id: master + + /** + * This signal is emitted whenever the state of Flickables and TabBars should + * be restored. + */ + signal reset + } + + Item { + id: container + + clip: true + + anchors { + top: toolbar.bottom + bottom: parent.bottom + left: parent.left + right: parent.right + } + + Flickable { + id: flickable + anchors { + left: parent.left + right: parent.right + } + height: tabRow.height + + flickableDirection: Flickable.HorizontalFlick + contentWidth: tabRow.width + + // Tabs + TabBar { + id: tabRow + visible: model.hasTabs + height: form.style.tabs.height + + Connections { + target: master + onReset: tabRow.currentIndex = 0 + } + + Connections { + target: swipeView + onCurrentIndexChanged: tabRow.currentIndex = swipeView.currentIndex + } + + Repeater { + model: form.model + + TabButton { + id: tabButton + text: Name + leftPadding: 8 * QgsQuick.Utils.dp + rightPadding: 8 * QgsQuick.Utils.dp + + width: contentItem.width + leftPadding + rightPadding + height: form.style.tabs.height + + contentItem: Text { + // Make sure the width is derived from the text so we can get wider + // than the parent item and the Flickable is useful + width: paintedWidth + text: tabButton.text + color: !tabButton.enabled ? form.style.tabs.disabledColor : tabButton.down || + tabButton.checked ? form.style.tabs.activeColor : form.style.tabs.normalColor + font.weight: tabButton.checked ? Font.DemiBold : Font.Normal + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + } + } + } + + SwipeView { + id: swipeView + currentIndex: tabRow.currentIndex + anchors { + top: flickable.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + + Repeater { + // One page per tab in tabbed forms, 1 page in auto forms + model: form.model.hasTabs ? form.model : 1 + + Item { + id: formPage + property int currentIndex: index + + /** + * The main form content area + */ + Rectangle { + width: parent.width + height: parent.height + color: form.style.backgroundColor + opacity: form.style.backgroundOpacity + } + + ListView { + id: content + anchors.fill: parent + clip: true + section.property: "Group" + section.labelPositioning: ViewSection.CurrentLabelAtStart | ViewSection.InlineLabels + section.delegate: Component { + // section header: group box name + Rectangle { + width: parent.width + height: section === "" ? 0 : form.style.group.height + color: form.style.group.backgroundColor + + Text { + anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter } + font.bold: true + text: section + } + } + } + + Connections { + target: master + onReset: content.contentY = 0 + } + + model: QgsQuick.SubModel { + id: contentModel + model: form.model + rootIndex: form.model.hasTabs ? form.model.index(currentIndex, 0) : undefined + } + + delegate: fieldItem + } + } + } + } + } + + /** + * A field editor + */ + Component { + id: fieldItem + + Item { + id: fieldContainer + visible: Type === 'field' + height: childrenRect.height + + anchors { + left: parent.left + right: parent.right + leftMargin: 12 * QgsQuick.Utils.dp + } + + Label { + id: fieldLabel + + text: Name || '' + font.bold: true + color: ConstraintValid ? form.style.constraint.validColor : form.style.constraint.invalidColor + } + + Label { + id: constraintDescriptionLabel + anchors { + left: parent.left + right: parent.right + top: fieldLabel.bottom + } + + text: ConstraintDescription + height: ConstraintValid ? 0 : undefined + visible: !ConstraintValid + + color: form.style.constraint.descriptionColor + } + + Item { + id: placeholder + height: childrenRect.height + anchors { left: parent.left; right: rememberCheckbox.left; top: constraintDescriptionLabel.bottom } + + Loader { + id: attributeEditorLoader + + height: childrenRect.height + anchors { left: parent.left; right: parent.right } + + enabled: form.state !== "ReadOnly" && !!AttributeEditable + + property var value: AttributeValue + property var config: EditorWidgetConfig + property var widget: EditorWidget + property var field: Field + property var constraintValid: ConstraintValid + property var homePath: form.project ? form.project.homePath : "" + property var photoCapturePanel: photoPanel + + active: widget !== 'Hidden' + source: 'qgsquick' + widget.toLowerCase() + '.qml' + + onStatusChanged: { + if ( attributeEditorLoader.status === Loader.Error ) + { + console.warn( "Editor widget type '" + EditorWidget + "' is not supported" ); + source = 'qgsquicktextedit.qml'; + } + } + } + + Connections { + target: form + onAboutToSave: { + try { + attributeEditorLoader.item.pushChanges() + } + catch ( err ) + {} + } + } + + Connections { + target: attributeEditorLoader.item + onValueChanged: { + AttributeValue = isNull ? undefined : value + } + } + } + + CheckBox { + id: rememberCheckbox + checked: RememberValue ? true : false + + visible: form.allowRememberAttribute && form.state === "Add" && EditorWidget !== "Hidden" + width: visible ? undefined : 0 + + anchors { right: parent.right; top: fieldLabel.bottom } + + onCheckedChanged: { + RememberValue = checked + } + } + } + } + + function save() { + parent.focus = true + aboutToSave() + + if ( form.state === "Add" ) { + model.create() + state = "Edit" + } + else + { + model.save() + } + + saved() + } + + Connections { + target: Qt.inputMethod + onVisibleChanged: { + Qt.inputMethod.commit() + } + } + + /** The title toolbar **/ + Item { + id: toolbar + height: visible ? 48 * QgsQuick.Utils.dp : 0 + visible: form.state === 'Add' + anchors { + top: parent.top + left: parent.left + right: parent.right + } + + RowLayout { + anchors.fill: parent + Layout.margins: 0 + + ToolButton { + id: saveButton + Layout.preferredWidth: form.style.toolbutton.size + Layout.preferredHeight: form.style.toolbutton.size + + visible: form.state !== "ReadOnly" + + contentItem: Image { + source: form.saveBtnIcon + sourceSize: Qt.size(width, height) + } + + background: Rectangle { + color: model.constraintsValid ? form.style.toolbutton.backgroundColor : form.style.toolbutton.backgroundColorInvalid + } + + enabled: model.constraintsValid + + onClicked: { + save() + } + } + + ToolButton { + id: deleteButton + + Layout.preferredWidth: form.style.toolbutton.size + Layout.preferredHeight: form.style.toolbutton.size + + visible: form.state === "Edit" + + contentItem: Image { + source: form.deleteBtnIcon + sourceSize: Qt.size(width, height) + } + + background: Rectangle { + color: form.style.toolbutton.backgroundColor + } + + onClicked: deleteDialog.visible = true + } + + Label { + id: titleLabel + + text: + { + var currentLayer = model.featureModel.layer + var layerName = 'N/A' + if (currentLayer !== null) + layerName = currentLayer.name + + if ( form.state === 'Add' ) + qsTr( 'Add feature on %1' ).arg(layerName ) + else if ( form.state === 'Edit' ) + qsTr( 'Edit feature on %1' ).arg(layerName) + else + qsTr( 'View feature on %1' ).arg(layerName) + } + font.bold: true + font.pointSize: 16 + elide: Label.ElideRight + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + Layout.fillWidth: true + color: "white" + } + + ToolButton { + id: closeButton + anchors.right: parent.right + + Layout.preferredWidth: form.style.toolbutton.size + Layout.preferredHeight: form.style.toolbutton.size + + contentItem: Image { + source: form.closeBtnIcon + sourceSize: Qt.size(width, height) + } + + background: Rectangle { + color: form.style.toolbutton.backgroundColor + } + + onClicked: { + Qt.inputMethod.hide() + + cancelled() + } + } + } + } + + MessageDialog { + id: deleteDialog + + visible: false + + title: qsTr( "Delete feature" ) + text: qsTr( "Really delete this feature?" ) + icon: StandardIcon.Warning + standardButtons: StandardButton.Ok | StandardButton.Cancel + onAccepted: { + model.featureModel.deleteFeature() + visible = false + + cancelled() + } + onRejected: { + visible = false + } + } + + QgsQuick.PhotoCapture { + id: photoPanel + height: window.height + width: window.width + edge: Qt.RightEdge + } + +} diff --git a/src/quickgui/plugin/qgsquickfeatureformstyling.qml b/src/quickgui/plugin/qgsquickfeatureformstyling.qml new file mode 100644 index 000000000000..99e5b93e5755 --- /dev/null +++ b/src/quickgui/plugin/qgsquickfeatureformstyling.qml @@ -0,0 +1,47 @@ +/*************************************************************************** + qgsquickfeatureformstyling.qml + -------------------------------------- + Date : January 2018 + Copyright : (C) 2018 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. * + * * + ***************************************************************************/ + +import QtQuick 2.0 + +import QgisQuick 0.1 as QgsQuick + +QtObject { + property color backgroundColor: "white" + property real backgroundOpacity: 1 + + property QtObject group: QtObject { + property color backgroundColor: "lightGray" + property real height: 30 * QgsQuick.Utils.dp + } + + property QtObject tabs: QtObject { + property color normalColor: "#4CAF50" + property color activeColor: "#1B5E20" + property color disabledColor: "#999999" + property real height: 48 * QgsQuick.Utils.dp + } + + property QtObject constraint: QtObject { + property color validColor: "black" + property color invalidColor: "#c0392b" + property color descriptionColor: "#e67e22" + } + + property QtObject toolbutton: QtObject { + property color backgroundColor: "transparent" + property color backgroundColorInvalid: "#bdc3c7" + property real size: 80 * QgsQuick.Utils.dp + } +} diff --git a/src/quickgui/plugin/qgsquickmapcanvas.qml b/src/quickgui/plugin/qgsquickmapcanvas.qml new file mode 100644 index 000000000000..b265dfdac459 --- /dev/null +++ b/src/quickgui/plugin/qgsquickmapcanvas.qml @@ -0,0 +1,140 @@ +/*************************************************************************** + qgsquickmapcanvas.qml + -------------------------------------- + Date : 10.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 2.2 +import QtQml 2.2 +import QgisQuick 0.1 as QgsQuick + +Item { + id: mapArea + property alias mapSettings: mapCanvasWrapper.mapSettings + property alias isRendering: mapCanvasWrapper.isRendering + property alias incrementalRendering: mapCanvasWrapper.incrementalRendering + + signal clicked(var mouse) + + /** + * Freezes the map canvas refreshes. + * + * In case of repeated geometry changes (animated resizes, pinch, pan...) + * triggering refreshes all the time can cause severe performance impacts. + * + * If freeze is called, an internal counter is incremented and only when the + * counter is 0, refreshes will happen. + * It is therefore important to call freeze() and unfreeze() exactly the same + * number of times. + */ + function freeze(id) { + mapCanvasWrapper.__freezecount[id] = true + mapCanvasWrapper.freeze = true + } + + function unfreeze(id) { + delete mapCanvasWrapper.__freezecount[id] + mapCanvasWrapper.freeze = Object.keys(mapCanvasWrapper.__freezecount).length !== 0 + } + + QgsQuick.MapCanvasMap { + id: mapCanvasWrapper + + anchors.fill: parent + + property var __freezecount: ({}) + + freeze: false + } + + PinchArea { + id: pinchArea + + anchors.fill: parent + + onPinchStarted: { + freeze('pinch') + } + + onPinchUpdated: { + mapCanvasWrapper.zoom( pinch.center, pinch.previousScale / pinch.scale ) + mapCanvasWrapper.pan( pinch.center, pinch.previousCenter ) + } + + onPinchFinished: { + unfreeze('pinch') + mapCanvasWrapper.refresh() + } + + MouseArea { + id: mouseArea + + property point __initialPosition + property point __lastPosition + + anchors.fill : parent + + onDoubleClicked: { + var center = Qt.point( mouse.x, mouse.y ) + mapCanvasWrapper.zoom( center, 0.8 ) + } + + onClicked: { + if ( mouse.button === Qt.RightButton ) + { + var center = Qt.point( mouse.x, mouse.y ) + mapCanvasWrapper.zoom( center, 1.2 ) + } + else + { + var distance = Math.abs( mouse.x - __initialPosition.x ) + Math.abs( mouse.y - __initialPosition.y ) + + if ( distance < 5 * QgsQuick.Utils.dp) + mapArea.clicked( mouse ) + } + } + + onPressed: { + __lastPosition = Qt.point( mouse.x, mouse.y) + __initialPosition = __lastPosition + freeze('pan') + } + + onReleased: { + unfreeze('pan') + } + + onPositionChanged: { + var currentPosition = Qt.point( mouse.x, mouse.y ) + mapCanvasWrapper.pan( currentPosition, __lastPosition ) + __lastPosition = currentPosition + } + + onCanceled: { + unfreezePanTimer.start() + } + + onWheel: { + mapCanvasWrapper.zoom( Qt.point( wheel.x, wheel.y ), Math.pow( 0.8, wheel.angleDelta.y / 60 ) ) + } + + Timer { + id: unfreezePanTimer + interval: 500; + running: false; + repeat: false + onTriggered: unfreeze('pan') + } + } + } +} diff --git a/src/quickgui/plugin/qgsquickmessagelog.qml b/src/quickgui/plugin/qgsquickmessagelog.qml new file mode 100644 index 000000000000..4024f17caa4c --- /dev/null +++ b/src/quickgui/plugin/qgsquickmessagelog.qml @@ -0,0 +1,56 @@ +/*************************************************************************** + qgsquickmessagelog.qml + -------------------------------------- + Date : January 2018 + Copyright : (C) 2018 by Peter Petrik + Email : zilolv 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. * + * * + ***************************************************************************/ + +import QtQuick.Controls 2.0 +import QtQuick 2.5 +import QgisQuick 0.1 as QgsQuick + +Item { + property alias model: table.model + property color bgColor: "white" + property color separatorColor: "gray" + property int separatorSize: 1 * QgsQuick.Utils.dp + + id: item + + Rectangle { + color: item.bgColor + anchors.fill: parent + } + + ListView { + id: table + anchors.fill: parent + + delegate: Column { + Text { + text: MessageTag + font.bold: true + } + + Text { + text: Message + width: table.width + wrapMode: Text.WordWrap + } + + Rectangle { + color: item.separatorColor + height: item.separatorSize + width: table.width + } + } + } +} diff --git a/src/quickgui/plugin/qgsquickphotopanel.qml b/src/quickgui/plugin/qgsquickphotopanel.qml new file mode 100644 index 000000000000..de4fd6c20191 --- /dev/null +++ b/src/quickgui/plugin/qgsquickphotopanel.qml @@ -0,0 +1,221 @@ +/*************************************************************************** + qgsquickphotopanel.qml + -------------------------------------- + Date : Dec 2017 + Copyright : (C) 2017 by Viktor Sklencar + Email : vsklencar 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. * + * * + ***************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 2.2 +import QtQml 2.2 +import QtMultimedia 5.8 +import QtGraphicalEffects 1.0 +import QgisQuick 0.1 as QgsQuick + +Drawer { + property var targetDir + property var lastPhotoName + property int iconSize: photoPanel.width/20 + property var fieldItem + + property color bgColor: "white" + property color borderColor: "black" + + // icons: + property var captureBtnIcon: QgsQuick.Utils.getThemeIcon("ic_camera_alt_border") + property var okBtnIcon: QgsQuick.Utils.getThemeIcon("ic_check_black") + property var cancelBtnIcon: QgsQuick.Utils.getThemeIcon("ic_clear_black") + + + id: photoPanel + visible: false + modal: true + interactive: true + dragMargin: 0 // prevents opening the drawer by dragging. + + + + background: Rectangle { + color: photoPanel.bgColor + opacity: 0.8 + } + + onVisibleChanged: { + if (visible) { + camera.setCameraState(Camera.ActiveState) + camera.start() + } else { + camera.stop() + photoPreview.visible = false + } + } + + // PhotoCapture item + Item { + + property bool saveImage: false + + id: captureItem + width: window.width + height: window.height + + Component.onDestruction: { + if (!captureItem && camera.imageCapture.capturedImagePath != ""){ + captureItem.saveImage = false + QgsQuick.Utils.remove(camera.imageCapture.capturedImagePath) + } + captureItem.saveImage = false + } + + Camera { + id: camera + cameraState: Camera.UnloadedState + + imageCapture { + onImageCaptured: { + // Show the preview in an Image + photoPreview.source = preview + } + } + } + + // Flipped VideoOutput on android - known ButtonGroup + // https://bugreports.qt.io/browse/QTBUG-64764 + VideoOutput { + id: videoOutput + source: camera + focus : visible // to receive focus and capture key events when visible + anchors.fill: parent + autoOrientation: true + + Rectangle { + id: captureBtn + property int borderWidth: 10 * QgsQuick.Utils.dp + width: parent.width/20 + height: parent.width/20 + color: photoPanel.bgColor + border.color: photoPanel.borderColor + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + border.width: borderWidth + radius: width*0.5 + antialiasing: true + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: { + if (targetDir !== "") { + camera.imageCapture.captureToLocation(photoPanel.targetDir); + } else { + // saved to default location - TODO handle this case + camera.imageCapture.capture(); + } + photoPreview.visible = true; + } + } + + Image { + id: captureBtnImage + fillMode: Image.PreserveAspectFit + anchors.centerIn: parent + sourceSize.height: captureBtn.height/2 + height: captureBtn.height/2 + source: photoPanel.captureBtnIcon + } + + } + + Image { + id: photoPreview + width: parent.width + height: parent.height + fillMode: Image.PreserveAspectFit + + // Cancel button + Rectangle { + id: cancelBtn + visible: camera.imageCapture.capturedImagePath != "" + + property int borderWidth: 10 * QgsQuick.Utils.dp + width: parent.width/20 + height: parent.width/20 + color: photoPanel.bgColor + border.color: photoPanel.borderColor + anchors.right: parent.right + anchors.top: confirmBtn.bottom + border.width: borderWidth + radius: width*0.5 + antialiasing: true + + MouseArea { + anchors.fill: parent + onClicked: { + captureItem.saveImage = false + photoPreview.visible = false + if (camera.imageCapture.capturedImagePath != "") { + QgsQuick.Utils.remove(camera.imageCapture.capturedImagePath) + } + } + } + + Image { + fillMode: Image.PreserveAspectFit + anchors.centerIn: parent + sourceSize.height: captureBtn.height/2 + height: captureBtn.height/2 + source: photoPanel.cancelBtnIcon + } + } + + // OK button + Rectangle { + id: confirmBtn + visible: camera.imageCapture.capturedImagePath != "" + + property int borderWidth: 10 * QgsQuick.Utils.dp + width: parent.width/20 + height: parent.width/20 + color: photoPanel.bgColor + border.color: photoPanel.borderColor + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + border.width: borderWidth + radius: width*0.5 + antialiasing: true + + MouseArea { + anchors.fill: parent + onClicked: { + captureItem.saveImage = true + photoPanel.visible = false + photoPanel.lastPhotoName = QgsQuick.Utils.getFileName(camera.imageCapture.capturedImagePath) + if (photoPanel.lastPhotoName !== "") { + fieldItem.image.source = photoPanel.targetDir + "/" + photoPanel.lastPhotoName + fieldItem.valueChanged(photoPanel.lastPhotoName, photoPanel.lastPhotoName === "" || photoPanel.lastPhotoName === null) + } + } + } + + Image { + fillMode: Image.PreserveAspectFit + anchors.centerIn: parent + sourceSize.height: captureBtn.height/2 + height: captureBtn.height/2 + source: photoPanel.okBtnIcon + } + } + } + } + } +} + diff --git a/src/quickgui/plugin/qgsquickplugin.cpp b/src/quickgui/plugin/qgsquickplugin.cpp new file mode 100644 index 000000000000..7e3d176c9559 --- /dev/null +++ b/src/quickgui/plugin/qgsquickplugin.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + qgsquickplugin.cpp + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 + +#include "qgsfeature.h" +#include "qgsmaplayer.h" +#include "qgsmessagelog.h" +#include "qgspointxy.h" +#include "qgsproject.h" +#include "qgsrelationmanager.h" +#include "qgscoordinatetransformcontext.h" +#include "qgsvectorlayer.h" + +#include "qgsquickattributeformmodel.h" +#include "qgsquickattributeformmodelbase.h" +#include "qgsquickcoordinatetransformer.h" +#include "qgsquickfeaturemodel.h" +#include "qgsquickfeaturehighlight.h" +#include "qgsquickidentifykit.h" +#include "qgsquickfeature.h" +#include "qgsquickmapcanvasmap.h" +#include "qgsquickmapsettings.h" +#include "qgsquickmaptransform.h" +#include "qgsquickmessagelogmodel.h" +#include "qgsquickplugin.h" +#include "qgsquickpositionkit.h" +#include "qgsquickscalebarkit.h" +#include "qgsquicksubmodel.h" +#include "qgsquickutils.h" + +static QObject *_utilsProvider( QQmlEngine *engine, QJSEngine *scriptEngine ) +{ + Q_UNUSED( engine ) + Q_UNUSED( scriptEngine ) + return QgsQuickUtils::instance(); // the object will be owned by QML engine and destroyed by the engine on exit +} + +void QgisQuickPlugin::registerTypes( const char *uri ) +{ + qDebug( "REGISTERING QQmlExtensionInterface: QgisQuick" ); + + qRegisterMetaType< QList >( "QList" ); + qRegisterMetaType< QgsAttributes > ( "QgsAttributes" ); + qRegisterMetaType< QgsCoordinateReferenceSystem >( "QgsCoordinateReferenceSystem" ); + qRegisterMetaType< QgsCoordinateTransformContext >( "QgsCoordinateTransformContext" ); + qRegisterMetaType< QgsFeature > ( "QgsFeature " ); + qRegisterMetaType< QgsFeatureId > ( "QgsFeatureId" ); + qRegisterMetaType< QgsPoint >( "QgsPoint" ); + qRegisterMetaType< QgsPointXY >( "QgsPointXY" ); + qRegisterMetaType< QgsQuickFeature >( "QgsQuickFeature" ); + + qmlRegisterType< QgsProject >( uri, 0, 1, "Project" ); + qmlRegisterType< QgsQuickAttributeFormModel >( uri, 0, 1, "AttributeFormModel" ); + qmlRegisterType< QgsQuickCoordinateTransformer >( uri, 0, 1, "CoordinateTransformer" ); + qmlRegisterType< QgsQuickFeatureModel >( uri, 0, 1, "FeatureModel" ); + qmlRegisterType< QgsQuickFeatureHighlight >( uri, 0, 1, "FeatureHighlight" ); + qmlRegisterType< QgsQuickIdentifyKit >( uri, 0, 1, "IdentifyKit" ); + qmlRegisterType< QgsQuickMapCanvasMap >( uri, 0, 1, "MapCanvasMap" ); + qmlRegisterType< QgsQuickMapSettings >( uri, 0, 1, "MapSettings" ); + qmlRegisterType< QgsQuickMapTransform >( uri, 0, 1, "MapTransform" ); + qmlRegisterType< QgsQuickMessageLogModel >( uri, 0, 1, "MessageLogModel" ); + qmlRegisterType< QgsQuickPositionKit >( uri, 0, 1, "PositionKit" ); + qmlRegisterType< QgsQuickScaleBarKit >( uri, 0, 1, "ScaleBarKit" ); + qmlRegisterType< QgsQuickSubModel >( uri, 0, 1, "SubModel" ); + qmlRegisterType< QgsRelationManager >( uri, 0, 1, "RelationManager" ); + qmlRegisterType< QgsVectorLayer >( uri, 0, 1, "VectorLayer" ); + + qmlRegisterSingletonType< QgsQuickUtils >( uri, 0, 1, "Utils", _utilsProvider ); + + qmlRegisterUncreatableType< QgsMessageLog >( uri, 0, 1, "QgsMessageLog", "Expose MessageLevel" ); + + qDebug( "REGISTERING FINISHED" ); +} + diff --git a/src/quickgui/plugin/qgsquickplugin.h b/src/quickgui/plugin/qgsquickplugin.h new file mode 100644 index 000000000000..edab34634be7 --- /dev/null +++ b/src/quickgui/plugin/qgsquickplugin.h @@ -0,0 +1,34 @@ +/*************************************************************************** + qgsquickplugin.h + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 QGSQUICKPLUGIN_H +#define QGSQUICKPLUGIN_H + +#include +#include +#include +#include + +class QgisQuickPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA( IID "org.qt-project.Qt.QQmlExtensionInterface" ) + + public: + void registerTypes( const char *uri ); +}; + +#endif // QGSQUICKPLUGIN_H + diff --git a/src/quickgui/plugin/qgsquickpositionmarker.qml b/src/quickgui/plugin/qgsquickpositionmarker.qml new file mode 100644 index 000000000000..14c31be0eb45 --- /dev/null +++ b/src/quickgui/plugin/qgsquickpositionmarker.qml @@ -0,0 +1,158 @@ +/*************************************************************************** + qgsquickpositionmarker.qml + -------------------------------------- + Date : Dec 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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. * + * * + ***************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 2.2 +import QtQml 2.2 +import QtGraphicalEffects 1.0 +import QgisQuick 0.1 as QgsQuick + +Item { + id: positionMarker + property int size: 48 * QgsQuick.Utils.dp + + property QgsQuick.MapSettings mapSettings // required to be connected from parent! + + property var simulatePositionLongLatRad // use in debug mode to simulate movement around some GPS location + // longitude, latitude, and radius, all in degrees WSG84 + // e.g. simulatePositionLongLatRad = [60, 10, 0.02] + + property point screenPosition // in pixels + property color baseColor: "darkblue" + property color unavailableColor: "gray" + property alias mapPosition: wgs84toMapCrs.projectedPosition // in map coordinates + property alias gpsPosition: positionKit.position // in WGS84 coordinates + property alias gpsAccuracy: positionKit.accuracy // in meters + property alias positionKit: positionKit + property var gpsPositionLabel: { + if (positionKit.hasPosition) { + QgsQuick.Utils.qgsPointToString(positionKit.position, 3) // e.g -2.243, 45.441 + } else { + "GPS signal lost" + } + } + property var gpsAccuracyLabel: { + if (positionMarker.withAccuracy) { + if (positionKit.hasPosition && positionKit.accuracy > 0) { + QgsQuick.Utils.distanceToString(positionKit.accuracy, 0) // e.g 1 km or 15 m or 500 mm + } else { + "GPS accuracy N/A" + } + } else { + "" + } + } + property var withAccuracy: true + property var markerIcon: QgsQuick.Utils.getThemeIcon("ic_navigation_black") + + onMapSettingsChanged: update_location() + + Connections { + target: mapSettings + onVisibleExtentChanged: update_location() + } + + function update_location() { + if (mapSettings) + screenPosition = mapSettings.coordinateToScreen(wgs84toMapCrs.projectedPosition) + } + + onSimulatePositionLongLatRadChanged: { + if (simulatePositionLongLatRad) { + var longitude = simulatePositionLongLatRad[0] + var latitude = simulatePositionLongLatRad[1] + var radius = simulatePositionLongLatRad[2] + console.log("Use simulated position around longlat: " + longitude + ", " + latitude + ", radius " + radius ) + positionKit.use_simulated_location(longitude, latitude, radius); + } else { + positionKit.use_gps_location(); + } + } + + QgsQuick.CoordinateTransformer { + id: wgs84toMapCrs + sourceCrs: QgsQuick.Utils.coordinateReferenceSystemFromEpsgId(4326) + destinationCrs: mapSettings.destinationCrs + sourcePosition: positionKit.position + mapSettings: positionMarker.mapSettings + onProjectedPositionChanged: update_location() + } + + QgsQuick.PositionKit { + id: positionKit + } + + // GPS accuracy circle-shaped indicator around positionMarker + Rectangle { + id: accuracyIndicator + visible: mapSettings && + withAccuracy && + positionKit.hasPosition && + (positionKit.accuracy > 0) && + (accuracyIndicator.width > positionMarker.size / 2.0) + x: positionMarker.screenPosition.x - width/2 + y: positionMarker.screenPosition.y - height/2 + width: { + if (positionKit.accuracy > 0) { + var scpm = QgsQuick.Utils.screenUnitsToMeters(positionMarker.mapSettings, 1) // scpm is how much meters is 1 pixel + if (scpm > 0) + 2 * ( positionKit.accuracy / scpm ) + else + 2 + } + else 2 + } + height: accuracyIndicator.width + color: baseColor + border.color: "black" + border.width: 3 + radius: width*0.5 + opacity: 0.1 + } + + Rectangle { + id: navigationMarker + property int borderWidth: 2 + width: positionMarker.size + 20 + height: width + color: "white" + border.color: baseColor + border.width: borderWidth + radius: width*0.5 + antialiasing: true + x: positionMarker.screenPosition.x - width/2 + y: positionMarker.screenPosition.y - height/2 + + Image { + id: navigation + source: positionMarker.markerIcon + fillMode: Image.PreserveAspectFit + rotation: positionKit.direction + anchors.centerIn: parent + width: positionMarker.size + height: width + } + + // Makes positionMarker (navigation) grey if gps signal is lost. + ColorOverlay { + anchors.fill: navigation + source: navigation + color: positionKit.hasPosition ? baseColor : unavailableColor + rotation: positionKit.direction + visible: !(positionKit.hasPosition && baseColor == "black") + } + } + +} diff --git a/src/quickgui/plugin/qgsquickscalebar.qml b/src/quickgui/plugin/qgsquickscalebar.qml new file mode 100644 index 000000000000..9e8ed6f90bdb --- /dev/null +++ b/src/quickgui/plugin/qgsquickscalebar.qml @@ -0,0 +1,105 @@ +/*************************************************************************** + qgsquickscalebar.qml + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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. * + * * + ***************************************************************************/ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QgisQuick 0.1 as QgsQuick + +Item { + id: scaleBar + property alias mapSettings: scaleBarKit.mapSettings + property alias preferredWidth: scaleBarKit.preferredWidth + + QgsQuick.ScaleBarKit { + id: scaleBarKit + } + + property int textWidth: fontMetrics.averageCharacterWidth * 8 + property color barColor: "white" + property color barBackgroundColor: "grey" + property double barOpacity: 0.8 + property string barText: scaleBarKit.distance + " " + scaleBarKit.units + property int barWidth: scaleBarKit.width + property int lineWidth: 5 * QgsQuick.Utils.dp + + width: textWidth + barWidth + + MouseArea { + anchors.fill: background + onClicked: { + animation.restart() + } + } + + NumberAnimation { + id: animation + target: scaleBar + property: "barWidth" + to: 200 + duration: 1000 + } + + Rectangle { + id: background + color: scaleBar.barBackgroundColor + opacity: scaleBar.barOpacity + width: parent.width + height: parent.height + } + + FontMetrics { + id: fontMetrics + font: text.font + } + + Row { + opacity: 1 + spacing: 0 + + Text { + id: text + width: textWidth + height: scaleBar.height + text: barText + color: barColor + font.pixelSize: scaleBar.height - 2 * scaleBar.lineWidth + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Rectangle { + id: leftBar + width: scaleBar.lineWidth + height: scaleBar.height - 20 * QgsQuick.Utils.dp + y: (scaleBar.height - leftBar.height) / 2 + color: barColor + opacity: 1 + } + + Rectangle { + width: scaleBar.width - text.width - 15 * QgsQuick.Utils.dp + height: scaleBar.lineWidth + y: (scaleBar.height - scaleBar.lineWidth) / 2 + color: barColor + } + + Rectangle { + id: rightBar + width: scaleBar.lineWidth + height: scaleBar.height - 20 * QgsQuick.Utils.dp + y: (scaleBar.height - leftBar.height) / 2 + color: barColor + } + } +} diff --git a/src/quickgui/plugin/qmldir b/src/quickgui/plugin/qmldir new file mode 100644 index 000000000000..b988324f65b7 --- /dev/null +++ b/src/quickgui/plugin/qmldir @@ -0,0 +1,24 @@ +# qmldir +# -------------------------------------- +# Date : Nov 2017 +# Copyright : (C) 2017 by Peter Petrik +# Email : zilolv 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. + + +module QgisQuick +plugin qgis_quick_plugin + +MapCanvas 0.1 qgsquickmapcanvas.qml +FeatureForm 0.1 qgsquickfeatureform.qml +FeatureFormStyling 0.1 qgsquickfeatureformstyling.qml +PositionMarker 0.1 qgsquickpositionmarker.qml +ScaleBar 0.1 qgsquickscalebar.qml +PhotoCapture 0.1 qgsquickphotopanel.qml +MessageLog 0.1 qgsquickmessagelog.qml + +typeinfo qgisquick.qmltypes diff --git a/src/quickgui/qgis_quick.h b/src/quickgui/qgis_quick.h new file mode 100644 index 000000000000..d98a615a8f6f --- /dev/null +++ b/src/quickgui/qgis_quick.h @@ -0,0 +1,33 @@ +/*************************************************************************** + qgis_quick.h + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 QGIS_QUICK_H +#define QGIS_QUICK_H + +#ifndef QUICK_EXPORT +# ifdef qgis_quick_EXPORTS +/* We are building this library */ +# define QUICK_EXPORT __attribute__((visibility("default"))) +# else +/* We are using this library */ +# define QUICK_EXPORT __attribute__((visibility("default"))) +# endif +#endif //QUICK_EXPORT + +#ifndef QUICK_NO_EXPORT +# define QUICK_NO_EXPORT __attribute__((visibility("hidden"))) +#endif + +#endif // QGIS_QUICK_H diff --git a/src/quickgui/qgsquickattributeformmodel.cpp b/src/quickgui/qgsquickattributeformmodel.cpp new file mode 100644 index 000000000000..69396fb9f3ce --- /dev/null +++ b/src/quickgui/qgsquickattributeformmodel.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** + qgsquickattributeformmodel.cpp + -------------------------------------- + Date : 22.9.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 "qgsquickattributeformmodel.h" +#include "qgsquickattributeformmodelbase.h" + +QgsQuickAttributeFormModel::QgsQuickAttributeFormModel( QObject *parent ) + : QSortFilterProxyModel( parent ) + , mSourceModel( new QgsQuickAttributeFormModelBase( this ) ) +{ + setSourceModel( mSourceModel ); + connect( mSourceModel, &QgsQuickAttributeFormModelBase::hasTabsChanged, this, &QgsQuickAttributeFormModel::hasTabsChanged ); + connect( mSourceModel, &QgsQuickAttributeFormModelBase::featureModelChanged, this, &QgsQuickAttributeFormModel::featureModelChanged ); + connect( mSourceModel, &QgsQuickAttributeFormModelBase::featureChanged, this, &QgsQuickAttributeFormModel::featureChanged ); + connect( mSourceModel, &QgsQuickAttributeFormModelBase::constraintsValidChanged, this, &QgsQuickAttributeFormModel::constraintsValidChanged ); +} + +bool QgsQuickAttributeFormModel::hasTabs() const +{ + return mSourceModel->hasTabs(); +} + +void QgsQuickAttributeFormModel::setHasTabs( bool hasTabs ) +{ + mSourceModel->setHasTabs( hasTabs ); +} + +QgsQuickFeatureModel *QgsQuickAttributeFormModel::featureModel() const +{ + return mSourceModel->featureModel(); +} + +void QgsQuickAttributeFormModel::setFeatureModel( QgsQuickFeatureModel *featureModel ) +{ + mSourceModel->setFeatureModel( featureModel ); +} + +bool QgsQuickAttributeFormModel::constraintsValid() const +{ + return mSourceModel->constraintsValid(); +} + +void QgsQuickAttributeFormModel::save() +{ + mSourceModel->save(); +} + +void QgsQuickAttributeFormModel::create() +{ + mSourceModel->create(); +} + +QVariant QgsQuickAttributeFormModel::attribute( const QString &name ) +{ + return mSourceModel->attribute( name ); +} + +bool QgsQuickAttributeFormModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const +{ + return mSourceModel->data( mSourceModel->index( source_row, 0, source_parent ), CurrentlyVisible ).toBool(); +} diff --git a/src/quickgui/qgsquickattributeformmodel.h b/src/quickgui/qgsquickattributeformmodel.h new file mode 100644 index 000000000000..c27809fdc34b --- /dev/null +++ b/src/quickgui/qgsquickattributeformmodel.h @@ -0,0 +1,97 @@ +/*************************************************************************** + qgsquickattributeformmodel.h + -------------------------------------- + Date : 22.9.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKATTRIBUTEFORMMODEL_H +#define QGSQUICKATTRIBUTEFORMMODEL_H + +#include + +#include "qgis_quick.h" + +class QgsQuickAttributeFormModelBase; +class QgsQuickFeatureModel; + +/** + * \ingroup quick + * This is a model implementation for attribute form of a feature from a vector layer. + * + * The model is based on vector layer's edit form config (QgsEditFormConfig). It supports + * auto-generated editor layouts and "tab" layout (layout defined with groups and tabs). + * The form layout gets flattened into a list, each row has a bunch of roles with values + * extracted from the edit form config. + * + * It also adds filtering of attribute (attributes may be visible or hidden based on expressions). + * + * \note QML Type: AttributeFormModel + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel +{ + Q_OBJECT + + Q_PROPERTY( QgsQuickFeatureModel *featureModel READ featureModel WRITE setFeatureModel NOTIFY featureModelChanged ) + Q_PROPERTY( bool hasTabs READ hasTabs WRITE setHasTabs NOTIFY hasTabsChanged ) + Q_PROPERTY( bool constraintsValid READ constraintsValid NOTIFY constraintsValidChanged ) + + public: + enum FeatureRoles + { + ElementType = Qt::UserRole + 1, + Name, + AttributeValue, + AttributeEditable, + EditorWidget, + EditorWidgetConfig, + RememberValue, + Field, + FieldIndex, + Group, + AttributeEditorElement, + CurrentlyVisible, + ConstraintValid, + ConstraintDescription + }; + + Q_ENUM( FeatureRoles ) + + QgsQuickAttributeFormModel( QObject *parent = nullptr ); + + bool hasTabs() const; + void setHasTabs( bool hasTabs ); + + QgsQuickFeatureModel *featureModel() const; + void setFeatureModel( QgsQuickFeatureModel *featureModel ); + + bool constraintsValid() const; + + Q_INVOKABLE void save(); + Q_INVOKABLE void create(); + Q_INVOKABLE QVariant attribute( const QString &name ); + + signals: + void featureModelChanged(); + void hasTabsChanged(); + void featureChanged(); + void constraintsValidChanged(); + + protected: + virtual bool filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const override; + + private: + QgsQuickAttributeFormModelBase *mSourceModel; +}; + +#endif // QGSQUICKATTRIBUTEFORMMODEL_H diff --git a/src/quickgui/qgsquickattributeformmodelbase.cpp b/src/quickgui/qgsquickattributeformmodelbase.cpp new file mode 100644 index 000000000000..6af4ae565cbe --- /dev/null +++ b/src/quickgui/qgsquickattributeformmodelbase.cpp @@ -0,0 +1,397 @@ +/*************************************************************************** + qgsquickfeaturemodelbase.cpp + -------------------------------------- + Date : 16.8.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 "qgseditorwidgetsetup.h" +#include "qgsvectorlayer.h" + +#include "qgsquickattributeformmodelbase.h" +#include "qgsquickattributeformmodel.h" + +/// @cond PRIVATE + +QgsQuickAttributeFormModelBase::QgsQuickAttributeFormModelBase( QObject *parent ) + : QStandardItemModel( 0, 1, parent ) + , mFeatureModel( nullptr ) + , mLayer( nullptr ) + , mTemporaryContainer( nullptr ) +{ +} + +QgsQuickAttributeFormModelBase::~QgsQuickAttributeFormModelBase() +{ + delete mTemporaryContainer; +} + +QHash QgsQuickAttributeFormModelBase::roleNames() const +{ + QHash roles = QAbstractItemModel::roleNames(); + + roles[QgsQuickAttributeFormModel::ElementType] = "Type"; + roles[QgsQuickAttributeFormModel::Name] = "Name"; + roles[QgsQuickAttributeFormModel::AttributeValue] = "AttributeValue"; + roles[QgsQuickAttributeFormModel::AttributeEditable] = "AttributeEditable"; + roles[QgsQuickAttributeFormModel::EditorWidget] = "EditorWidget"; + roles[QgsQuickAttributeFormModel::EditorWidgetConfig] = "EditorWidgetConfig"; + roles[QgsQuickAttributeFormModel::RememberValue] = "RememberValue"; + roles[QgsQuickAttributeFormModel::Field] = "Field"; + roles[QgsQuickAttributeFormModel::Group] = "Group"; + roles[QgsQuickAttributeFormModel::ConstraintValid] = "ConstraintValid"; + roles[QgsQuickAttributeFormModel::ConstraintDescription] = "ConstraintDescription"; + + return roles; +} + +bool QgsQuickAttributeFormModelBase::setData( const QModelIndex &index, const QVariant &value, int role ) +{ + if ( data( index, role ) != value ) + { + switch ( role ) + { + case QgsQuickAttributeFormModel::RememberValue: + { + QStandardItem *item = itemFromIndex( index ); + int fieldIndex = item->data( QgsQuickAttributeFormModel::FieldIndex ).toInt(); + mFeatureModel->setData( mFeatureModel->index( fieldIndex ), value, QgsQuickFeatureModel::RememberAttribute ); + item->setData( value, QgsQuickAttributeFormModel::RememberValue ); + break; + } + + case QgsQuickAttributeFormModel::AttributeValue: + { + QStandardItem *item = itemFromIndex( index ); + int fieldIndex = item->data( QgsQuickAttributeFormModel::FieldIndex ).toInt(); + bool changed = mFeatureModel->setData( mFeatureModel->index( fieldIndex ), value, QgsQuickFeatureModel::AttributeValue ); + if ( changed ) + { + item->setData( value, QgsQuickAttributeFormModel::AttributeValue ); + emit dataChanged( index, index, QVector() << role ); + } + updateVisibility( fieldIndex ); + return changed; + break; + } + } + } + return false; +} + +QgsQuickFeatureModel *QgsQuickAttributeFormModelBase::featureModel() const +{ + return mFeatureModel; +} + +void QgsQuickAttributeFormModelBase::setFeatureModel( QgsQuickFeatureModel *featureModel ) +{ + if ( mFeatureModel == featureModel ) + return; + + if ( mFeatureModel ) + { + disconnect( mFeatureModel, &QgsQuickFeatureModel::layerChanged, this, &QgsQuickAttributeFormModelBase::onLayerChanged ); + disconnect( mFeatureModel, &QgsQuickFeatureModel::featureChanged, this, &QgsQuickAttributeFormModelBase::onFeatureChanged ); + disconnect( mFeatureModel, &QgsQuickFeatureModel::modelReset, this, &QgsQuickAttributeFormModelBase::onFeatureChanged ); + } + + mFeatureModel = featureModel; + + if ( mFeatureModel ) + { + connect( mFeatureModel, &QgsQuickFeatureModel::layerChanged, this, &QgsQuickAttributeFormModelBase::onLayerChanged ); + connect( mFeatureModel, &QgsQuickFeatureModel::featureChanged, this, &QgsQuickAttributeFormModelBase::onFeatureChanged ); + connect( mFeatureModel, &QgsQuickFeatureModel::modelReset, this, &QgsQuickAttributeFormModelBase::onFeatureChanged ); + } + + emit featureModelChanged(); +} + +void QgsQuickAttributeFormModelBase::onLayerChanged() +{ + clear(); + + mLayer = mFeatureModel->layer(); + mVisibilityExpressions.clear(); + mConstraints.clear(); + + if ( mLayer ) + { + QgsAttributeEditorContainer *root; + delete mTemporaryContainer; + mTemporaryContainer = nullptr; + + if ( mLayer->editFormConfig().layout() == QgsEditFormConfig::TabLayout ) + { + root = mLayer->editFormConfig().invisibleRootContainer(); + } + else + { + root = generateRootContainer(); + mTemporaryContainer = root; + } + + setHasTabs( !root->children().isEmpty() && QgsAttributeEditorElement::AeTypeContainer == root->children().first()->type() ); + + invisibleRootItem()->setColumnCount( 1 ); + if ( mHasTabs ) + { + Q_FOREACH ( QgsAttributeEditorElement *element, root->children() ) + { + if ( element->type() == QgsAttributeEditorElement::AeTypeContainer ) + { + QgsAttributeEditorContainer *container = static_cast( element ); + + QStandardItem *item = new QStandardItem(); + item->setData( element->name(), QgsQuickAttributeFormModel::Name ); + item->setData( "container", QgsQuickAttributeFormModel::ElementType ); + item->setData( true, QgsQuickAttributeFormModel::CurrentlyVisible ); + invisibleRootItem()->appendRow( item ); + + if ( container->visibilityExpression().enabled() ) + { + mVisibilityExpressions.append( qMakePair( container->visibilityExpression().data(), QVector() << item ) ); + } + + QVector dummy; + flatten( container, item, QString(), dummy ); + } + } + } + else + { + QVector dummy; + flatten( invisibleRootContainer(), invisibleRootItem(), QString(), dummy ); + } + + mExpressionContext = mLayer->createExpressionContext(); + } +} + +void QgsQuickAttributeFormModelBase::onFeatureChanged() +{ + for ( int i = 0 ; i < invisibleRootItem()->rowCount(); ++i ) + { + updateAttributeValue( invisibleRootItem()->child( i ) ); + } + + updateVisibility(); +} + +QgsAttributeEditorContainer *QgsQuickAttributeFormModelBase::generateRootContainer() const +{ + QgsAttributeEditorContainer *root = new QgsAttributeEditorContainer( QString(), nullptr ); + QgsFields fields = mLayer->fields(); + for ( int i = 0; i < fields.size(); ++i ) + { + if ( fields.at( i ).editorWidgetSetup().type() != QStringLiteral( "Hidden" ) ) + { + QgsAttributeEditorField *field = new QgsAttributeEditorField( fields.at( i ).name(), i, root ); + root->addChildElement( field ); + } + } + return root; +} + +QgsAttributeEditorContainer *QgsQuickAttributeFormModelBase::invisibleRootContainer() const +{ + return mTemporaryContainer ? mTemporaryContainer : mLayer->editFormConfig().invisibleRootContainer(); +} + +void QgsQuickAttributeFormModelBase::updateAttributeValue( QStandardItem *item ) +{ + if ( item->data( QgsQuickAttributeFormModel::ElementType ) == "field" ) + { + item->setData( mFeatureModel->feature().attribute( item->data( QgsQuickAttributeFormModel::FieldIndex ).toInt() ), QgsQuickAttributeFormModel::AttributeValue ); + } + else + { + for ( int i = 0; i < item->rowCount(); ++i ) + { + updateAttributeValue( item->child( i ) ); + } + } +} + +void QgsQuickAttributeFormModelBase::flatten( QgsAttributeEditorContainer *container, QStandardItem *parent, const QString &visibilityExpressions, QVector &items ) +{ + QString visibilityExpression = visibilityExpressions; + + Q_FOREACH ( QgsAttributeEditorElement *element, container->children() ) + { + switch ( element->type() ) + { + case QgsAttributeEditorElement::AeTypeContainer: + { + QgsAttributeEditorContainer *container = static_cast( element ); + if ( container->visibilityExpression().enabled() ) + { + if ( visibilityExpression.isNull() ) + visibilityExpression = container->visibilityExpression().data().expression(); + else + visibilityExpression += " AND " + container->visibilityExpression().data().expression(); + } + + QVector newItems; + flatten( container, parent, visibilityExpression, newItems ); + if ( !visibilityExpression.isEmpty() ) + mVisibilityExpressions.append( qMakePair( QgsExpression( visibilityExpression ), newItems ) ); + break; + } + + case QgsAttributeEditorElement::AeTypeField: + { + QgsAttributeEditorField *editorField = static_cast( element ); + int fieldIndex = editorField->idx(); + if ( fieldIndex < 0 || fieldIndex >= mLayer->fields().size() ) + continue; + + QgsField field = mLayer->fields().at( fieldIndex ); + + QStandardItem *item = new QStandardItem(); + + + item->setData( mLayer->attributeDisplayName( fieldIndex ), QgsQuickAttributeFormModel::Name ); + item->setData( mFeatureModel->feature().attribute( fieldIndex ), QgsQuickAttributeFormModel::AttributeValue ); + item->setData( !mLayer->editFormConfig().readOnly( fieldIndex ), QgsQuickAttributeFormModel::AttributeEditable ); + QgsEditorWidgetSetup setup = mLayer->editorWidgetSetup( fieldIndex ); + item->setData( setup.type(), QgsQuickAttributeFormModel::EditorWidget ); + item->setData( setup.config(), QgsQuickAttributeFormModel::EditorWidgetConfig ); + item->setData( mFeatureModel->rememberedAttributes().at( fieldIndex ) ? Qt::Checked : Qt::Unchecked, QgsQuickAttributeFormModel::RememberValue ); + item->setData( mLayer->fields().at( fieldIndex ), QgsQuickAttributeFormModel::Field ); + item->setData( "field", QgsQuickAttributeFormModel::ElementType ); + item->setData( fieldIndex, QgsQuickAttributeFormModel::FieldIndex ); + item->setData( container->isGroupBox() ? container->name() : QString(), QgsQuickAttributeFormModel::Group ); + item->setData( true, QgsQuickAttributeFormModel::CurrentlyVisible ); + item->setData( true, QgsQuickAttributeFormModel::ConstraintValid ); + item->setData( field.constraints().constraintDescription(), QgsQuickAttributeFormModel::ConstraintDescription ); + + if ( !field.constraints().constraintExpression().isEmpty() ) + { + mConstraints.insert( item, field.constraints().constraintExpression() ); + } + + items.append( item ); + + parent->appendRow( item ); + break; + } + + case QgsAttributeEditorElement::AeTypeRelation: + // todo + break; + + case QgsAttributeEditorElement::AeTypeInvalid: + // todo + break; + } + } +} + +void QgsQuickAttributeFormModelBase::updateVisibility( int fieldIndex ) +{ + QgsFields fields = mFeatureModel->feature().fields(); + mExpressionContext.setFields( fields ); + mExpressionContext.setFeature( mFeatureModel->feature() ); + + Q_FOREACH ( const VisibilityExpression &it, mVisibilityExpressions ) + { + if ( fieldIndex == -1 || it.first.referencedAttributeIndexes( fields ).contains( fieldIndex ) ) + { + QgsExpression exp = it.first; + exp.prepare( &mExpressionContext ); + + bool visible = exp.evaluate( &mExpressionContext ).toInt(); + Q_FOREACH ( QStandardItem *item, it.second ) + { + if ( item->data( QgsQuickAttributeFormModel::CurrentlyVisible ).toBool() != visible ) + { + item->setData( visible, QgsQuickAttributeFormModel::CurrentlyVisible ); + } + } + } + } + + bool allConstraintsValid = true; + QMap::ConstIterator constraintIterator( mConstraints.constBegin() ); + for ( ; constraintIterator != mConstraints.constEnd(); ++constraintIterator ) + { + QStandardItem *item = constraintIterator.key(); + if ( item->data( QgsQuickAttributeFormModel::FieldIndex ) == fieldIndex || fieldIndex == -1 ) + { + QgsExpression exp = constraintIterator.value(); + exp.prepare( &mExpressionContext ); + bool constraintSatisfied = exp.evaluate( &mExpressionContext ).toBool(); + + if ( constraintSatisfied != item->data( QgsQuickAttributeFormModel::ConstraintValid ).toBool() ) + { + item->setData( constraintSatisfied, QgsQuickAttributeFormModel::ConstraintValid ); + } + } + + if ( !item->data( QgsQuickAttributeFormModel::ConstraintValid ).toBool() ) + { + allConstraintsValid = false; + } + } + + setConstraintsValid( allConstraintsValid ); +} + +bool QgsQuickAttributeFormModelBase::constraintsValid() const +{ + return mConstraintsValid; +} + +QVariant QgsQuickAttributeFormModelBase::attribute( const QString &name ) +{ + if ( !mLayer ) + return QVariant(); + + int idx = mLayer->fields().indexOf( name ); + return mFeatureModel->feature().attribute( idx ); +} + +void QgsQuickAttributeFormModelBase::setConstraintsValid( bool constraintsValid ) +{ + if ( constraintsValid == mConstraintsValid ) + return; + + mConstraintsValid = constraintsValid; + emit constraintsValidChanged(); +} + +bool QgsQuickAttributeFormModelBase::hasTabs() const +{ + return mHasTabs; +} + +void QgsQuickAttributeFormModelBase::setHasTabs( bool hasTabs ) +{ + if ( hasTabs == mHasTabs ) + return; + + mHasTabs = hasTabs; + emit hasTabsChanged(); +} + +void QgsQuickAttributeFormModelBase::save() +{ + mFeatureModel->save(); +} + +void QgsQuickAttributeFormModelBase::create() +{ + mFeatureModel->create(); +} + +/// @endcond diff --git a/src/quickgui/qgsquickattributeformmodelbase.h b/src/quickgui/qgsquickattributeformmodelbase.h new file mode 100644 index 000000000000..3ec80f3e3731 --- /dev/null +++ b/src/quickgui/qgsquickattributeformmodelbase.h @@ -0,0 +1,120 @@ +/*************************************************************************** + qgsquickfeaturemodelbase.h + -------------------------------------- + Date : 16.8.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKATTRIBUTEFORMMODELBASE_H +#define QGSQUICKATTRIBUTEFORMMODELBASE_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 +#include + +#include "qgseditformconfig.h" +#include "qgsexpressioncontext.h" + +#include "qgis_quick.h" +#include "qgsquickfeaturemodel.h" + +/** + * \ingroup quick + * This is an internal (implementation) class used as the source model for QgsQuickAttributeFormModel. + * + * \sa QgsQuickAttributeFormModel + * + * \since QGIS 3.2 + */ +class QgsQuickAttributeFormModelBase : public QStandardItemModel +{ + Q_OBJECT + + Q_PROPERTY( QgsQuickFeatureModel *featureModel READ featureModel WRITE setFeatureModel NOTIFY featureModelChanged ) + Q_PROPERTY( bool hasTabs READ hasTabs WRITE setHasTabs NOTIFY hasTabsChanged ) + Q_PROPERTY( bool constraintsValid READ constraintsValid NOTIFY constraintsValidChanged ) + + public: + explicit QgsQuickAttributeFormModelBase( QObject *parent = nullptr ); + ~QgsQuickAttributeFormModelBase(); + + QHash roleNames() const override; + + bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override; + + QgsQuickFeatureModel *featureModel() const; + void setFeatureModel( QgsQuickFeatureModel *featureModel ); + + bool hasTabs() const; + void setHasTabs( bool hasTabs ); + + void save(); + + void create(); + + bool constraintsValid() const; + + QVariant attribute( const QString &name ); + + signals: + void featureModelChanged(); + void hasTabsChanged(); + void featureChanged(); + void constraintsValidChanged(); + + private slots: + void onLayerChanged(); + void onFeatureChanged(); + + private: + + /** + * Generates a root container for autogenerated layouts, so we can just use the same + * form logic to deal with them. + */ + QgsAttributeEditorContainer *generateRootContainer() const; + + QgsAttributeEditorContainer *invisibleRootContainer() const; + + void updateAttributeValue( QStandardItem *item ); + + void flatten( QgsAttributeEditorContainer *container, QStandardItem *parent, const QString &visibilityExpressions, QVector &items ); + + void updateVisibility( int fieldIndex = -1 ); + + void setConstraintsValid( bool constraintsValid ); + + QgsQuickFeatureModel *mFeatureModel; + QgsVectorLayer *mLayer; + QgsAttributeEditorContainer *mTemporaryContainer; + bool mHasTabs; + + typedef QPair > VisibilityExpression; + QList mVisibilityExpressions; + QMap mConstraints; + + QgsExpressionContext mExpressionContext; + bool mConstraintsValid; +}; + +/// @endcond + +#endif // QGSQUICKATTRIBUTEFORMMODELBASE_H diff --git a/src/quickgui/qgsquickcoordinatetransformer.cpp b/src/quickgui/qgsquickcoordinatetransformer.cpp new file mode 100644 index 000000000000..2572ab05ab4f --- /dev/null +++ b/src/quickgui/qgsquickcoordinatetransformer.cpp @@ -0,0 +1,103 @@ +/*************************************************************************** + qgsquickcoordinatetransformer.cpp + -------------------------------------- + Date : 1.6.2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 + +#include "qgsquickcoordinatetransformer.h" + +QgsQuickCoordinateTransformer::QgsQuickCoordinateTransformer( QObject *parent ) + : QObject( parent ) + , mMapSettings( nullptr ) +{ + mCoordinateTransform.setSourceCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ) ); + mCoordinateTransform.setContext( QgsCoordinateTransformContext() ); +} + +QgsPoint QgsQuickCoordinateTransformer::projectedPosition() const +{ + return mProjectedPosition; +} + +QgsPoint QgsQuickCoordinateTransformer::sourcePosition() const +{ + return mSourcePosition; +} + +void QgsQuickCoordinateTransformer::setSourcePosition( QgsPoint sourcePosition ) +{ + if ( mSourcePosition == sourcePosition ) + return; + + mSourcePosition = sourcePosition; + + emit sourcePositionChanged(); + updatePosition(); +} + +QgsCoordinateReferenceSystem QgsQuickCoordinateTransformer::destinationCrs() const +{ + return mCoordinateTransform.destinationCrs(); +} + +void QgsQuickCoordinateTransformer::setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ) +{ + if ( destinationCrs == mCoordinateTransform.destinationCrs() ) + return; + + mCoordinateTransform.setDestinationCrs( destinationCrs ); + emit destinationCrsChanged(); + updatePosition(); +} + +QgsCoordinateReferenceSystem QgsQuickCoordinateTransformer::sourceCrs() const +{ + return mCoordinateTransform.sourceCrs(); +} + +void QgsQuickCoordinateTransformer::setSourceCrs( const QgsCoordinateReferenceSystem &sourceCrs ) +{ + if ( sourceCrs == mCoordinateTransform.sourceCrs() ) + return; + + mCoordinateTransform.setSourceCrs( sourceCrs ); + + emit sourceCrsChanged(); + updatePosition(); +} + +void QgsQuickCoordinateTransformer::updatePosition() +{ + double x = mSourcePosition.x(); + double y = mSourcePosition.y(); + double z = mSourcePosition.z(); + + // If Z is NaN, coordinate transformation (proj4) will + // also set X and Y to NaN. But we also want to get projected + // coords if we do not have any Z coordinate. + if ( qIsNaN( z ) ) + { + z = 0; + } + + if ( mMapSettings ) + mCoordinateTransform.setContext( mMapSettings->transformContext() ); + + mCoordinateTransform.transformInPlace( x, y, z ); + + mProjectedPosition = QgsPoint( x, y ); + mProjectedPosition.addZValue( mSourcePosition.z() ); + + emit projectedPositionChanged(); +} diff --git a/src/quickgui/qgsquickcoordinatetransformer.h b/src/quickgui/qgsquickcoordinatetransformer.h new file mode 100644 index 000000000000..c9235bf62fcc --- /dev/null +++ b/src/quickgui/qgsquickcoordinatetransformer.h @@ -0,0 +1,74 @@ +/*************************************************************************** + qgsquickcoordinatetransformer.h + -------------------------------------- + Date : 1.6.2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKCOORDINATETRANSFORMER_H +#define QGSQUICKCOORDINATETRANSFORMER_H + +#include + +#include "qgspoint.h" + +#include "qgis_quick.h" +#include "qgsquickmapsettings.h" + +/** + * \ingroup quick + * Helper class for transform of coordinates (QgsPoint) to a different coordinate reference system. + * + * \note QML Type: CoordinateTransformer + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickCoordinateTransformer : public QObject +{ + Q_OBJECT + + Q_PROPERTY( QgsPoint projectedPosition READ projectedPosition NOTIFY projectedPositionChanged ) + Q_PROPERTY( QgsPoint sourcePosition READ sourcePosition WRITE setSourcePosition NOTIFY sourcePositionChanged ) + Q_PROPERTY( QgsCoordinateReferenceSystem destinationCrs READ destinationCrs WRITE setDestinationCrs NOTIFY destinationCrsChanged ) + Q_PROPERTY( QgsCoordinateReferenceSystem sourceCrs READ sourceCrs WRITE setSourceCrs NOTIFY sourceCrsChanged ) + Q_PROPERTY( QgsQuickMapSettings *mapSettings MEMBER mMapSettings NOTIFY mapSettingsChanged ) + + public: + explicit QgsQuickCoordinateTransformer( QObject *parent = 0 ); + QgsPoint projectedPosition() const; + + QgsPoint sourcePosition() const; + void setSourcePosition( QgsPoint sourcePosition ); + + QgsCoordinateReferenceSystem destinationCrs() const; + void setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ); + + QgsCoordinateReferenceSystem sourceCrs() const; + void setSourceCrs( const QgsCoordinateReferenceSystem &sourceCrs ); + + private: + void updatePosition(); + + signals: + void projectedPositionChanged(); + void sourcePositionChanged(); + void destinationCrsChanged(); + void sourceCrsChanged(); + void mapSettingsChanged(); + + private: + QgsPoint mProjectedPosition; + QgsPoint mSourcePosition; + QgsCoordinateTransform mCoordinateTransform; + QgsQuickMapSettings *mMapSettings; +}; + +#endif // QGSQUICKCOORDINATETRANSFORMER_H diff --git a/src/quickgui/qgsquickfeature.cpp b/src/quickgui/qgsquickfeature.cpp new file mode 100644 index 000000000000..a27bd055c129 --- /dev/null +++ b/src/quickgui/qgsquickfeature.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + qgsquickfeature.cpp + --------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 "qgsvectorlayer.h" +#include "qgsfeature.h" + +#include "qgsquickfeature.h" + +QgsQuickFeature::QgsQuickFeature( const QgsFeature &feature, QgsVectorLayer *layer ) + : mLayer( layer ) + , mFeature( feature ) +{ + if ( !mLayer ) + { + mFeature.setValid( false ); + } +} + +QgsQuickFeature::QgsQuickFeature(): mLayer( 0 ) +{ + mFeature.setValid( false ); +} + +QgsVectorLayer *QgsQuickFeature::layer() const +{ + return mLayer; +} + +QgsFeature QgsQuickFeature::feature() const +{ + return mFeature; +} + +bool QgsQuickFeature::valid() const +{ + return ( mLayer && mFeature.isValid() ); +} diff --git a/src/quickgui/qgsquickfeature.h b/src/quickgui/qgsquickfeature.h new file mode 100644 index 000000000000..6ce4e3b3eafc --- /dev/null +++ b/src/quickgui/qgsquickfeature.h @@ -0,0 +1,59 @@ +/*************************************************************************** + qgsquickfeature.h + --------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 QGSQUICKFEATURE_H +#define QGSQUICKFEATURE_H + +#include + +#include "qgsfeature.h" + +#include "qgis_quick.h" + +class QgsVectorLayer; + +/** + * \ingroup quick + * Helper class for QgsFeature and QgsVectorLayer where it belongs. + * + * \note QML Type: IdentifyResult + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickFeature +{ + Q_GADGET + + Q_PROPERTY( QgsVectorLayer *layer READ layer ) + Q_PROPERTY( QgsFeature feature READ feature ) + Q_PROPERTY( bool valid READ valid ) + + public: + QgsQuickFeature(); + QgsQuickFeature( const QgsFeature &feature, + QgsVectorLayer *layer ); + + QgsVectorLayer *layer() const; + QgsFeature feature() const; + bool valid() const; + + private: + QgsVectorLayer *mLayer; + QgsFeature mFeature; +}; + +Q_DECLARE_METATYPE( QgsQuickFeature ) + +#endif // QGSQUICKFEATURE_H diff --git a/src/quickgui/qgsquickfeaturehighlight.cpp b/src/quickgui/qgsquickfeaturehighlight.cpp new file mode 100644 index 000000000000..1b0d251a70e3 --- /dev/null +++ b/src/quickgui/qgsquickfeaturehighlight.cpp @@ -0,0 +1,85 @@ +/*************************************************************************** + qgsqguickfeaturehighlight.cpp + -------------------------------------- + Date : 9.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 "qgsvectorlayer.h" + +#include "qgsquickfeaturemodel.h" +#include "qgsquickfeaturehighlight.h" +#include "qgsquickmapsettings.h" +#include "qgsquickhighlightsgnode.h" + + +QgsQuickFeatureHighlight::QgsQuickFeatureHighlight( QQuickItem *parent ) + : QQuickItem( parent ) + , mModel( nullptr ) + , mDirty( false ) + , mMapSettings( nullptr ) +{ + setFlags( QQuickItem::ItemHasContents ); + setAntialiasing( true ); + + connect( this, &QgsQuickFeatureHighlight::modelChanged, this, &QgsQuickFeatureHighlight::onDataChanged ); +} + +void QgsQuickFeatureHighlight::onDataChanged() +{ + if ( mModel ) + { + connect( mModel, &QgsQuickFeatureModel::modelReset, this, &QgsQuickFeatureHighlight::onModelDataChanged ); + connect( mModel, &QgsQuickFeatureModel::rowsRemoved, this, &QgsQuickFeatureHighlight::onModelDataChanged ); + } + + onModelDataChanged(); +} + +void QgsQuickFeatureHighlight::onModelDataChanged() +{ + mDirty = true; + update(); +} + +QSGNode *QgsQuickFeatureHighlight::updatePaintNode( QSGNode *n, QQuickItem::UpdatePaintNodeData * ) +{ + if ( !mDirty || !mMapSettings ) + return n; + + delete n; + n = new QSGNode; + + if ( !mModel ) + return n; + + QgsVectorLayer *layer = mModel->layer(); + if ( layer ) + { + QgsCoordinateTransform transf( layer->crs(), mMapSettings->destinationCrs(), mMapSettings->transformContext() ); + + QgsFeature feature = mModel->feature(); + QgsGeometry geom( feature.geometry() ); + geom.transform( transf ); + + // TODO: this is very crude conversion! QgsQuickHighlightsNode should accept any type of geometry + QVector points; + for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it ) + points.append( *it ); + + QgsQuickHighlightSGNode *rb = new QgsQuickHighlightSGNode( points, geom.type(), mColor, mWidth ); + rb->setFlag( QSGNode::OwnedByParent ); + n->appendChildNode( rb ); + } + mDirty = false; + + return n; +} diff --git a/src/quickgui/qgsquickfeaturehighlight.h b/src/quickgui/qgsquickfeaturehighlight.h new file mode 100644 index 000000000000..ff66419cdd6e --- /dev/null +++ b/src/quickgui/qgsquickfeaturehighlight.h @@ -0,0 +1,70 @@ +/*************************************************************************** + qgsqguickfeaturehighlight.h + -------------------------------------- + Date : 9.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKFEATUREHIGHLIGHT_H +#define QGSQUICKFEATUREHIGHLIGHT_H + +#include + +#include "qgis_quick.h" + +class QgsQuickMapSettings; +class QgsQuickFeatureModel; + +/** + * \ingroup quick + * + * Creates map highlights for a geometry provided by a FeatureModel. + * + * The highlights are compatible with the QtQuick scene graph. + * + * \note QML Type: FeatureModelHighlight + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickFeatureHighlight : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY( QgsQuickFeatureModel *model MEMBER mModel NOTIFY modelChanged ) + Q_PROPERTY( QColor color MEMBER mColor NOTIFY colorChanged ) + Q_PROPERTY( unsigned int width MEMBER mWidth NOTIFY widthChanged ) + Q_PROPERTY( QgsQuickMapSettings *mapSettings MEMBER mMapSettings NOTIFY mapSettingsChanged ) + + public: + explicit QgsQuickFeatureHighlight( QQuickItem *parent = 0 ); + + signals: + void modelChanged(); + void colorChanged(); + void mapCanvasChanged(); + void widthChanged(); + void mapSettingsChanged(); + + private slots: + void onDataChanged(); + void onModelDataChanged(); + + private: + virtual QSGNode *updatePaintNode( QSGNode *n, UpdatePaintNodeData * ) override; + + QColor mColor; + QgsQuickFeatureModel *mModel; + bool mDirty; + unsigned int mWidth; + QgsQuickMapSettings *mMapSettings; +}; + +#endif // QGSQUICKFEATUREHIGHLIGHT_H diff --git a/src/quickgui/qgsquickfeaturemodel.cpp b/src/quickgui/qgsquickfeaturemodel.cpp new file mode 100644 index 000000000000..878dfcbf81da --- /dev/null +++ b/src/quickgui/qgsquickfeaturemodel.cpp @@ -0,0 +1,289 @@ +/*************************************************************************** + qgsquickfeaturemodel.cpp + -------------------------------------- + Date : 10.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 + +#include "qgsmessagelog.h" +#include "qgsvectorlayer.h" + +#include "qgsquickfeaturemodel.h" + +QgsQuickFeatureModel::QgsQuickFeatureModel( QObject *parent ) + : QAbstractListModel( parent ) +{ + connect( this, &QgsQuickFeatureModel::modelReset, this, &QgsQuickFeatureModel::featureChanged ); +} + +void QgsQuickFeatureModel::setFeature( const QgsFeature &feature ) +{ + if ( mFeature == feature ) + return; + + beginResetModel(); + mFeature = feature; + endResetModel(); + emit featureChanged(); +} + +void QgsQuickFeatureModel::setLayer( QgsVectorLayer *layer ) +{ + if ( layer == mLayer ) + return; + + mLayer = layer; + if ( mLayer ) + { + mFeature = QgsFeature( mLayer->fields() ); + + mRememberedAttributes.resize( layer->fields().size() ); + mRememberedAttributes.fill( false ); + } + + emit layerChanged(); +} + +QgsVectorLayer *QgsQuickFeatureModel::layer() const +{ + return mLayer; +} + +QgsFeature QgsQuickFeatureModel::feature() const +{ + return mFeature; +} + +QHash QgsQuickFeatureModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles[AttributeName] = "AttributeName"; + roles[AttributeValue] = "AttributeValue"; + roles[Field] = "Field"; + roles[RememberAttribute] = "RememberAttribute"; + + return roles; +} + + +int QgsQuickFeatureModel::rowCount( const QModelIndex &parent ) const +{ + if ( parent.isValid() ) + return 0; + else + return mFeature.attributes().count(); +} + +QVariant QgsQuickFeatureModel::data( const QModelIndex &index, int role ) const +{ + if ( mLayer ) + qWarning() << "Get data " << mLayer->name(); + + switch ( role ) + { + case AttributeName: + return mLayer->attributeDisplayName( index.row() ); + break; + + case AttributeValue: + return mFeature.attribute( index.row() ); + break; + + case Field: + return mLayer->fields().at( index.row() ); + break; + + case RememberAttribute: + return mRememberedAttributes.at( index.row() ); + break; + } + + return QVariant(); +} + +bool QgsQuickFeatureModel::setData( const QModelIndex &index, const QVariant &value, int role ) +{ + if ( data( index, role ) == value ) + return true; + + switch ( role ) + { + case AttributeValue: + { + QVariant val( value ); + QgsField fld = mFeature.fields().at( index.row() ); + + if ( !fld.convertCompatible( val ) ) + { + QgsMessageLog::logMessage( tr( "Value \"%1\" %4 could not be converted to a compatible value for field %2(%3)." ).arg( value.toString(), fld.name(), fld.typeName(), value.isNull() ? "NULL" : "NOT NULL" ) ); + return false; + } + bool success = mFeature.setAttribute( index.row(), val ); + if ( success ) + emit dataChanged( index, index, QVector() << role ); + return success; + break; + } + + case RememberAttribute: + { + mRememberedAttributes[ index.row() ] = value.toBool(); + emit dataChanged( index, index, QVector() << role ); + break; + } + } + + return false; +} + +bool QgsQuickFeatureModel::save() +{ + if ( !mLayer ) + return false; + + bool rv = true; + + if ( !startEditing() ) + { + rv = false; + } + + QgsFeature feat = mFeature; + if ( !mLayer->updateFeature( feat ) ) + QgsMessageLog::logMessage( tr( "Cannot update feature" ), "QgsQuick", QgsMessageLog::WARNING ); + rv = commit(); + + if ( rv ) + { + QgsFeature feat; + if ( mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( feat ) ) + setFeature( feat ); + else + QgsMessageLog::logMessage( tr( "Feature %1 could not be fetched after commit" ).arg( mFeature.id() ), "QgsQuick", QgsMessageLog::WARNING ); + } + return rv; +} + +bool QgsQuickFeatureModel::deleteFeature() +{ + if ( !mLayer ) + return false; + + bool rv = true; + + if ( !startEditing() ) + { + rv = false; + } + + if ( !mLayer->deleteFeature( mFeature.id() ) ) + QgsMessageLog::logMessage( tr( "Cannot delete feature" ), "QgsQuick", QgsMessageLog::WARNING ); + rv = commit(); + + return rv; +} + +void QgsQuickFeatureModel::reset() +{ + if ( !mLayer ) + return; + + mLayer->rollBack(); +} + +bool QgsQuickFeatureModel::suppressFeatureForm() const +{ + if ( !mLayer ) + return false; + + return mLayer->editFormConfig().suppress(); +} + +void QgsQuickFeatureModel::resetAttributes() +{ + if ( !mLayer ) + return; + + QgsExpressionContext expressionContext = mLayer->createExpressionContext(); + expressionContext.setFeature( mFeature ); + + QgsFields fields = mLayer->fields(); + + beginResetModel(); + for ( int i = 0; i < fields.count(); ++i ) + { + if ( !mRememberedAttributes.at( i ) ) + { + if ( !fields.at( i ).defaultValueDefinition().expression().isEmpty() ) + { + QgsExpression exp( fields.at( i ).defaultValueDefinition().expression() ); + QVariant value = exp.evaluate( &expressionContext ); + mFeature.setAttribute( i, value ); + } + else + { + mFeature.setAttribute( i, QVariant() ); + } + } + } + endResetModel(); +} + +void QgsQuickFeatureModel::create() +{ + if ( !mLayer ) + return; + + startEditing(); + if ( !mLayer->addFeature( mFeature ) ) + { + QgsMessageLog::logMessage( tr( "Feature could not be added" ), "QgsQuick", QgsMessageLog::CRITICAL ); + } + commit(); +} + +bool QgsQuickFeatureModel::commit() +{ + if ( !mLayer->commitChanges() ) + { + QgsMessageLog::logMessage( tr( "Could not save changes. Rolling back." ), "QgsQuick", QgsMessageLog::CRITICAL ); + mLayer->rollBack(); + return false; + } + else + { + return true; + } +} + +bool QgsQuickFeatureModel::startEditing() +{ + // Already an edit session active + if ( mLayer->editBuffer() ) + return true; + + if ( !mLayer->startEditing() ) + { + QgsMessageLog::logMessage( tr( "Cannot start editing" ), "QgsQuick", QgsMessageLog::WARNING ); + return false; + } + else + { + return true; + } +} + +QVector QgsQuickFeatureModel::rememberedAttributes() const +{ + return mRememberedAttributes; +} diff --git a/src/quickgui/qgsquickfeaturemodel.h b/src/quickgui/qgsquickfeaturemodel.h new file mode 100644 index 000000000000..5885cb827a0a --- /dev/null +++ b/src/quickgui/qgsquickfeaturemodel.h @@ -0,0 +1,114 @@ +/*************************************************************************** + qgsquickfeaturemodel.h + -------------------------------------- + Date : 10.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKFEATUREMODEL_H +#define QGSQUICKFEATUREMODEL_H + +#include +#include + +#include "qgsfeature.h" +#include "qgsvectorlayer.h" + +#include "qgis_quick.h" + +/** + * \ingroup quick + * Item model implementation for attributes of QgsFeature: each attribute gets a row in the model. + * + * \note QML Type: FeatureModel + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickFeatureModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY( QgsFeature feature READ feature WRITE setFeature NOTIFY featureChanged ) + Q_PROPERTY( QgsVectorLayer *layer READ layer WRITE setLayer NOTIFY layerChanged ) + Q_ENUMS( FeatureRoles ) + + public: + enum FeatureRoles + { + AttributeName = Qt::UserRole + 1, //!< Attribute's display name (the original field name or a custom alias) + AttributeValue, //!< Value of the feature's attribute + Field, //!< Field definition (QgsField) + RememberAttribute + }; + + explicit QgsQuickFeatureModel( QObject *parent = 0 ); + explicit QgsQuickFeatureModel( const QgsFeature &feat, QObject *parent = 0 ); + + void setFeature( const QgsFeature &feature ); + + /** + * Return the feature wrapped in a QVariant for passing it around in QML + */ + QgsFeature feature() const; + + + void setLayer( QgsVectorLayer *layer ); + QgsVectorLayer *layer() const; + + + QHash roleNames() const override; + int rowCount( const QModelIndex &parent ) const override; + QVariant data( const QModelIndex &index, int role ) const override; + bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override; + + /** + * Will commit the edit buffer of this layer. + * May change in the future to only commit the changes buffered in this model. + * + * @return Success of the operation + */ + Q_INVOKABLE bool save(); + + /** + * Will delete the current feature from the layer and commit the changes. + * @return Success of the operation + */ + Q_INVOKABLE bool deleteFeature(); + + /** + * Will reset the feature to the original values and dismiss any buffered edits. + */ + Q_INVOKABLE void reset(); + Q_INVOKABLE void create(); + + Q_INVOKABLE bool suppressFeatureForm() const; + + Q_INVOKABLE void resetAttributes(); + + QVector rememberedAttributes() const; + + public slots: + + signals: + void featureChanged(); + void layerChanged(); + + void warning( const QString &text ); + + private: + bool commit(); + bool startEditing(); + + QgsVectorLayer *mLayer = nullptr; + QgsFeature mFeature; + QVector mRememberedAttributes; +}; + +#endif // QGSQUICKFEATUREMODEL_H diff --git a/src/quickgui/qgsquickhighlightsgnode.cpp b/src/quickgui/qgsquickhighlightsgnode.cpp new file mode 100644 index 000000000000..78b98ced28fd --- /dev/null +++ b/src/quickgui/qgsquickhighlightsgnode.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + qgsquickhighlightsgnode.cpp + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 "qgsquickhighlightsgnode.h" + + +QgsQuickHighlightSGNode::QgsQuickHighlightSGNode( const QVector &points, QgsWkbTypes::GeometryType type, const QColor &color, qreal width ) + : QSGNode() +{ + mMaterial.setColor( color ); + + // TODO: support multi-part geometries + switch ( type ) + { + case QgsWkbTypes::PointGeometry: + { + if ( !points.isEmpty() ) + appendChildNode( createPointGeometry( points.at( 0 ), width ) ); + break; + } + + case QgsWkbTypes::LineGeometry: + { + appendChildNode( createLineGeometry( points, width ) ); + break; + } + + case QgsWkbTypes::PolygonGeometry: + { + // TODO: support polygon geometries + break; + } + + case QgsWkbTypes::UnknownGeometry: + case QgsWkbTypes::NullGeometry: + break; + } +} + +QSGGeometryNode *QgsQuickHighlightSGNode::createLineGeometry( const QVector &points, qreal width ) +{ + QSGGeometryNode *node = new QSGGeometryNode; + QSGGeometry *sgGeom = new QSGGeometry( QSGGeometry::defaultAttributes_Point2D(), points.count() ); + QSGGeometry::Point2D *vertices = sgGeom->vertexDataAsPoint2D(); + + int i = 0; + Q_FOREACH ( const QgsPoint &pt, points ) + { + vertices[i++].set( pt.x(), pt.y() ); + } + + sgGeom->setLineWidth( width ); + sgGeom->setDrawingMode( GL_LINE_STRIP ); + node->setGeometry( sgGeom ); + node->setMaterial( &mMaterial ); + node->setFlag( QSGNode::OwnsGeometry ); + node->setFlag( QSGNode::OwnedByParent ); + return node; +} + +QSGGeometryNode *QgsQuickHighlightSGNode::createPointGeometry( const QgsPoint &point, qreal width ) +{ + QSGGeometryNode *node = new QSGGeometryNode; + + QSGGeometry *sgGeom = new QSGGeometry( QSGGeometry::defaultAttributes_Point2D(), 1 ); + + QSGGeometry::Point2D *vertices = sgGeom->vertexDataAsPoint2D(); + vertices[0].set( point.x(), point.y() ); + sgGeom->setDrawingMode( GL_POINTS ); + sgGeom->setLineWidth( width ); + + node->setGeometry( sgGeom ); + node->setMaterial( &mMaterial ); + node->setFlag( QSGNode::OwnsGeometry ); + node->setFlag( QSGNode::OwnedByParent ); + return node; +} + +QSGGeometryNode *QgsQuickHighlightSGNode::createPolygonGeometry( const QVector &points ) +{ + Q_UNUSED( points ); + return 0; +} diff --git a/src/quickgui/qgsquickhighlightsgnode.h b/src/quickgui/qgsquickhighlightsgnode.h new file mode 100644 index 000000000000..62fc99bdd096 --- /dev/null +++ b/src/quickgui/qgsquickhighlightsgnode.h @@ -0,0 +1,49 @@ +/*************************************************************************** + qgsquickhighlightsgnode.h + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKHIGHLIGHTSGNODE_H +#define QGSQUICKHIGHLIGHTSGNODE_H + +#include +#include + +#include "qgspoint.h" +#include "qgswkbtypes.h" + +#include "qgis_quick.h" + +/** + * \ingroup quick + * + * This is used to transform (render) QgsGeometry to node for QtQuick scene graph. + * + * \note QML Type: not exported + * + * \since QGIS 3.2 + */ +class QUICK_NO_EXPORT QgsQuickHighlightSGNode : public QSGNode +{ + public: + QgsQuickHighlightSGNode( const QVector &points, QgsWkbTypes::GeometryType type, const QColor &color, qreal width ); + + private: + QSGGeometryNode *createLineGeometry( const QVector &points, qreal width ); + QSGGeometryNode *createPointGeometry( const QgsPoint &point, qreal width ); + QSGGeometryNode *createPolygonGeometry( const QVector &points ); + + QSGFlatColorMaterial mMaterial; +}; + +#endif // QGSQUICKHIGHLIGHTSGNODE diff --git a/src/quickgui/qgsquickidentifykit.cpp b/src/quickgui/qgsquickidentifykit.cpp new file mode 100644 index 000000000000..26fb9328d262 --- /dev/null +++ b/src/quickgui/qgsquickidentifykit.cpp @@ -0,0 +1,289 @@ +/*************************************************************************** + qgsquickidentifykit.cpp + --------------------- + Date : 30.8.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 "qgsproject.h" +#include "qgsrenderer.h" +#include "qgsvectorlayer.h" + +#include "qgsquickidentifykit.h" +#include "qgsquickmapsettings.h" + +QgsQuickIdentifyKit::QgsQuickIdentifyKit( QObject *parent ) + : QObject( parent ) + , mMapSettings( nullptr ) + , mSearchRadiusMm( 8 ) + , mFeaturesLimit( 100 ) +{ +} + +QgsQuickMapSettings *QgsQuickIdentifyKit::mapSettings() const +{ + return mMapSettings; +} + +void QgsQuickIdentifyKit::setMapSettings( QgsQuickMapSettings *mapSettings ) +{ + if ( mapSettings == mMapSettings ) + return; + + mMapSettings = mapSettings; + emit mapSettingsChanged(); +} + + +QList QgsQuickIdentifyKit::identify( const QPointF &point ) +{ + QList results; + + if ( !mMapSettings ) + { + qWarning() << "Unable to use IdentifyKit without mapSettings property set."; + return results; + } + + QgsPointXY mapPoint = mMapSettings->mapSettings().mapToPixel().toMapCoordinates( point.toPoint() ); + + QStringList noIdentifyLayerIdList; + if ( mMapSettings->project() ) + { + noIdentifyLayerIdList = mMapSettings->project()->nonIdentifiableLayers(); + } + + Q_FOREACH ( QgsMapLayer *layer, mMapSettings->mapSettings().layers() ) + { + if ( mMapSettings->project() && noIdentifyLayerIdList.contains( layer->id() ) ) + continue; + + QgsVectorLayer *vl = qobject_cast( layer ); + if ( vl ) + { + QgsFeatureList featureList = identifyVectorLayer( vl, mapPoint ); + + Q_FOREACH ( const QgsFeature &feature, featureList ) + { + results.append( QgsQuickFeature( feature, vl ) ); + } + } + } + + qDebug() << "IdentifyKit identified " << results.count() << " results"; + return results; +} + +static QgsFeature _closestFeature( const QgsFeatureList &results, const QgsMapSettings &mapSettings, QgsVectorLayer *layer, const QPointF &point ) +{ + QgsPointXY mapPoint = mapSettings.mapToPixel().toMapCoordinates( point.toPoint() ); + QgsGeometry mapPointGeom( QgsGeometry::fromPointXY( mapPoint ) ); + QgsCoordinateTransform ctLayerToMap = mapSettings.layerTransform( layer ); + + Q_ASSERT( results.count() != 0 ); + double distMin = 1e10; + int iMin = -1; + for ( int i = 0; i < results.count(); ++i ) + { + QgsGeometry geom( results.at( i ).geometry() ); + geom.transform( ctLayerToMap ); + + double dist = geom.distance( mapPointGeom ); + if ( dist < distMin ) + { + iMin = i; + distMin = dist; + } + } + return results.at( iMin ); +} + + +static QgsQuickFeature _closestFeature( const QList &results, const QgsMapSettings &mapSettings, const QPointF &point ) +{ + QgsPointXY mapPoint = mapSettings.mapToPixel().toMapCoordinates( point.toPoint() ); + QgsGeometry mapPointGeom( QgsGeometry::fromPointXY( mapPoint ) ); + + Q_ASSERT( results.count() != 0 ); + double distMin = 1e10; + int iMin = -1; + for ( int i = 0; i < results.count(); ++i ) + { + const QgsQuickFeature &res = results.at( i ); + QgsGeometry geom( res.feature().geometry() ); + geom.transform( mapSettings.layerTransform( res.layer() ) ); + + double dist = geom.distance( mapPointGeom ); + if ( dist < distMin ) + { + iMin = i; + distMin = dist; + } + } + return results.at( iMin ); +} + + +QgsFeature QgsQuickIdentifyKit::identifyOne( QgsVectorLayer *layer, const QPointF &point ) +{ + QgsFeatureList results = identify( layer, point ); + if ( results.empty() ) + { + QgsFeature f = QgsFeature(); + f.setValid( false ); + return f; + } + else + { + return _closestFeature( results, mMapSettings->mapSettings(), layer, point ); + } +} + + +QgsQuickFeature QgsQuickIdentifyKit::identifyOne( const QPointF &point ) +{ + QList results = identify( point ); + if ( results.empty() ) + { + QgsQuickFeature emptyRes; + return emptyRes; + } + else + { + return _closestFeature( results, mMapSettings->mapSettings(), point ); + } +} + +QgsFeatureList QgsQuickIdentifyKit::identify( QgsVectorLayer *layer, const QPointF &point ) +{ + QgsFeatureList results; + + Q_ASSERT( layer ); + + if ( !mMapSettings ) + { + qWarning() << "Unable to use IdentifyKit without mapSettings property set."; + return results; + } + QgsPointXY mapPoint = mMapSettings->mapSettings().mapToPixel().toMapCoordinates( point.toPoint() ); + + results = identifyVectorLayer( layer, mapPoint ); + + qDebug() << "IdentifyKit identified " << results.count() << " results for layer " << layer->name(); + + return results; +} + + +QgsFeatureList QgsQuickIdentifyKit::identifyVectorLayer( QgsVectorLayer *layer, const QgsPointXY &point ) const +{ + QgsFeatureList results; + + if ( !layer || !layer->isSpatial() ) + return results; + + if ( !layer->isInScaleRange( mMapSettings->mapSettings().scale() ) ) + return results; + + QgsFeatureList featureList; + + // toLayerCoordinates will throw an exception for an 'invalid' point. + // For example, if you project a world map onto a globe using EPSG 2163 + // and then click somewhere off the globe, an exception will be thrown. + try + { + // create the search rectangle + double searchRadius = searchRadiusMU(); + + QgsRectangle r; + r.setXMinimum( point.x() - searchRadius ); + r.setXMaximum( point.x() + searchRadius ); + r.setYMinimum( point.y() - searchRadius ); + r.setYMaximum( point.y() + searchRadius ); + + r = toLayerCoordinates( layer, r ); + + QgsFeatureRequest req; + req.setFilterRect( r ); + req.setLimit( mFeaturesLimit ); + req.setFlags( QgsFeatureRequest::ExactIntersect ); + + QgsFeatureIterator fit = layer->getFeatures( req ); + QgsFeature f; + while ( fit.nextFeature( f ) ) + featureList << QgsFeature( f ); + } + catch ( QgsCsException &cse ) + { + Q_UNUSED( cse ); + // catch exception for 'invalid' point and proceed with no features found + } + + bool filter = false; + + QgsRenderContext context( QgsRenderContext::fromMapSettings( mMapSettings->mapSettings() ) ); + context.expressionContext() << QgsExpressionContextUtils::layerScope( layer ); + QgsFeatureRenderer *renderer = layer->renderer(); + if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent ) + { + // setup scale for scale dependent visibility (rule based) + renderer->startRender( context, layer->fields() ); + filter = renderer->capabilities() & QgsFeatureRenderer::Filter; + } + + Q_FOREACH ( const QgsFeature &feature, featureList ) + { + context.expressionContext().setFeature( feature ); + + if ( filter && !renderer->willRenderFeature( const_cast( feature ), context ) ) + continue; + + results.append( feature ); + } + + if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent ) + { + renderer->stopRender( context ); + } + + return results; +} + +double QgsQuickIdentifyKit::searchRadiusMU( const QgsRenderContext &context ) const +{ + return mSearchRadiusMm * context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel(); +} + +double QgsQuickIdentifyKit::searchRadiusMU() const +{ + QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings->mapSettings() ); + return searchRadiusMU( context ); +} + +QgsRectangle QgsQuickIdentifyKit::toLayerCoordinates( QgsMapLayer *layer, const QgsRectangle &rect ) const +{ + return mMapSettings->mapSettings().mapToLayerCoordinates( layer, rect ); +} + +double QgsQuickIdentifyKit::searchRadiusMm() const +{ + return mSearchRadiusMm; +} + +void QgsQuickIdentifyKit::setSearchRadiusMm( double searchRadiusMm ) +{ + if ( mSearchRadiusMm == searchRadiusMm ) + return; + + mSearchRadiusMm = searchRadiusMm; + emit searchRadiusMmChanged(); +} diff --git a/src/quickgui/qgsquickidentifykit.h b/src/quickgui/qgsquickidentifykit.h new file mode 100644 index 000000000000..d6f698fc0747 --- /dev/null +++ b/src/quickgui/qgsquickidentifykit.h @@ -0,0 +1,109 @@ +/*************************************************************************** + qgsquickidentifykit.h + --------------------- + Date : 30.8.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKIDENTIFYKIT_H +#define QGSQUICKIDENTIFYKIT_H + +#include +#include + +#include "qgsfeature.h" +#include "qgsmapsettings.h" +#include "qgspoint.h" +#include "qgsrendercontext.h" + +#include "qgis_quick.h" +#include "qgsquickfeature.h" + +class QgsQuickProject; +class QgsMapLayer; +class QgsQuickMapSettings; +class QgsVectorLayer; + +/** + * \ingroup quick + * Convinient set of tools to get a list of QgsFeatures in a defined radius from a point. + * Also possible to get a feature with the closest distance to the point or feature(s) from + * specified QgsVectorLayer + * + * \note QML Type: IdentifyKit + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickIdentifyKit : public QObject +{ + Q_OBJECT + Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) + + /** + * Search radius for the identify functions from the point. Default is 8 + */ + Q_PROPERTY( double searchRadiusMm READ searchRadiusMm WRITE setSearchRadiusMm NOTIFY searchRadiusMmChanged ) + + /** + * Maximum number of feature returned from by the identify functions in QgsFeatureList. Default is 100 + */ + Q_PROPERTY( long featuresLimit MEMBER mFeaturesLimit NOTIFY featuresLimitChanged ) + + public: + explicit QgsQuickIdentifyKit( QObject *parent = 0 ); + + QgsQuickMapSettings *mapSettings() const; + void setMapSettings( QgsQuickMapSettings *mapSettings ); + + double searchRadiusMm() const; + void setSearchRadiusMm( double searchRadiusMm ); + + /** + * Get the closest feature to the point from the layer in case it is identifiable layer + */ + Q_INVOKABLE QgsFeature identifyOne( QgsVectorLayer *layer, const QPointF &point ); + + /** + * Get the closest feature to the point from any identifiable layer + */ + Q_INVOKABLE QgsQuickFeature identifyOne( const QPointF &point ); + + /** + * Get all features interseting the point from the layer in case it is identifiable layer + */ + Q_INVOKABLE QgsFeatureList identify( QgsVectorLayer *layer, const QPointF &point ); + + /** + * Get all features interseting the point from any identifiable layer + */ + Q_INVOKABLE QList identify( const QPointF &point ); + + + signals: + void mapSettingsChanged(); + void searchRadiusMmChanged(); + void featuresLimitChanged(); + + private: + QgsQuickProject *mProject; + QgsQuickMapSettings *mMapSettings; + + double searchRadiusMU( const QgsRenderContext &context ) const; + double searchRadiusMU() const; + + QgsRectangle toLayerCoordinates( QgsMapLayer *layer, const QgsRectangle &rect ) const; + QgsFeatureList identifyVectorLayer( QgsVectorLayer *layer, const QgsPointXY &point ) const; + + double mSearchRadiusMm; + int mFeaturesLimit; +}; + +#endif // QGSQUICKIDENTIFYKIT_H diff --git a/src/quickgui/qgsquickmapcanvasmap.cpp b/src/quickgui/qgsquickmapcanvasmap.cpp new file mode 100644 index 000000000000..d98c0552fb05 --- /dev/null +++ b/src/quickgui/qgsquickmapcanvasmap.cpp @@ -0,0 +1,410 @@ +/*************************************************************************** + qgsquickmapcanvasmap.cpp + -------------------------------------- + Date : 10.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "qgsquickmapcanvasmap.h" +#include "qgsquickmapsettings.h" + + +QgsQuickMapCanvasMap::QgsQuickMapCanvasMap( QQuickItem *parent ) + : QQuickItem( parent ) + , mMapSettings( new QgsQuickMapSettings() ) + , mPinching( false ) + , mJob( nullptr ) + , mCache( nullptr ) + , mLabelingResults( nullptr ) + , mDirty( false ) + , mFreeze( false ) + , mIncrementalRendering( false ) +{ + connect( this, &QQuickItem::windowChanged, this, &QgsQuickMapCanvasMap::onWindowChanged ); + connect( &mRefreshTimer, &QTimer::timeout, this, &QgsQuickMapCanvasMap::refreshMap ); + connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsQuickMapCanvasMap::renderJobUpdated ); + + connect( mMapSettings, &QgsQuickMapSettings::extentChanged, this, &QgsQuickMapCanvasMap::onExtentChanged ); + connect( mMapSettings, &QgsQuickMapSettings::layersChanged, this, &QgsQuickMapCanvasMap::onLayersChanged ); + + connect( this, &QgsQuickMapCanvasMap::renderStarting, this, &QgsQuickMapCanvasMap::isRenderingChanged ); + connect( this, &QgsQuickMapCanvasMap::mapCanvasRefreshed, this, &QgsQuickMapCanvasMap::isRenderingChanged ); + + mMapUpdateTimer.setSingleShot( false ); + mMapUpdateTimer.setInterval( 250 ); + mRefreshTimer.setSingleShot( true ); + setTransformOrigin( QQuickItem::TopLeft ); + setFlags( QQuickItem::ItemHasContents ); +} + +QgsQuickMapCanvasMap::~QgsQuickMapCanvasMap() +{ +} + +QgsQuickMapSettings *QgsQuickMapCanvasMap::mapSettings() const +{ + return mMapSettings; +} + +void QgsQuickMapCanvasMap::zoom( QPointF center, qreal scale ) +{ + QgsRectangle extent = mMapSettings->extent(); + QgsPoint oldCenter( extent.center() ); + QgsPoint mousePos( mMapSettings->screenToCoordinate( center ) ); + QgsPointXY newCenter( mousePos.x() + ( ( oldCenter.x() - mousePos.x() ) * scale ), + mousePos.y() + ( ( oldCenter.y() - mousePos.y() ) * scale ) ); + + // same as zoomWithCenter (no coordinate transformations are needed) + extent.scale( scale, &newCenter ); + mMapSettings->setExtent( extent ); +} + +void QgsQuickMapCanvasMap::pan( QPointF oldPos, QPointF newPos ) +{ + QgsPoint start = mMapSettings->screenToCoordinate( oldPos.toPoint() ); + QgsPoint end = mMapSettings->screenToCoordinate( newPos.toPoint() ); + + double dx = end.x() - start.x(); + double dy = end.y() - start.y(); + + // modify the extent + QgsRectangle extent = mMapSettings->extent(); + + extent.setXMinimum( extent.xMinimum() + dx ); + extent.setXMaximum( extent.xMaximum() + dx ); + extent.setYMaximum( extent.yMaximum() + dy ); + extent.setYMinimum( extent.yMinimum() + dy ); + + mMapSettings->setExtent( extent ); +} + +void QgsQuickMapCanvasMap::refreshMap() +{ + stopRendering(); // if any... + + QgsMapSettings mapSettings = mMapSettings->mapSettings(); + + //build the expression context + QgsExpressionContext expressionContext; + expressionContext << QgsExpressionContextUtils::globalScope() + << QgsExpressionContextUtils::mapSettingsScope( mapSettings ); + + QgsProject *project = mMapSettings->project(); + if ( project ) + { + expressionContext << QgsExpressionContextUtils::projectScope( project ); + } + + mapSettings.setExpressionContext( expressionContext ); + + // create the renderer job + Q_ASSERT( !mJob ); + mJob = new QgsMapRendererParallelJob( mapSettings ); + + if ( mIncrementalRendering ) + mMapUpdateTimer.start(); + + connect( mJob, &QgsMapRendererJob::renderingLayersFinished, this, &QgsQuickMapCanvasMap::renderJobUpdated ); + connect( mJob, &QgsMapRendererJob::finished, this, &QgsQuickMapCanvasMap::renderJobFinished ); + mJob->setCache( mCache ); + + mJob->start(); + + emit renderStarting(); +} + +void QgsQuickMapCanvasMap::renderJobUpdated() +{ + mImage = mJob->renderedImage(); + mImageMapSettings = mJob->mapSettings(); + mDirty = true; + // Temporarily freeze the canvas, we only need to reset the geometry but not trigger a repaint + bool freeze = mFreeze; + mFreeze = true; + updateTransform(); + mFreeze = freeze; + + update(); + emit mapCanvasRefreshed(); +} + +void QgsQuickMapCanvasMap::renderJobFinished() +{ + Q_FOREACH ( const QgsMapRendererJob::Error &error, mJob->errors() ) + { + QgsMessageLog::logMessage( error.layerID + " :: " + error.message, tr( "Rendering" ) ); + } + + // take labeling results before emitting renderComplete, so labeling map tools + // connected to signal work with correct results + delete mLabelingResults; + mLabelingResults = mJob->takeLabelingResults(); + + mImage = mJob->renderedImage(); + mImageMapSettings = mJob->mapSettings(); + + // now we are in a slot called from mJob - do not delete it immediately + // so the class is still valid when the execution returns to the class + mJob->deleteLater(); + mJob = nullptr; + mDirty = true; + mMapUpdateTimer.stop(); + + // Temporarily freeze the canvas, we only need to reset the geometry but not trigger a repaint + bool freeze = mFreeze; + mFreeze = true; + updateTransform(); + mFreeze = freeze; + + update(); + emit mapCanvasRefreshed(); +} + +void QgsQuickMapCanvasMap::onWindowChanged( QQuickWindow *window ) +{ + disconnect( this, SLOT( onScreenChanged( QScreen * ) ) ); + if ( window ) + { + connect( window, &QQuickWindow::screenChanged, this, &QgsQuickMapCanvasMap::onScreenChanged ); + onScreenChanged( window->screen() ); + } +} + +void QgsQuickMapCanvasMap::onScreenChanged( QScreen *screen ) +{ + if ( screen ) + mMapSettings->setOutputDpi( screen->physicalDotsPerInch() ); +} + +void QgsQuickMapCanvasMap::onExtentChanged() +{ + updateTransform(); + + // And trigger a new rendering job + refresh(); +} + +void QgsQuickMapCanvasMap::updateTransform() +{ + QgsMapSettings currentMapSettings = mMapSettings->mapSettings(); + QgsMapToPixel mtp = currentMapSettings.mapToPixel(); + + QgsRectangle imageExtent = mImageMapSettings.visibleExtent(); + QgsRectangle newExtent = currentMapSettings.visibleExtent(); + QgsPointXY pixelPt = mtp.transform( imageExtent.xMinimum(), imageExtent.yMaximum() ); + setScale( imageExtent.width() / newExtent.width() ); + + setX( pixelPt.x() ); + setY( pixelPt.y() ); +} + +int QgsQuickMapCanvasMap::mapUpdateInterval() const +{ + return mMapUpdateTimer.interval(); +} + +void QgsQuickMapCanvasMap::setMapUpdateInterval( int mapUpdateInterval ) +{ + if ( mMapUpdateInterval == mapUpdateInterval ) + return; + + mMapUpdateTimer.setInterval( mapUpdateInterval ); + + emit mapUpdateIntervalChanged(); +} + +bool QgsQuickMapCanvasMap::incrementalRendering() const +{ + return mIncrementalRendering; +} + +void QgsQuickMapCanvasMap::setIncrementalRendering( bool incrementalRendering ) +{ + if ( incrementalRendering == mIncrementalRendering ) + return; + + mIncrementalRendering = incrementalRendering; + emit incrementalRenderingChanged(); +} + +bool QgsQuickMapCanvasMap::freeze() const +{ + return mFreeze; +} + +void QgsQuickMapCanvasMap::setFreeze( bool freeze ) +{ + if ( freeze == mFreeze ) + return; + + mFreeze = freeze; + + if ( !mFreeze ) + refresh(); + + emit freezeChanged(); +} + +bool QgsQuickMapCanvasMap::isRendering() const +{ + return mJob; +} + +QgsRectangle QgsQuickMapCanvasMap::extent() const +{ + return mMapSettings->extent(); +} + +void QgsQuickMapCanvasMap::setExtent( const QgsRectangle &extent ) +{ + mMapSettings->setExtent( extent ); +} + +QSGNode *QgsQuickMapCanvasMap::updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * ) +{ + if ( mDirty ) + { + delete oldNode; + oldNode = nullptr; + mDirty = false; + } + + QSGSimpleTextureNode *node = static_cast( oldNode ); + if ( !node ) + { + node = new QSGSimpleTextureNode(); + QSGTexture *texture = window()->createTextureFromImage( mImage ); + node->setTexture( texture ); + node->setOwnsTexture( true ); + } + + QRectF rect( boundingRect() ); + + // Check for resizes that change the w/h ratio + if ( !rect.isEmpty() && !mImage.size().isEmpty() && rect.width() / rect.height() != mImage.width() / mImage.height() ) + { + if ( rect.height() == mImage.height() ) + { + rect.setHeight( rect.width() / mImage.width() * mImage.height() ); + } + else + { + rect.setWidth( rect.height() / mImage.height() * mImage.width() ); + } + } + + node->setRect( rect ); + + return node; +} + +QgsCoordinateReferenceSystem QgsQuickMapCanvasMap::destinationCrs() const +{ + return mMapSettings->destinationCrs(); +} + +void QgsQuickMapCanvasMap::setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ) +{ + mMapSettings->setDestinationCrs( destinationCrs ); +} + +void QgsQuickMapCanvasMap::geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry ) +{ + Q_UNUSED( oldGeometry ) + // The Qt documentation advices to call the base method here. + // However, this introduces instabilities and heavy performance impacts on Android. + // It seems on desktop disabling it prevents us from downsizing the window... + // Be careful when re-enabling it. + // QQuickItem::geometryChanged( newGeometry, oldGeometry ); + + mMapSettings->setOutputSize( newGeometry.size().toSize() ); + refresh(); +} + +void QgsQuickMapCanvasMap::onLayersChanged() +{ + if ( mMapSettings->extent().isEmpty() ) + zoomToFullExtent(); + + Q_FOREACH ( const QMetaObject::Connection &conn, mLayerConnections ) + { + disconnect( conn ); + } + mLayerConnections.clear(); + + Q_FOREACH ( QgsMapLayer *layer, mMapSettings->layers() ) + { + // QgsVectorLayer* vl = qobject_cast( layer ); + mLayerConnections << connect( layer, &QgsMapLayer::repaintRequested, this, &QgsQuickMapCanvasMap::refresh ); + } + + refresh(); +} + +void QgsQuickMapCanvasMap::destroyJob( QgsMapRendererJob *job ) +{ + job->cancel(); + job->deleteLater(); +} + +void QgsQuickMapCanvasMap::stopRendering() +{ + if ( mJob ) + { + disconnect( mJob, &QgsMapRendererJob::renderingLayersFinished, this, &QgsQuickMapCanvasMap::renderJobUpdated ); + disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsQuickMapCanvasMap::renderJobFinished ); + + // Destroy job in separate worker thread, killing an iterator may take some time + // and reduce responsiveness + mZombieJobs.addFuture( QtConcurrent::run( this, &QgsQuickMapCanvasMap::destroyJob, mJob ) ); + mJob = nullptr; + } +} + +void QgsQuickMapCanvasMap::zoomToFullExtent() +{ + QgsRectangle extent; + Q_FOREACH ( QgsMapLayer *layer, mMapSettings->layers() ) + { + if ( mMapSettings->destinationCrs() != layer->crs() ) + { + QgsCoordinateTransform transform( layer->crs(), mMapSettings->destinationCrs(), mMapSettings->transformContext() ); + extent.combineExtentWith( transform.transformBoundingBox( layer->extent() ) ); + } + else + { + extent.combineExtentWith( layer->extent() ); + } + } + mMapSettings->setExtent( extent ); + + refresh(); +} + +void QgsQuickMapCanvasMap::refresh() +{ + if ( mMapSettings->outputSize().isNull() ) + return; // the map image size has not been set yet + + if ( !mFreeze ) + mRefreshTimer.start( 1 ); +} diff --git a/src/quickgui/qgsquickmapcanvasmap.h b/src/quickgui/qgsquickmapcanvasmap.h new file mode 100644 index 000000000000..1a5463f3c3ac --- /dev/null +++ b/src/quickgui/qgsquickmapcanvasmap.h @@ -0,0 +1,164 @@ +/*************************************************************************** + qgsquickmapcanvasmap.h + -------------------------------------- + Date : 10.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKMAPCANVASMAP_H +#define QGSQUICKMAPCANVASMAP_H + +#include +#include +#include + +#include "qgsmapsettings.h" +#include "qgspoint.h" + +#include "qgis_quick.h" +#include "qgsquickmapsettings.h" + +class QgsMapRendererParallelJob; +class QgsMapRendererCache; +class QgsLabelingResults; + +/** + * \ingroup quick + * This class implements a visual Qt Quick Item that does map rendering + * according to the current map settings. Client code is expected to use + * MapCanvas item rather than using this class directly. + * + * \note QML Type: MapCanvasMap + * + * \sa QgsQuickMapCanvas + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickMapCanvasMap : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY( QgsCoordinateReferenceSystem destinationCrs READ destinationCrs WRITE setDestinationCrs NOTIFY destinationCrsChanged ) + Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings ) + Q_PROPERTY( bool freeze READ freeze WRITE setFreeze NOTIFY freezeChanged ) + Q_PROPERTY( bool isRendering READ isRendering NOTIFY isRenderingChanged ) + + /** + * Interval in milliseconds after which the map canvas will be updated while a rendering job is ongoing. + * This only has an effect if incrementalRendering is activated. + * Default is 250 [ms]. + */ + Q_PROPERTY( int mapUpdateInterval READ mapUpdateInterval WRITE setMapUpdateInterval NOTIFY mapUpdateIntervalChanged ) + Q_PROPERTY( bool incrementalRendering READ incrementalRendering WRITE setIncrementalRendering NOTIFY incrementalRenderingChanged ) + + public: + QgsQuickMapCanvasMap( QQuickItem *parent = 0 ); + ~QgsQuickMapCanvasMap(); + + QgsPoint toMapCoordinates( QPoint canvasCoordinates ); + + QgsQuickMapSettings *mapSettings() const; + + QgsUnitTypes::DistanceUnit mapUnits() const; + void setMapUnits( const QgsUnitTypes::DistanceUnit &mapUnits ); + + QList layerSet() const; + void setLayerSet( const QList &layerSet ); + + bool hasCrsTransformEnabled() const; + void setCrsTransformEnabled( bool hasCrsTransformEnabled ); + + QgsCoordinateReferenceSystem destinationCrs() const; + void setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ); + + QgsRectangle extent() const; + void setExtent( const QgsRectangle &extent ); + + virtual QSGNode *updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * ); + + bool freeze() const; + void setFreeze( bool freeze ); + + bool isRendering() const; + + int mapUpdateInterval() const; + void setMapUpdateInterval( int mapUpdateInterval ); + + bool incrementalRendering() const; + void setIncrementalRendering( bool incrementalRendering ); + + signals: + void renderStarting(); + + void mapCanvasRefreshed(); + + void extentChanged(); + + void destinationCrsChanged(); + + void freezeChanged(); + + void isRenderingChanged(); + + void mapUpdateIntervalChanged(); + + void incrementalRenderingChanged(); + + protected: + void geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry ); + + public slots: + void stopRendering(); + + void zoomToFullExtent(); + + void zoom( QPointF center, qreal scale ); + void pan( QPointF oldPos, QPointF newPos ); + void refresh(); + + private slots: + void refreshMap(); + void renderJobUpdated(); + void renderJobFinished(); + void onWindowChanged( QQuickWindow *window ); + void onScreenChanged( QScreen *screen ); + void onExtentChanged(); + void onLayersChanged(); + + private: + + /** + * Should only be called ba stopRendering()! + */ + void destroyJob( QgsMapRendererJob *job ); + QgsMapSettings prepareMapSettings() const; + void updateTransform(); + + QgsQuickMapSettings *mMapSettings; + + bool mPinching; + QPoint mPinchStartPoint; + QgsMapRendererParallelJob *mJob; + QgsMapRendererCache *mCache; + QgsLabelingResults *mLabelingResults; + QImage mImage; + QgsMapSettings mImageMapSettings; + QTimer mRefreshTimer; + bool mDirty; + bool mFreeze; + QList mLayerConnections; + QFutureSynchronizer mZombieJobs; + QTimer mMapUpdateTimer; + int mMapUpdateInterval; + bool mIncrementalRendering; +}; + +#endif // QGSQUICKMAPCANVASMAP_H diff --git a/src/quickgui/qgsquickmapsettings.cpp b/src/quickgui/qgsquickmapsettings.cpp new file mode 100644 index 000000000000..0c4cd7a16e67 --- /dev/null +++ b/src/quickgui/qgsquickmapsettings.cpp @@ -0,0 +1,229 @@ +/*************************************************************************** + qgsquickmapsettings.cpp + -------------------------------------- + Date : 27.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 "qgsmaplayer.h" +#include "qgsmaplayerstylemanager.h" +#include "qgsmessagelog.h" +#include "qgsproject.h" + +#include "qgsquickmapsettings.h" + +QgsQuickMapSettings::QgsQuickMapSettings( QObject *parent ) + : QObject( parent ) + , mProject( 0 ) +{ + // Connect signals for derived values + connect( this, &QgsQuickMapSettings::destinationCrsChanged, this, &QgsQuickMapSettings::mapUnitsPerPixelChanged ); + connect( this, &QgsQuickMapSettings::extentChanged, this, &QgsQuickMapSettings::mapUnitsPerPixelChanged ); + connect( this, &QgsQuickMapSettings::outputSizeChanged, this, &QgsQuickMapSettings::mapUnitsPerPixelChanged ); + connect( this, &QgsQuickMapSettings::extentChanged, this, &QgsQuickMapSettings::visibleExtentChanged ); + connect( this, &QgsQuickMapSettings::rotationChanged, this, &QgsQuickMapSettings::visibleExtentChanged ); + connect( this, &QgsQuickMapSettings::outputSizeChanged, this, &QgsQuickMapSettings::visibleExtentChanged ); +} + +QgsQuickMapSettings::~QgsQuickMapSettings() +{ + +} + +void QgsQuickMapSettings::setProject( QgsProject *project ) +{ + if ( project == mProject ) + return; + + // If we have already something connected, disconnect it! + if ( mProject ) + { + disconnect( mProject, 0, this, 0 ); + } + + mProject = project; + + // Connect all signals + if ( mProject ) + { + connect( mProject, &QgsProject::readProject, this, &QgsQuickMapSettings::onReadProject ); + + // TODO: we have a problem here, since project can have alread .qgs loaded, so + // we are unable to get DOM to reload project settings for the canvas + // fortunately setProject is used only once at the very beginning of the run, + // so it should work .... + setDestinationCrs( mProject->crs() ); + + // Set Context too + mMapSettings.setTransformContext( mProject->transformContext() ); + } + else + { + mMapSettings.setTransformContext( QgsCoordinateTransformContext() ); + } + + emit projectChanged(); +} + +QgsProject *QgsQuickMapSettings::project() const +{ + return mProject; +} + +QgsCoordinateTransformContext QgsQuickMapSettings::transformContext() const +{ + return mMapSettings.transformContext(); +} + +QgsRectangle QgsQuickMapSettings::extent() const +{ + return mMapSettings.extent(); +} + +void QgsQuickMapSettings::setExtent( const QgsRectangle &extent ) +{ + if ( mMapSettings.extent() == extent ) + return; + + mMapSettings.setExtent( extent ); + emit extentChanged(); +} + +void QgsQuickMapSettings::setCenter( const QgsPoint ¢er ) +{ + QgsVector delta = QgsPointXY( center ) - mMapSettings.extent().center(); + + QgsRectangle e = mMapSettings.extent(); + e.setXMinimum( e.xMinimum() + delta.x() ); + e.setXMaximum( e.xMaximum() + delta.x() ); + e.setYMinimum( e.yMinimum() + delta.y() ); + e.setYMaximum( e.yMaximum() + delta.y() ); + + setExtent( e ); +} + +double QgsQuickMapSettings::mapUnitsPerPixel() const +{ + return mMapSettings.mapUnitsPerPixel(); +} + +QgsRectangle QgsQuickMapSettings::visibleExtent() const +{ + return mMapSettings.visibleExtent(); +} + +QPointF QgsQuickMapSettings::coordinateToScreen( const QgsPoint &p ) const +{ + QgsPointXY pt( p.x(), p.y() ); + QgsPointXY pp = mMapSettings.mapToPixel().transform( pt ); + return pp.toQPointF(); +} + +QgsPoint QgsQuickMapSettings::screenToCoordinate( const QPointF &p ) const +{ + const QgsPointXY pp = mMapSettings.mapToPixel().toMapCoordinates( p.toPoint() ); + return QgsPoint( pp ); +} + +QgsMapSettings QgsQuickMapSettings::mapSettings() const +{ + return mMapSettings; +} + +QSize QgsQuickMapSettings::outputSize() const +{ + return mMapSettings.outputSize(); +} + +void QgsQuickMapSettings::setOutputSize( const QSize &outputSize ) +{ + if ( mMapSettings.outputSize() == outputSize ) + return; + + mMapSettings.setOutputSize( outputSize ); + emit outputSizeChanged(); +} + +double QgsQuickMapSettings::outputDpi() const +{ + return mMapSettings.outputDpi(); +} + +void QgsQuickMapSettings::setOutputDpi( double outputDpi ) +{ + if ( mMapSettings.outputDpi() == outputDpi ) + return; + + mMapSettings.setOutputDpi( outputDpi ); + emit outputDpiChanged(); +} + +QgsCoordinateReferenceSystem QgsQuickMapSettings::destinationCrs() const +{ + return mMapSettings.destinationCrs(); +} + +void QgsQuickMapSettings::setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ) +{ + if ( mMapSettings.destinationCrs() == destinationCrs ) + return; + + mMapSettings.setDestinationCrs( destinationCrs ); + emit destinationCrsChanged(); +} + +QList QgsQuickMapSettings::layers() const +{ + return mMapSettings.layers(); +} + +void QgsQuickMapSettings::setLayers( const QList &layers ) +{ + mMapSettings.setLayers( layers ); + emit layersChanged(); +} + +void QgsQuickMapSettings::onReadProject( const QDomDocument &doc ) +{ + QDomNodeList nodes = doc.elementsByTagName( "mapcanvas" ); + if ( nodes.count() ) + { + QDomNode node = nodes.item( 0 ); + + mMapSettings.readXml( node ); + + if ( mMapSettings.rotation() != 0 ) + QgsMessageLog::logMessage( tr( "Map Canvas rotation is not supported. Resetting from %1 to 0." ).arg( mMapSettings.rotation() ) ); + + mMapSettings.setRotation( 0 ); + + emit extentChanged(); + emit destinationCrsChanged(); + emit outputSizeChanged(); + emit outputDpiChanged(); + emit layersChanged(); + } +} + +double QgsQuickMapSettings::rotation() const +{ + return mMapSettings.rotation(); +} + +void QgsQuickMapSettings::setRotation( double rotation ) +{ + if ( rotation == mMapSettings.rotation() ) + return; + + mMapSettings.setRotation( rotation ); + emit rotationChanged(); +} diff --git a/src/quickgui/qgsquickmapsettings.h b/src/quickgui/qgsquickmapsettings.h new file mode 100644 index 000000000000..5ceaff700448 --- /dev/null +++ b/src/quickgui/qgsquickmapsettings.h @@ -0,0 +1,143 @@ +/*************************************************************************** + qgsquickmapsettings.h + -------------------------------------- + Date : 27.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKMAPSETTINGS_H +#define QGSQUICKMAPSETTINGS_H + +#include + +#include "qgscoordinatetransformcontext.h" +#include "qgsmapsettings.h" +#include "qgsmapthemecollection.h" +#include "qgspoint.h" +#include "qgsrectangle.h" + +#include "qgis_quick.h" + +class QgsProject; + +/** + * \ingroup quick + * The QgsQuickMapSettings class encapsulates QgsMapSettings class to offer + * settings of configuration of map rendering via QML properties. + * + * \note QML Type: MapSettings + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickMapSettings : public QObject +{ + Q_OBJECT + + Q_PROPERTY( QgsProject *project READ project WRITE setProject NOTIFY projectChanged ) + Q_PROPERTY( QgsRectangle extent READ extent WRITE setExtent NOTIFY extentChanged ) + Q_PROPERTY( QgsRectangle visibleExtent READ visibleExtent NOTIFY visibleExtentChanged ) + Q_PROPERTY( double mapUnitsPerPixel READ mapUnitsPerPixel NOTIFY mapUnitsPerPixelChanged ) + Q_PROPERTY( double rotation READ rotation WRITE setRotation NOTIFY rotationChanged ) + Q_PROPERTY( QSize outputSize READ outputSize WRITE setOutputSize NOTIFY outputSizeChanged ) + Q_PROPERTY( double outputDpi READ outputDpi WRITE setOutputDpi NOTIFY outputDpiChanged ) + Q_PROPERTY( QgsCoordinateReferenceSystem destinationCrs READ destinationCrs WRITE setDestinationCrs NOTIFY destinationCrsChanged ) + Q_PROPERTY( QList layers READ layers WRITE setLayers NOTIFY layersChanged ) + + public: + QgsQuickMapSettings( QObject *parent = 0 ); + ~QgsQuickMapSettings(); + + QgsRectangle extent() const; + void setExtent( const QgsRectangle &extent ); + + void setProject( QgsProject *project ); + QgsProject *project() const; + + Q_INVOKABLE void setCenter( const QgsPoint ¢er ); + + double mapUnitsPerPixel() const; + + QgsRectangle visibleExtent() const; + + /** + * Returns the coordinate transform context, which stores various + * information regarding which datum transforms should be used when transforming points + * from a source to destination coordinate reference system. + */ + Q_INVOKABLE QgsCoordinateTransformContext transformContext() const; + + /** + * Convert a map coordinate to screen pixel coordinates + * + * @param p A coordinate in map coordinates + * + * @return A coordinate in pixel / screen space + */ + Q_INVOKABLE QPointF coordinateToScreen( const QgsPoint &p ) const; + + + /** + * Convert a screen coordinate to a map coordinate + * + * @param p A coordinate in pixel / screen coordinates + * + * @return A coordinate in map coordinates + */ + Q_INVOKABLE QgsPoint screenToCoordinate( const QPointF &p ) const; + + /** + * Sets the coordinate transform \a context, which stores various + * information regarding which datum transforms should be used when transforming points + * from a source to destination coordinate reference system. + * + * \since QGIS 3.0 + * \see transformContext() + */ + void setTransformContext( const QgsCoordinateTransformContext &context ); + + double rotation() const; + void setRotation( double rotation ); + + QgsMapSettings mapSettings() const; + + QSize outputSize() const; + void setOutputSize( const QSize &outputSize ); + + double outputDpi() const; + void setOutputDpi( double outputDpi ); + + QgsCoordinateReferenceSystem destinationCrs() const; + void setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ); + + QList layers() const; + void setLayers( const QList &layers ); + + signals: + void projectChanged(); + void extentChanged(); + void destinationCrsChanged(); + void mapUnitsPerPixelChanged(); + void rotationChanged(); + void visibleExtentChanged(); + void outputSizeChanged(); + void outputDpiChanged(); + void layersChanged(); + + private slots: + void onReadProject( const QDomDocument &doc ); + + private: + QgsProject *mProject; + QgsMapSettings mMapSettings; + +}; + +#endif // QGSQUICKMAPSETTINGS_H diff --git a/src/quickgui/qgsquickmaptransform.cpp b/src/quickgui/qgsquickmaptransform.cpp new file mode 100644 index 000000000000..2ab97a714326 --- /dev/null +++ b/src/quickgui/qgsquickmaptransform.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + qgsquickmaptransform.cpp + -------------------------------------- + Date : 27.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 "qgsquickmaptransform.h" +#include "qgsquickmapsettings.h" + +QgsQuickMapTransform::QgsQuickMapTransform() +{ +} + +QgsQuickMapTransform::~QgsQuickMapTransform() +{ +} + +void QgsQuickMapTransform::applyTo( QMatrix4x4 *matrix ) const +{ + *matrix *= mMatrix; + matrix->optimize(); +} + +QgsQuickMapSettings *QgsQuickMapTransform::mapSettings() const +{ + return mMapSettings; +} + +void QgsQuickMapTransform::setMapSettings( QgsQuickMapSettings *mapSettings ) +{ + if ( mapSettings == mMapSettings ) + return; + + if ( mMapSettings ) + disconnect( mMapSettings, &QgsQuickMapSettings::visibleExtentChanged, this, &QgsQuickMapTransform::updateMatrix ); + + mMapSettings = mapSettings; + + if ( mMapSettings ) + connect( mMapSettings, &QgsQuickMapSettings::visibleExtentChanged, this, &QgsQuickMapTransform::updateMatrix ); + + emit mapSettingsChanged(); +} + +void QgsQuickMapTransform::updateMatrix() +{ + QMatrix4x4 matrix; + float scaleFactor = 1 / mMapSettings->mapUnitsPerPixel(); + + matrix.scale( scaleFactor, -scaleFactor ); + matrix.translate( -mMapSettings->visibleExtent().xMinimum(), -mMapSettings->visibleExtent().yMaximum() ); + + mMatrix = matrix; + update(); +} diff --git a/src/quickgui/qgsquickmaptransform.h b/src/quickgui/qgsquickmaptransform.h new file mode 100644 index 000000000000..37824fc0361b --- /dev/null +++ b/src/quickgui/qgsquickmaptransform.h @@ -0,0 +1,62 @@ +/*************************************************************************** + qgsquickmaptransform.h + -------------------------------------- + Date : 27.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKMAPTRANSFORM_H +#define QGSQUICKMAPTRANSFORM_H + +#include +#include + +#include "qgis_quick.h" + +class QgsQuickMapSettings; + +/** + * \ingroup quick + * The QgsQuickMapTransform is transformation that can be attached to any QQuickItem. Transformation scales and translates + * Item based on the current QgsQuickMapSettings settings. + * + * For example it can be used on QgsQuickFeatureHighlight to place it correctly on the map canvas. + * + * \note QML Type: MapTransform + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickMapTransform : public QQuickTransform +{ + Q_OBJECT + Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) + + public: + QgsQuickMapTransform(); + ~QgsQuickMapTransform(); + + void applyTo( QMatrix4x4 *matrix ) const; + + QgsQuickMapSettings *mapSettings() const; + void setMapSettings( QgsQuickMapSettings *mapSettings ); + + signals: + void mapSettingsChanged(); + + private slots: + void updateMatrix(); + + private: + QgsQuickMapSettings *mMapSettings = nullptr; + QMatrix4x4 mMatrix; +}; + +#endif // QGSQUICKMAPTRANSFORM_H diff --git a/src/quickgui/qgsquickmessagelogmodel.cpp b/src/quickgui/qgsquickmessagelogmodel.cpp new file mode 100644 index 000000000000..02a4826576ba --- /dev/null +++ b/src/quickgui/qgsquickmessagelogmodel.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** + qgsquickmessagelogmodel.cpp + -------------------------------------- + date : 13.7.2016 + copyright : (C) 2016 by Matthias Kuhn + email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 + +#include "qgsapplication.h" + +#include "qgsquickmessagelogmodel.h" + +QgsQuickMessageLogModel::QgsQuickMessageLogModel( QObject *parent ) + : QAbstractListModel( parent ) + , mMessageLog( QgsApplication::messageLog() ) +{ + connect( mMessageLog, static_cast( &QgsMessageLog::messageReceived ), this, &QgsQuickMessageLogModel::onMessageReceived ); +} + +QHash QgsQuickMessageLogModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles[MessageRole] = "Message"; + roles[MessageTagRole] = "MessageTag"; + roles[MessageLevelRole] = "MessageLevel"; + + return roles; +} + +int QgsQuickMessageLogModel::rowCount( const QModelIndex &parent ) const +{ + Q_UNUSED( parent ) + return mMessages.size(); +} + +QVariant QgsQuickMessageLogModel::data( const QModelIndex &index, int role ) const +{ + if ( index.row() >= mMessages.size() ) + return QVariant(); + + if ( role == MessageRole ) + return mMessages.at( index.row() ).message; + else if ( role == MessageTagRole ) + return mMessages.at( index.row() ).tag; + else if ( role == MessageLevelRole ) + return mMessages.at( index.row() ).level; + + return QVariant(); +} + +void QgsQuickMessageLogModel::onMessageReceived( const QString &message, const QString &tag, QgsMessageLog::MessageLevel level ) +{ + beginInsertRows( QModelIndex(), 0, 0 ); + mMessages.prepend( LogMessage( tag, message, level ) ); + qDebug() << "Next message " << tag << " : " << message; + endInsertRows(); +} diff --git a/src/quickgui/qgsquickmessagelogmodel.h b/src/quickgui/qgsquickmessagelogmodel.h new file mode 100644 index 000000000000..0629638b231f --- /dev/null +++ b/src/quickgui/qgsquickmessagelogmodel.h @@ -0,0 +1,80 @@ +/*************************************************************************** + qgsquickmessagelogmodel.h + -------------------------------------- + date : 13.7.2016 + copyright : (C) 2016 by Matthias Kuhn + email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKMESSAGELOGMODEL_H +#define QGSQUICKMESSAGELOGMODEL_H + +#include + +#include "qgsmessagelog.h" + +#include "qgis_quick.h" + +/** + * \ingroup quick + * + * This model will connect to the QgsMessageLog and publish any + * messages received from there. Can be used as a model for QListView, + * for example QgsQuick MessageLog class. + * + * \note QML Type: MessageLogModel + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickMessageLogModel : public QAbstractListModel +{ + Q_OBJECT + + struct LogMessage + { + LogMessage() + {} + + LogMessage( const QString &tag, const QString &message, QgsMessageLog::MessageLevel level ) + { + this->tag = tag; + this->message = message; + this->level = level; + } + + QString tag; + QString message; + QgsMessageLog::MessageLevel level; + }; + + enum Roles + { + MessageRole = Qt::UserRole, + MessageTagRole, + MessageLevelRole + }; + + public: + QgsQuickMessageLogModel( QObject *parent = nullptr ); + + QHash roleNames() const override; + + int rowCount( const QModelIndex &parent ) const override; + QVariant data( const QModelIndex &index, int role ) const override; + + private slots: + void onMessageReceived( const QString &message, const QString &tag, QgsMessageLog::MessageLevel level ); + + private: + QgsMessageLog *mMessageLog; + QVector mMessages; +}; + +#endif // QGSQUICKMESSAGELOGMODEL_H diff --git a/src/quickgui/qgsquickpositionkit.cpp b/src/quickgui/qgsquickpositionkit.cpp new file mode 100644 index 000000000000..cbb88d2270d4 --- /dev/null +++ b/src/quickgui/qgsquickpositionkit.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + qgsquickpositionkit.cpp + -------------------------------------- + Date : Dec. 2017 + Copyright : (C) 2017 Peter Petrik + Email : zilolv 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 "qgsmessagelog.h" + +#include "qgsquickpositionkit.h" +#include "qgsquicksimulatedpositionsource.h" + +QgsQuickPositionKit::QgsQuickPositionKit( QObject *parent ) + : QObject( parent ) + , mAccuracy( -1 ) + , mDirection( -1 ) + , mHasPosition( false ) + , mIsSimulated( false ) + , mSource( nullptr ) +{ + use_gps_location(); +} + +QGeoPositionInfoSource *QgsQuickPositionKit::gpsSource() +{ + // this should give us "true" position source + // on Linux it comes from Geoclue library + QGeoPositionInfoSource *source = QGeoPositionInfoSource::createDefaultSource( this ); + if ( source->error() != QGeoPositionInfoSource::NoError ) + { + QgsMessageLog::logMessage( tr( "Unable to create default GPS Position Source" ) + + "(" + QString::number( ( long )source->error() ) + ")" + , "QgsQuick" + , QgsMessageLog::WARNING ); + delete source; + return 0; + } + else + { + return source; + } +} + +QGeoPositionInfoSource *QgsQuickPositionKit::simulatedSource( double longitude, double latitude, double radius ) +{ + return new QgsQuickSimulatedPositionSource( this, longitude, latitude, radius ); +} + +void QgsQuickPositionKit::use_simulated_location( double longitude, double latitude, double radius ) +{ + QGeoPositionInfoSource *source = simulatedSource( longitude, latitude, radius ); + mIsSimulated = true; + replacePositionSource( source ); +} + +void QgsQuickPositionKit::use_gps_location() +{ + QGeoPositionInfoSource *source = gpsSource(); + mIsSimulated = false; + replacePositionSource( source ); +} + +void QgsQuickPositionKit::replacePositionSource( QGeoPositionInfoSource *source ) +{ + if ( mSource == source ) + return; + + if ( mSource ) + { + disconnect( mSource, 0, this, 0 ); + delete mSource; + mSource = 0; + } + + mSource = source; + + if ( mSource ) + { + connect( mSource, SIGNAL( positionUpdated( QGeoPositionInfo ) ), + this, SLOT( positionUpdated( QGeoPositionInfo ) ) ); + connect( mSource, SIGNAL( updateTimeout() ), this, SLOT( onUpdateTimeout() ) ); + mSource->startUpdates(); + qDebug() << "Position source changed: " << mSource->sourceName(); + } +} + +void QgsQuickPositionKit::positionUpdated( const QGeoPositionInfo &info ) +{ + if ( !info.coordinate().isValid() ) + { + // keep last valid position + mHasPosition = false; + emit hasPositionChanged(); + } + + + mPosition = QgsPoint( info.coordinate().longitude(), + info.coordinate().latitude(), + info.coordinate().altitude() ); // can be NaN + + if ( info.hasAttribute( QGeoPositionInfo::HorizontalAccuracy ) ) + mAccuracy = info.attribute( QGeoPositionInfo::HorizontalAccuracy ); + else + mAccuracy = -1; + if ( info.hasAttribute( QGeoPositionInfo::Direction ) ) + mDirection = info.attribute( QGeoPositionInfo::Direction ); + else + mDirection = -1; + + emit positionChanged(); + + if ( !mHasPosition ) + { + mHasPosition = true; + emit hasPositionChanged(); + } +} + +void QgsQuickPositionKit::onUpdateTimeout() +{ + if ( mHasPosition ) + { + mHasPosition = false; + emit hasPositionChanged(); + } +} diff --git a/src/quickgui/qgsquickpositionkit.h b/src/quickgui/qgsquickpositionkit.h new file mode 100644 index 000000000000..473030085c2c --- /dev/null +++ b/src/quickgui/qgsquickpositionkit.h @@ -0,0 +1,124 @@ +/*************************************************************************** + qgsquickpositionkit.h + -------------------------------------- + Date : Dec. 2017 + Copyright : (C) 2017 Peter Petrik + Email : zilolv 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 QGSQUICKPOSITIONKIT_H +#define QGSQUICKPOSITIONKIT_H + +#include +#include + +#include "qgspoint.h" + +#include "qgis_quick.h" + +/** + * \ingroup quick + * Convinient set of tools to read GPS position and accuracy. + * + * Also, if one can use use_simulated_location to specify simulated position. + * Simulated position source generates random points in circles around the selected + * point and radius. Real GPS position is not used in this mode. + * + * \note QML Type: PositionKit + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickPositionKit : public QObject +{ + Q_OBJECT + + /** + * GPS position in WGS84 coords + */ + Q_PROPERTY( QgsPoint position READ position NOTIFY positionChanged ) + + /** + * GPS position is available (position property is a valid number) + */ + Q_PROPERTY( bool hasPosition READ hasPosition NOTIFY hasPositionChanged ) + + /** + * GPS horizontal accuracy in meters, -1 if not available + */ + Q_PROPERTY( qreal accuracy READ accuracy NOTIFY positionChanged ) + + /** + * GPS direction, bearing in degrees clockwise from north to direction of travel. -1 if not available + */ + Q_PROPERTY( qreal direction READ direction NOTIFY positionChanged ) + + /** + * GPS position and accuracy is simulated (not real from GPS sensor). Default false (use real GPS) + */ + Q_PROPERTY( bool isSimulated READ simulated NOTIFY isSimulatedChanged ) + + public: + explicit QgsQuickPositionKit( QObject *parent = 0 ); + + bool hasPosition() const { return mHasPosition; } + QgsPoint position() const { return mPosition; } + qreal accuracy() const { return mAccuracy; } + qreal direction() const { return mDirection; } + bool simulated() const { return mIsSimulated; } + + /** + * Use simulated GPS source. + * + * We do not want to have the origin point as property + * We basically want to set it once based on project/map cente and keep + * it that way regardless of mapsettings change (e.g. zoom etc) + */ + Q_INVOKABLE void use_simulated_location( double longitude, double latitude, double radius ); + + /** + * Use real GPS source (not simulated) + */ + Q_INVOKABLE void use_gps_location(); + + signals: + void positionChanged(); + void hasPositionChanged(); + void isSimulatedChanged(); + void statusChanged(); + + public slots: + + private slots: + void positionUpdated( const QGeoPositionInfo &info ); + void onUpdateTimeout(); + + protected: + + protected: + QgsPoint mPosition; + qreal mAccuracy; + qreal mDirection; + bool mHasPosition; + + // Simulated source + bool mIsSimulated; + + QGeoPositionInfoSource *mSource; + + private: + void replacePositionSource( QGeoPositionInfoSource *source ); + QString calculateStatusLabel(); + + QGeoPositionInfoSource *gpsSource(); + QGeoPositionInfoSource *simulatedSource( double longitude, double latitude, double radius ); + +}; + +#endif // QGSQUICKPOSITIONKIT_H diff --git a/src/quickgui/qgsquickscalebarkit.cpp b/src/quickgui/qgsquickscalebarkit.cpp new file mode 100644 index 000000000000..2ca2cf023027 --- /dev/null +++ b/src/quickgui/qgsquickscalebarkit.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + qgsquickscalebarkit.cpp + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 +#include +#include + +#include "qgspointxy.h" +#include "qgsdistancearea.h" + +#include "qgsquickmapsettings.h" +#include "qgsquickscalebarkit.h" +#include "qgsquickutils.h" + +QgsQuickScaleBarKit::QgsQuickScaleBarKit( QObject *parent ) + : QObject( parent ) + , mMapSettings( 0 ) + , mPreferredWidth( 300 ) + , mWidth( mPreferredWidth ) + , mDistance( 0 ) + , mUnits( "" ) +{ + connect( this, SIGNAL( mapSettingsChanged() ), this, SLOT( updateScaleBar() ) ); + connect( this, SIGNAL( preferredWidthChanged() ), this, SLOT( updateScaleBar() ) ); +} + +QgsQuickScaleBarKit::~QgsQuickScaleBarKit() {} + + +void QgsQuickScaleBarKit::setMapSettings( QgsQuickMapSettings *mapSettings ) +{ + if ( mMapSettings == mapSettings ) + return; + + // If we have already something connected, disconnect it! + if ( mMapSettings ) + { + disconnect( mMapSettings, 0, this, 0 ); + } + + mMapSettings = mapSettings; + + // Connect all signals to change scale bar when needed! + if ( mMapSettings ) + { + connect( mMapSettings, SIGNAL( extentChanged() ), this, SLOT( updateScaleBar() ) ); + connect( mMapSettings, SIGNAL( destinationCrsChanged() ), this, SLOT( updateScaleBar() ) ); + connect( mMapSettings, SIGNAL( mapUnitsPerPixelChanged() ), this, SLOT( updateScaleBar() ) ); + connect( mMapSettings, SIGNAL( visibleExtentChanged() ), this, SLOT( updateScaleBar() ) ); + connect( mMapSettings, SIGNAL( outputSizeChanged() ), this, SLOT( updateScaleBar() ) ); + connect( mMapSettings, SIGNAL( outputDpiChanged() ), this, SLOT( updateScaleBar() ) ); + } + + emit mapSettingsChanged(); +} + +int QgsQuickScaleBarKit::width() const +{ + return mWidth; +} + +QString QgsQuickScaleBarKit::units() const +{ + return mUnits; +} + +int QgsQuickScaleBarKit::distance() const +{ + return mDistance; +} + +void QgsQuickScaleBarKit::updateScaleBar() +{ + if ( !mMapSettings ) + return; + + double dist = QgsQuickUtils::instance()->screenUnitsToMeters( mMapSettings, mPreferredWidth ); // meters + if ( dist > 1000.0 ) + { + dist = dist / 1000.0; // meters to kilometers + mUnits = "km"; + } + else + { + mUnits = "m"; + } + + // we want to show nice round distances e.g. 200 km instead of e.g. 273 km + // so we determine which "nice" number to use and also update the scale bar + // length accordingly. First digit will be 1, 2 or 5, the rest will be zeroes. + int digits = floor( log10( ( dist ) ) ); // number of digits after first one + double base = pow( 10, digits ); // e.g. for 1234 this will be 1000 + double first_digit = dist / base; // get the first digit + int round_digit; + if ( first_digit < 2 ) + round_digit = 1; + else if ( first_digit < 5 ) + round_digit = 2; + else + round_digit = 5; + + mDistance = round_digit * base; + mWidth = mPreferredWidth * mDistance / dist; + //qDebug() << "Scale: " << mWidth << "px -> " << mDistance << " " << mUnits; + + emit scaleBarChanged(); +} diff --git a/src/quickgui/qgsquickscalebarkit.h b/src/quickgui/qgsquickscalebarkit.h new file mode 100644 index 000000000000..900cdb1a44eb --- /dev/null +++ b/src/quickgui/qgsquickscalebarkit.h @@ -0,0 +1,97 @@ +/*************************************************************************** + qgsquickscalebarkit.h + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 QGSQUICKSCALEBARKIT_H +#define QGSQUICKSCALEBARKIT_H + +#include +#include + +#include "qgis_quick.h" + +class QgsQuickMapSettings; + +/** + * \ingroup quick + * + * The class QgsQuickScaleBarKit encapsulates the utilies to calculate + * scale bar properties + * + * It requires connection to mapSettings of the active canvas to automatically + * update text and width + * + * From preferred width in pixel, it calculates the width (pixel) of scalebar + * distance in meters or kilometers (int) rounded to "nice" number (e.g. 72.4 to 100) + * and units text (e.g. km) + * + * \note QML Type: ScaleBarKit + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickScaleBarKit : public QObject +{ + Q_OBJECT + + Q_PROPERTY( QgsQuickMapSettings *mapSettings MEMBER mMapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) + + /** + * Preferred width of scalebar in pixels. Defaults to 300 + */ + Q_PROPERTY( int preferredWidth MEMBER mPreferredWidth NOTIFY preferredWidthChanged ) + + /** + * Units of distance (e.g. km or m) Read-only (result) + */ + Q_PROPERTY( QString units READ units NOTIFY scaleBarChanged ) + + /** + * Distance rounded to "nice" number (e.g. 100, 20). To be used with units property for labels. Read-only (result) + */ + Q_PROPERTY( int distance READ distance NOTIFY scaleBarChanged ) + + /** + * Calculated width of scalebar in pixels representing distance + units. Differs minimum possible from prefferedWidth to + * get "nice" distance number. + */ + Q_PROPERTY( int width READ width NOTIFY scaleBarChanged ) + + public: + explicit QgsQuickScaleBarKit( QObject *parent = 0 ); + ~QgsQuickScaleBarKit(); + + void setMapSettings( QgsQuickMapSettings *mapSettings ); + int width() const; + int distance() const; + QString units() const; + + signals: + void scaleBarChanged(); + void mapSettingsChanged(); + void preferredWidthChanged(); + + public slots: + void updateScaleBar(); + + private: + QgsQuickMapSettings *mMapSettings; + + int mPreferredWidth; // pixels + int mWidth; // pixels + int mDistance; // in meters or kilometers, rounded + QString mUnits; // km or m +}; + + +#endif // QGSQUICKSCALEBARKIT_H diff --git a/src/quickgui/qgsquicksimulatedpositionsource.cpp b/src/quickgui/qgsquicksimulatedpositionsource.cpp new file mode 100644 index 000000000000..1443f8280222 --- /dev/null +++ b/src/quickgui/qgsquicksimulatedpositionsource.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + qgsquicksimulatedpositionsource.cpp + -------------------------------------- + Date : Dec. 2017 + Copyright : (C) 2017 Peter Petrik + Email : zilolv 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 "qgsquicksimulatedpositionsource.h" + +/// @cond PRIVATE + +QgsQuickSimulatedPositionSource::QgsQuickSimulatedPositionSource( QObject *parent, double longitude, double latitude, double flightRadius ) + : QGeoPositionInfoSource( parent ) + , mTimer( new QTimer( this ) ) + , mAngle( 0 ) + , mFlightRadius( flightRadius ) + , mLongitude( longitude ) + , mLatitude( latitude ) +{ + connect( mTimer, SIGNAL( timeout() ), this, SLOT( readNextPosition() ) ); +} + +void QgsQuickSimulatedPositionSource::startUpdates() +{ + int interval = updateInterval(); + if ( interval < minimumUpdateInterval() ) + interval = minimumUpdateInterval(); + + mTimer->start( interval ); +} + +void QgsQuickSimulatedPositionSource::stopUpdates() +{ + mTimer->stop(); +} + +void QgsQuickSimulatedPositionSource::requestUpdate( int /*timeout*/ ) +{ + readNextPosition(); +} + +void QgsQuickSimulatedPositionSource::readNextPosition() +{ + double latitude = mLatitude, longitude = mLongitude; + latitude += sin( mAngle * M_PI / 180 ) * mFlightRadius; + longitude += cos( mAngle * M_PI / 180 ) * mFlightRadius; + mAngle += 1; + + QGeoCoordinate coordinate( latitude, longitude ); + double altitude = std::rand() % 40 + 20; // rand altitude <20,55>m and lost (0) + if ( altitude <= 55 ) + { + coordinate.setAltitude( altitude ); // 3D + } + + QDateTime timestamp = QDateTime::currentDateTime(); + + QGeoPositionInfo info( coordinate, timestamp ); + if ( info.isValid() ) + { + mLastPosition = info; + info.setAttribute( QGeoPositionInfo::Direction, 360 - int( mAngle ) % 360 ); + int accuracy = std::rand() % 40 + 20; // rand accuracy <20,55>m and lost (-1) + if ( accuracy > 55 ) + { + accuracy = -1; + } + info.setAttribute( QGeoPositionInfo::HorizontalAccuracy, accuracy ); + emit positionUpdated( info ); + } +} + +/// @endcond diff --git a/src/quickgui/qgsquicksimulatedpositionsource.h b/src/quickgui/qgsquicksimulatedpositionsource.h new file mode 100644 index 000000000000..d1e45af77a47 --- /dev/null +++ b/src/quickgui/qgsquicksimulatedpositionsource.h @@ -0,0 +1,82 @@ +/*************************************************************************** + qgsquicksimulatedpositionsource.h + -------------------------------------- + Date : Dec. 2017 + Copyright : (C) 2017 Peter Petrik + Email : zilolv 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 QGSQUICKSIMULATEDPOSITIONSOURCE_H +#define QGSQUICKSIMULATEDPOSITIONSOURCE_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 "qgis_quick.h" +#include +#include +#include + +/** + * \ingroup quick + * This is an internal (implementation) class used to generate (fake) GPS position source + * Useful for for testing purposes (e.g. testing of the application with map for different + * location then your physical (GPS) location) + * + * Simulated position source generates random points in circles around the selected + * point and radius. Real GPS position is not used in this mode. + * + * \note QML Type: not exported + * + * \since QGIS 3.2 + */ + +//! +class QUICK_NO_EXPORT QgsQuickSimulatedPositionSource : public QGeoPositionInfoSource +{ + Q_OBJECT + public: + QgsQuickSimulatedPositionSource( QObject *parent, double longitude, double latitude, double flightRadius ); + + QGeoPositionInfo lastKnownPosition( bool /*fromSatellitePositioningMethodsOnly = false*/ ) const { return mLastPosition; } + PositioningMethods supportedPositioningMethods() const { return AllPositioningMethods; } + int minimumUpdateInterval() const { return 1000; } + Error error() const { return QGeoPositionInfoSource::NoError; } + + public slots: + virtual void startUpdates(); + virtual void stopUpdates(); + + virtual void requestUpdate( int timeout = 5000 ); + + private slots: + void readNextPosition(); + + private: + QTimer *mTimer; + QGeoPositionInfo mLastPosition; + double mAngle; + + double mFlightRadius; + double mLongitude; + double mLatitude; +}; + +/// @endcond + +#endif // QGSQUICKSIMULATEDPOSITIONSOURCE_H diff --git a/src/quickgui/qgsquicksubmodel.cpp b/src/quickgui/qgsquicksubmodel.cpp new file mode 100644 index 000000000000..bdb85b81e566 --- /dev/null +++ b/src/quickgui/qgsquicksubmodel.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** + qgsquicksubmodel.cpp + -------------------------------------- + Date : 16.9.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 "qgsquicksubmodel.h" +#include + +QgsQuickSubModel::QgsQuickSubModel( QObject *parent ) + : QAbstractItemModel( parent ) + , mModel( 0 ) +{ +} + +QModelIndex QgsQuickSubModel::index( int row, int column, const QModelIndex &parent ) const +{ + if ( !mModel ) + return mRootIndex; + + QModelIndex sourceIndex = mModel->index( row, column, parent.isValid() ? mapToSource( parent ) : static_cast( mRootIndex ) ); + return mapFromSource( sourceIndex ); +} + +QModelIndex QgsQuickSubModel::parent( const QModelIndex &child ) const +{ + if ( !mModel ) + return mRootIndex; + + QModelIndex idx = mModel->parent( child ); + if ( idx == mRootIndex ) + return QModelIndex(); + else + return mapFromSource( idx ); +} + +int QgsQuickSubModel::rowCount( const QModelIndex &parent ) const +{ + if ( !mModel ) + return 0; + + return mModel->rowCount( parent.isValid() ? mapToSource( parent ) : static_cast( mRootIndex ) ); +} + +int QgsQuickSubModel::columnCount( const QModelIndex &parent ) const +{ + if ( !mModel ) + return 0; + + return mModel->columnCount( parent.isValid() ? mapToSource( parent ) : static_cast( mRootIndex ) ); +} + +QVariant QgsQuickSubModel::data( const QModelIndex &index, int role ) const +{ + if ( !mModel ) + return QVariant(); + + return mModel->data( mapToSource( index ), role ); +} + +bool QgsQuickSubModel::setData( const QModelIndex &index, const QVariant &value, int role ) +{ + if ( !mModel ) + return false; + + return mModel->setData( mapToSource( index ), value, role ); +} + +QHash QgsQuickSubModel::roleNames() const +{ + if ( !mModel ) + return QHash(); + + return mModel->roleNames(); +} + +QModelIndex QgsQuickSubModel::rootIndex() const +{ + return mRootIndex; +} + +void QgsQuickSubModel::setRootIndex( const QModelIndex &rootIndex ) +{ + if ( rootIndex == mRootIndex ) + return; + + beginResetModel(); + mRootIndex = rootIndex; + endResetModel(); + emit rootIndexChanged(); +} + +QAbstractItemModel *QgsQuickSubModel::model() const +{ + return mModel; +} + +void QgsQuickSubModel::setModel( QAbstractItemModel *model ) +{ + if ( model == mModel ) + return; + + if ( model ) + { + connect( model, &QAbstractItemModel::rowsAboutToBeInserted, this, &QgsQuickSubModel::onRowsAboutToBeInserted ); + connect( model, &QAbstractItemModel::rowsInserted, this, &QgsQuickSubModel::onRowsInserted ); + connect( model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &QgsQuickSubModel::onRowsAboutToBeRemoved ); + connect( model, &QAbstractItemModel::rowsRemoved, this, &QgsQuickSubModel::onRowsRemoved ); + connect( model, &QAbstractItemModel::modelAboutToBeReset, this, &QgsQuickSubModel::onModelAboutToBeReset ); + connect( model, &QAbstractItemModel::modelReset, this, &QAbstractItemModel::modelReset ); + connect( model, &QAbstractItemModel::dataChanged, this, &QgsQuickSubModel::onDataChanged ); + } + + mModel = model; + emit modelChanged(); +} + +void QgsQuickSubModel::onRowsAboutToBeInserted( const QModelIndex &parent, int first, int last ) +{ + emit beginInsertRows( mapFromSource( parent ), first, last ); +} + +void QgsQuickSubModel::onRowsInserted( const QModelIndex &parent, int first, int last ) +{ + Q_UNUSED( parent ) + Q_UNUSED( first ) + Q_UNUSED( last ) + emit endInsertRows(); +} + +void QgsQuickSubModel::onRowsAboutToBeRemoved( const QModelIndex &parent, int first, int last ) +{ + emit beginRemoveRows( mapFromSource( parent ), first, last ); +} + +void QgsQuickSubModel::onRowsRemoved( const QModelIndex &parent, int first, int last ) +{ + Q_UNUSED( parent ) + Q_UNUSED( first ) + Q_UNUSED( last ) + emit endRemoveRows(); +} + +void QgsQuickSubModel::onModelAboutToBeReset() +{ + mMappings.clear(); +} + +void QgsQuickSubModel::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles ) +{ + emit dataChanged( mapFromSource( topLeft ), mapFromSource( bottomRight ), roles ); +} + +QModelIndex QgsQuickSubModel::mapFromSource( const QModelIndex &sourceIndex ) const +{ + if ( sourceIndex == mRootIndex || !sourceIndex.isValid() ) + return QModelIndex(); + + if ( !mMappings.contains( sourceIndex.internalId() ) ) + { + mMappings.insert( sourceIndex.internalId(), sourceIndex.parent() ); + } + + return createIndex( sourceIndex.row(), sourceIndex.column(), sourceIndex.internalId() ); +} + +QModelIndex QgsQuickSubModel::mapToSource( const QModelIndex &index ) const +{ + if ( !index.isValid() ) + return mRootIndex; + + if ( !mModel ) + return mRootIndex; + + return mModel->index( index.row(), index.column(), mMappings.find( index.internalId() ).value() ); +} diff --git a/src/quickgui/qgsquicksubmodel.h b/src/quickgui/qgsquicksubmodel.h new file mode 100644 index 000000000000..7c8156834f1d --- /dev/null +++ b/src/quickgui/qgsquicksubmodel.h @@ -0,0 +1,77 @@ +/*************************************************************************** + qgsquicksubmodel.h + -------------------------------------- + Date : 16.9.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKSUBMODEL_H +#define QGSQUICKSUBMODEL_H + +#include "qgis_quick.h" +#include +#include "qgsquickattributeformmodel.h" + +/** + * \ingroup quick + * + * Helper class for submodels (e.g. tabs within feature model) + * + * \note QML Type: SubModel + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickSubModel : public QAbstractItemModel +{ + Q_OBJECT + + Q_PROPERTY( QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged ) + Q_PROPERTY( QModelIndex rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged ) + + public: + QgsQuickSubModel( QObject *parent = nullptr ); + QModelIndex index( int row, int column, const QModelIndex &parent ) const override; + QModelIndex parent( const QModelIndex &child ) const override; + int rowCount( const QModelIndex &parent ) const override; + int columnCount( const QModelIndex &parent ) const override; + QVariant data( const QModelIndex &index, int role ) const override; + bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override; + QHash roleNames() const override; + + QModelIndex rootIndex() const; + void setRootIndex( const QModelIndex &rootIndex ); + + QAbstractItemModel *model() const; + void setModel( QAbstractItemModel *model ); + + signals: + void modelChanged(); + void rootIndexChanged(); + + private slots: + void onRowsAboutToBeInserted( const QModelIndex &parent, int first, int last ); + void onRowsInserted( const QModelIndex &parent, int first, int last ); + void onRowsAboutToBeRemoved( const QModelIndex &parent, int first, int last ); + void onRowsRemoved( const QModelIndex &parent, int first, int last ); + void onModelAboutToBeReset(); + void onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector() ); + + private: + QModelIndex mapFromSource( const QModelIndex &sourceIndex ) const; + QModelIndex mapToSource( const QModelIndex &index ) const; + + QAbstractItemModel *mModel; + QPersistentModelIndex mRootIndex; + + // Map internal id to parent index + mutable QHash mMappings; +}; + +#endif // QGSQUICKSUBMODEL_H diff --git a/src/quickgui/qgsquickutils.cpp b/src/quickgui/qgsquickutils.cpp new file mode 100644 index 000000000000..826aba7c18c3 --- /dev/null +++ b/src/quickgui/qgsquickutils.cpp @@ -0,0 +1,247 @@ +/*************************************************************************** + qgsquickutils.cpp + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 +#include +#include +#include +#include +#include +#include + +#include "qgscoordinatereferencesystem.h" +#include "qgscoordinatetransform.h" +#include "qgsdistancearea.h" +#include "qgsmessagelog.h" +#include "qgsvectorlayer.h" + +#include "qgsquickmapsettings.h" +#include "qgsquickutils.h" + + +QgsQuickUtils *QgsQuickUtils::sInstance = 0; + +QgsQuickUtils *QgsQuickUtils::instance() +{ + if ( !sInstance ) + { + qDebug() << "QgsQuickUtils created: " << QThread::currentThreadId(); + sInstance = new QgsQuickUtils(); + } + return sInstance; +} + +QgsQuickUtils::QgsQuickUtils( QObject *parent ) + : QObject( parent ) +{ + + // calculate screen density for calculation of real pixel sizes from density-independent pixels + int dpiX = QApplication::desktop()->physicalDpiX(); + int dpiY = QApplication::desktop()->physicalDpiY(); + int dpi = dpiX < dpiY ? dpiX : dpiY; // In case of asymetrical DPI. Improbable + mScreenDensity = dpi / 160.; // 160 DPI is baseline for density-independent pixels in Android +} + +QgsQuickUtils::~QgsQuickUtils() +{ +} + +QgsCoordinateReferenceSystem QgsQuickUtils::coordinateReferenceSystemFromEpsgId( long epsg ) const +{ + return QgsCoordinateReferenceSystem::fromEpsgId( epsg ); +} + +QgsPointXY QgsQuickUtils::pointXYFactory( double x, double y ) const +{ + return QgsPointXY( x, y ); +} + +QgsPoint QgsQuickUtils::pointFactory( double x, double y ) const +{ + return QgsPoint( x, y ); +} + + +QgsPointXY QgsQuickUtils::transformPoint( const QgsCoordinateReferenceSystem &srcCrs, + const QgsCoordinateReferenceSystem &destCrs, + const QgsCoordinateTransformContext &context, + const QgsPointXY &srcPoint ) const +{ + QgsCoordinateTransform mTransform( srcCrs, destCrs, context ); + QgsPointXY pt = mTransform.transform( srcPoint ); + return pt; +} + +bool QgsQuickUtils::hasValidGeometry( QgsVectorLayer *layer, const QgsFeature &feat ) +{ + if ( !layer ) + return false; + + if ( !feat.hasGeometry() ) + return false; + + if ( feat.geometry().type() != layer->geometryType() ) + return false; + + if ( QgsWkbTypes::hasZ( layer->wkbType() ) != QgsWkbTypes::hasZ( feat.geometry().wkbType() ) ) + return false; + + return true; +} + +double QgsQuickUtils::screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels ) const +{ + if ( mapSettings == 0 ) return 0; + + QgsDistanceArea mDistanceArea; + mDistanceArea.setEllipsoid( "WGS84" ); + mDistanceArea.setSourceCrs( mapSettings->destinationCrs(), mapSettings->transformContext() ); + + // calculate the geographic distance from the central point of extent + // to the specified number of points on the right side + QSize s = mapSettings->outputSize(); + QPoint pointCenter( s.width() / 2, s.height() / 2 ); + QgsPointXY p1 = mapSettings->screenToCoordinate( pointCenter ); + QgsPointXY p2 = mapSettings->screenToCoordinate( pointCenter + QPoint( baseLengthPixels, 0 ) ); + return mDistanceArea.measureLine( p1, p2 ); +} + +bool QgsQuickUtils::fileExists( QString path ) +{ + QFileInfo check_file( path ); + // check if file exists and if yes: Is it really a file and no directory? + if ( check_file.exists() && check_file.isFile() ) + { + return true; + } + else + { + return false; + } +} + +void QgsQuickUtils::copyFile( QString sourcePath, QString targetPath ) +{ + if ( !fileExists( sourcePath ) ) + { + qDebug() << "Source file does not exist!" << sourcePath; + return; + } + + if ( !QDir::root().mkpath( targetPath ) ) + { + QgsApplication::messageLog()->logMessage( tr( "Could not create folder %1" ).arg( targetPath ), "QgsQuick", QgsMessageLog::CRITICAL ); + return; + } + + QDir dir( targetPath ); + QString filename( QFile( sourcePath ).fileName() ); + + if ( !QFile( sourcePath ).rename( dir.absoluteFilePath( filename ) ) ) + { + qDebug() << "Couldn't rename file! Trying to copy instead"; + if ( !QFile( sourcePath ).copy( dir.absoluteFilePath( filename ) ) ) + { + QgsApplication::messageLog()->logMessage( tr( "File %1 could not be copied to folder %2.", "QgsQuick", QgsMessageLog::CRITICAL ).arg( sourcePath, targetPath ) ); + return; + } + } +} + +void QgsQuickUtils::remove( QString path ) +{ + QFile::remove( path ); +} + +QString QgsQuickUtils::getFileName( QString path ) +{ + QFileInfo fileInfo( path ); + QString filename( fileInfo.fileName() ); + return filename; +} + +void QgsQuickUtils::logMessage( const QString &message, const QString &tag, QgsMessageLog::MessageLevel level ) +{ + QgsMessageLog::logMessage( message, tag, level ); +} + +QUrl QgsQuickUtils::getThemeIcon( const QString &name ) +{ + QString extension( ".svg" ); + QString path = "qrc:/" + name + extension; + qDebug() << "Using icon " << name << " from " << path; + return QUrl( path ); +} + +QString QgsQuickUtils::qgsPointToString( const QgsPoint &point, int decimals ) +{ + QString label; + label += QString::number( point.x(), 'f', decimals ); + label += ", "; + label += QString::number( point.y(), 'f', decimals ); + return label; +} + +QString QgsQuickUtils::distanceToString( qreal dist, int decimals ) +{ + if ( dist < 0 ) + { + return "0 m"; + } + + QString label; + if ( dist > 1000 ) + { + label += QString::number( dist / 1000.0, 'f', decimals ); + label += QString( " km" ); + } + else + { + if ( dist > 1 ) + { + label += QString::number( dist, 'f', decimals ); + label += QString( " m" ); + } + else + { + label += QString::number( dist * 1000, 'f', decimals ); + label += QString( " mm" ); + } + } + return label; +} + +QString QgsQuickUtils::dumpScreenInfo() const +{ + QRect rec = QApplication::desktop()->screenGeometry(); + int dpiX = QApplication::desktop()->physicalDpiX(); + int dpiY = QApplication::desktop()->physicalDpiY(); + int height = rec.height(); + int width = rec.width(); + double sizeX = ( double ) width / dpiX * 25.4; + double sizeY = ( double ) height / dpiY * 25.4; + + QString msg; + msg += "screen resolution: " + QString::number( width ) + "x" + QString::number( height ) + " px\n"; + msg += "screen DPI: " + QString::number( dpiX ) + "x" + QString::number( dpiY ) + "\n"; + msg += "screen size: " + QString::number( sizeX, 'f', 0 ) + "x" + QString::number( sizeY, 'f', 0 ) + " mm\n"; + msg += "screen density: " + QString::number( mScreenDensity ); + return msg; +} + +qreal QgsQuickUtils::screenDensity() const +{ + return mScreenDensity; +} diff --git a/src/quickgui/qgsquickutils.h b/src/quickgui/qgsquickutils.h new file mode 100644 index 000000000000..0e65d56349ba --- /dev/null +++ b/src/quickgui/qgsquickutils.h @@ -0,0 +1,144 @@ +/*************************************************************************** + qgsquickutils.h + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 QGSQUICKUTILS_H +#define QGSQUICKUTILS_H + + +#include +#include +#include + +#include "qgsfeature.h" +#include "qgsmessagelog.h" +#include "qgspoint.h" +#include "qgspointxy.h" + +#include "qgsquickmapsettings.h" +#include "qgis_quick.h" + + +class QgsFeature; +class QgsVectorLayer; +class QgsCoordinateReferenceSystem; + +/** + * \ingroup quick + * + * Singleton encapsulating the common utilies for QgsQuick library. + * + * + * \since QGIS 3.2 + */ +class QUICK_EXPORT QgsQuickUtils: public QObject +{ + Q_OBJECT + + /** + * "dp" is useful for building building components that work well with different screen densities. + * It stands for density-independent pixels. A width of 10dp is going to be the same physical size + * on all screens regardless their density. In QML code, all values are specified in screen pixels, + * so in order to set a width of 10dp, one would use the following code: "width: 10 * QgsQuick.Utils.dp" + * + * 1dp is approximately 0.16mm. When screen has 160 DPI (baseline), the value of "dp" is 1. + * On high DPI screen the value will be greater, e.g. 1.5. + */ + Q_PROPERTY( qreal dp READ screenDensity CONSTANT ) + + public: + static QgsQuickUtils *instance(); + + //! Calculated density of the screen - see "dp" property for more details + qreal screenDensity() const; + + // CRS and geometry + + /** + * Create crs from epsg code in QML + */ + Q_INVOKABLE QgsCoordinateReferenceSystem coordinateReferenceSystemFromEpsgId( long epsg ) const; + + /** + * Create QgsPointXY in QML + */ + Q_INVOKABLE QgsPointXY pointXYFactory( double x, double y ) const; + + /** + * Create QgsPoint in QML + */ + Q_INVOKABLE QgsPoint pointFactory( double x, double y ) const; + + /** + * Transform point between different crs from QML + */ + Q_INVOKABLE QgsPointXY transformPoint( const QgsCoordinateReferenceSystem &srcCrs, + const QgsCoordinateReferenceSystem &destCrs, + const QgsCoordinateTransformContext &context, + const QgsPointXY &srcPoint ) const; + + /** + * Calculate the distance in meter representing baseLengthPixels pixels on the screen based on the current map settings. + */ + Q_INVOKABLE double screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels ) const; + + /** + * Has QgsFeature a geometry that can be added to the layer (non-emptry, same geometry type)? + */ + Q_INVOKABLE bool hasValidGeometry( QgsVectorLayer *layer, const QgsFeature &feat ); + + // Common + Q_INVOKABLE bool fileExists( QString path ); + Q_INVOKABLE void copyFile( QString sourcePath, QString targetPath ); + Q_INVOKABLE void remove( QString path ); + Q_INVOKABLE QString getFileName( QString path ); + Q_INVOKABLE void logMessage( const QString &message, + const QString &tag = QString( "QgsQuick" ), + QgsMessageLog::MessageLevel level = QgsMessageLog::WARNING ); + + // Themes + + /** + * Get icon from custom theme dir or default if not found in the theme dir + */ + Q_INVOKABLE QUrl getThemeIcon( const QString &name ); + + // Formatting + + /** + * point to string, e.g. -2.234521, 34.4444421 -> -2.234, 34.444 + */ + Q_INVOKABLE QString qgsPointToString( const QgsPoint &point, int decimals = 3 ); + + /** + * distance in meters to human readable length e.g. 1222.234 m -> 1.2 km + */ + Q_INVOKABLE QString distanceToString( qreal dist, int decimals = 1 ); + + //! Returns a string with information about screen size and resolution - useful for debugging + QString dumpScreenInfo() const; + + signals: + + private: + explicit QgsQuickUtils( QObject *parent = 0 ); + ~QgsQuickUtils(); + + static QgsQuickUtils *sInstance; + + qreal mScreenDensity; + +}; + +#endif // QGSQUICKUTILS_H diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index c803622e9ee3..514902f5cc29 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -46,4 +46,7 @@ IF (ENABLE_TESTS) ADD_SUBDIRECTORY(python) ENDIF (WITH_BINDINGS) ADD_SUBDIRECTORY(geometry_checker) + IF (WITH_QUICK) + ADD_SUBDIRECTORY(quickgui) + ENDIF (WITH_QUICK) ENDIF (ENABLE_TESTS) diff --git a/tests/src/quickgui/CMakeLists.txt b/tests/src/quickgui/CMakeLists.txt new file mode 100644 index 000000000000..9f3c9340318a --- /dev/null +++ b/tests/src/quickgui/CMakeLists.txt @@ -0,0 +1,87 @@ +# Standard includes and utils to compile into all tests. + +##################################################### +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/core/annotations + ${CMAKE_SOURCE_DIR}/src/core/auth + ${CMAKE_SOURCE_DIR}/src/core/composer + ${CMAKE_SOURCE_DIR}/src/core/fieldformatter + ${CMAKE_SOURCE_DIR}/src/core/geometry + ${CMAKE_SOURCE_DIR}/src/core/layertree + ${CMAKE_SOURCE_DIR}/src/core/layout + ${CMAKE_SOURCE_DIR}/src/core/locator + ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/providers/memory + ${CMAKE_SOURCE_DIR}/src/core/raster + ${CMAKE_SOURCE_DIR}/src/core/scalebar + ${CMAKE_SOURCE_DIR}/src/core/symbology + ${CMAKE_SOURCE_DIR}/src/core/effects + ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/expression + ${CMAKE_SOURCE_DIR}/src/native + ${CMAKE_SOURCE_DIR}/src/quickgui + ${CMAKE_SOURCE_DIR}/src/test + + ${CMAKE_BINARY_DIR}/src/core + ${CMAKE_BINARY_DIR}/src/native + ${CMAKE_BINARY_DIR}/src/quickgui +) + +INCLUDE_DIRECTORIES(SYSTEM + ${LIBZIP_INCLUDE_DIRS} + ${SPATIALINDEX_INCLUDE_DIR} + ${PROJ_INCLUDE_DIR} + ${GEOS_INCLUDE_DIR} + ${GDAL_INCLUDE_DIR} + ${EXPAT_INCLUDE_DIR} + ${SQLITE3_INCLUDE_DIR} + ${SPATIALITE_INCLUDE_DIR} + ${QCA_INCLUDE_DIR} + ${QTKEYCHAIN_INCLUDE_DIR} +) + +#note for tests we should not include the moc of our +#qtests in the executable file list as the moc is +#directly included in the sources +#and should not be compiled twice. Trying to include +#them in will cause an error at build time + +#No relinking and full RPATH for the install tree +#See: http://www.cmake.org/Wiki/CMake_RPATH_handling#No_relinking_and_full_RPATH_for_the_install_tree + +MACRO (ADD_QGIS_TEST testname testsrc) + SET(qgis_${testname}_SRCS ${testsrc} ${util_SRCS}) + ADD_CUSTOM_TARGET(qgis_${testname}moc ALL DEPENDS ${qgis_${testname}_MOC_SRCS}) + ADD_EXECUTABLE(qgis_${testname} ${qgis_${testname}_SRCS}) + SET_TARGET_PROPERTIES(qgis_${testname} PROPERTIES AUTOMOC TRUE) + TARGET_LINK_LIBRARIES(qgis_${testname} + ${QT_QTXML_LIBRARY} + ${QT_QTCORE_LIBRARY} + ${QT_QTSVG_LIBRARY} + ${QT_QTTEST_LIBRARY} + Qt5::Gui + Qt5::Qml + Qt5::Quick + Qt5::Xml + Qt5::Widgets + qgis_core + qgis_quick) + + ADD_TEST(qgis_${testname} ${CMAKE_CURRENT_BINARY_DIR}/../../../output/bin/qgis_${testname} -maxwarnings 10000) +ENDMACRO (ADD_QGIS_TEST) + +############################################################# +# Tests: + +ADD_QGIS_TEST(qgsquickutils testqgsquickutils.cpp) + + +############################################################# +# Add also test application +ADD_SUBDIRECTORY(app) diff --git a/tests/src/quickgui/app/CMakeLists.txt b/tests/src/quickgui/app/CMakeLists.txt new file mode 100644 index 000000000000..a83d98c300d7 --- /dev/null +++ b/tests/src/quickgui/app/CMakeLists.txt @@ -0,0 +1,82 @@ +SET(QGIS_QUICK_APP_MOC_HDRS +) + +SET(QGIS_QUICK_APP_SRCS + main.cpp +) + +SET(QGIS_QUICK_APP_QMLS + main.qml + FeaturePanel.qml +) + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/core/annotations + ${CMAKE_SOURCE_DIR}/src/core/auth + ${CMAKE_SOURCE_DIR}/src/core/composer + ${CMAKE_SOURCE_DIR}/src/core/fieldformatter + ${CMAKE_SOURCE_DIR}/src/core/geometry + ${CMAKE_SOURCE_DIR}/src/core/layertree + ${CMAKE_SOURCE_DIR}/src/core/layout + ${CMAKE_SOURCE_DIR}/src/core/locator + ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/providers/memory + ${CMAKE_SOURCE_DIR}/src/core/raster + ${CMAKE_SOURCE_DIR}/src/core/scalebar + ${CMAKE_SOURCE_DIR}/src/core/symbology + ${CMAKE_SOURCE_DIR}/src/core/effects + ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/expression + ${CMAKE_SOURCE_DIR}/src/native + ${CMAKE_SOURCE_DIR}/src/quickgui + + ${CMAKE_BINARY_DIR}/src/core + ${CMAKE_BINARY_DIR}/src/native + ${CMAKE_BINARY_DIR}/src/quickgui +) + +INCLUDE_DIRECTORIES(SYSTEM + ${LIBZIP_INCLUDE_DIRS} + ${SPATIALINDEX_INCLUDE_DIR} + ${PROJ_INCLUDE_DIR} + ${GEOS_INCLUDE_DIR} + ${GDAL_INCLUDE_DIR} + ${EXPAT_INCLUDE_DIR} + ${SQLITE3_INCLUDE_DIR} + ${SPATIALITE_INCLUDE_DIR} + ${QCA_INCLUDE_DIR} + ${QTKEYCHAIN_INCLUDE_DIR} +) + +QT5_WRAP_CPP(QGIS_QUICK_APP_MOC_SRCS ${QGIS_QUICK_APP_MOC_HDRS}) +QT5_ADD_RESOURCES(QGIS_QUICK_APP_RESOURCES qml.qrc) +SET(QGIS_QUICK_APP_NAME qgis_quickapp) +ADD_EXECUTABLE(${QGIS_QUICK_APP_NAME} + ${QGIS_QUICK_APP_RESOURCES} + ${QGIS_QUICK_APP_QMLS} + ${QGIS_QUICK_APP_SRCS} + ${QGIS_QUICK_APP_MOC_SRCS} +) + +TARGET_LINK_LIBRARIES(${QGIS_QUICK_APP_NAME} Qt5::Gui Qt5::Qml Qt5::Quick Qt5::Xml Qt5::Widgets qgis_core qgis_quick) +SET_TARGET_PROPERTIES(${QGIS_QUICK_APP_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + +TARGET_COMPILE_DEFINITIONS(${QGIS_QUICK_APP_NAME} PRIVATE "-DDQT_NO_FOREACH") +ADD_DEPENDENCIES(${QGIS_QUICK_APP_NAME} + qgis_quick_plugin + ogrprovider + gdalprovider + spatialiteprovider + virtuallayerprovider) + +INSTALL(TARGETS ${QGIS_QUICK_APP_NAME} + RUNTIME DESTINATION ${QGIS_BIN_DIR} + LIBRARY DESTINATION ${QGIS_LIB_DIR} + ARCHIVE DESTINATION ${QGIS_LIB_DIR} + FRAMEWORK DESTINATION ${QGIS_FW_SUBDIR} + PUBLIC_HEADER DESTINATION ${QGIS_INCLUDE_DIR}) + diff --git a/tests/src/quickgui/app/FeaturePanel.qml b/tests/src/quickgui/app/FeaturePanel.qml new file mode 100644 index 000000000000..9e854319a64c --- /dev/null +++ b/tests/src/quickgui/app/FeaturePanel.qml @@ -0,0 +1,79 @@ +/*************************************************************************** + FeaturePanel.qml + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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. * + * * + ***************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QgisQuick 0.1 as QgsQuick + +Drawer { + + property var mapSettings + property var project + + property alias state: featureForm.state + property alias layer: featureModel.layer + property alias feature: featureModel.feature + property alias currentFeatureModel: featureModel + + id: featurePanel + visible: false + modal: true + interactive: true + dragMargin: 0 // prevents opening the drawer by dragging. + + background: Rectangle { + color: "white" + opacity: 0.5 + } + + function show_panel(layer, feature, state) { + if (QgsQuick.Utils.hasValidGeometry(layer, feature)) { + // layer needs to be set before the feature otherwise the panel ends up empty on layer change + featurePanel.layer = layer + featurePanel.feature = feature + featurePanel.state = state + + // visible needs to be after setting correct layer&feature, + // so currentFeatureModel is already up to date (required for feature highlight) + featurePanel.visible = true + } else { + QgsQuick.Utils.logMessage("The feature " + layer.name + " has a wrong geometry." , "YDNPA Surveys") + } + } + + QgsQuick.FeatureForm { + id: featureForm + + // using anchors here is not working well as + width: featurePanel.width + height: featurePanel.height + + model: QgsQuick.AttributeFormModel { + featureModel: QgsQuick.FeatureModel { + id: featureModel + } + } + + project: featurePanel.project + + toolbarVisible: true + + onSaved: { + featurePanel.visible = false + } + onCancelled: featurePanel.visible = false + } + +} diff --git a/tests/src/quickgui/app/main.cpp b/tests/src/quickgui/app/main.cpp new file mode 100644 index 000000000000..ae7693f4c717 --- /dev/null +++ b/tests/src/quickgui/app/main.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + main.cpp + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 +#include +#include +#include +#include +#include + +#include "qgis.h" +#include "qgsapplication.h" +#include "qgsproject.h" +#include "qgslayertree.h" +#include "qgsmessagelog.h" +#include "qgsquickutils.h" + + +int main( int argc, char *argv[] ) +{ + // 1) Initialize QGIS + QgsApplication app( argc, argv, true ); + + // Set up the QSettings environment must be done after qapp is created + QCoreApplication::setOrganizationName( "QGIS" ); + QCoreApplication::setOrganizationDomain( "qgis.org" ); + QCoreApplication::setApplicationName( "QgsQuick Test App" ); + QCoreApplication::setApplicationVersion( Qgis::QGIS_VERSION ); + + QCommandLineParser parser; + parser.addVersionOption(); + parser.process( app ); + + QgsApplication::init(); + QgsApplication::initQgis(); + + // 2) Load QGIS Project + QString dataDir( TEST_DATA_DIR ); // defined in CMakeLists.txt + QString projectFile = dataDir + "/quickapp_project.qgs"; + qDebug() << "project file: " << projectFile; + QgsProject project; + bool res = project.read( projectFile ); + Q_ASSERT( res ); + + QQmlEngine engine; + engine.addImportPath( QgsApplication::qmlImportPath() ); + engine.rootContext()->setContextProperty( "__project", &project ); + engine.rootContext()->setContextProperty( "__layers", QVariant::fromValue( project.layerTreeRoot()->layerOrder() ) ); + + // Set simulated position for desktop builds + bool use_simulated_position = true; + engine.rootContext()->setContextProperty( "__use_simulated_position", use_simulated_position ); + + QQmlComponent component( &engine, QUrl( QStringLiteral( "qrc:/main.qml" ) ) ); + QObject *object = component.create(); + + if ( !component.errors().isEmpty() ) + { + qDebug( "%s", QgsApplication::showSettings().toLocal8Bit().data() ); + + qDebug() << "****************************************"; + qDebug() << "***** QML errors: *****"; + qDebug() << "****************************************"; + for ( const QQmlError &error : component.errors() ) + { + qDebug() << " " << error; + } + qDebug() << "****************************************"; + qDebug() << "****************************************"; + } + + if ( object == 0 ) + { + qDebug() << "FATAL ERROR: unable to create main.qml"; + return EXIT_FAILURE; + } + + // Add some data for debugging if needed + QgsApplication::messageLog()->logMessage( QgsQuickUtils::instance()->dumpScreenInfo() ); + QgsApplication::messageLog()->logMessage( "data directory: " + dataDir ); + QgsApplication::messageLog()->logMessage( "All up and running" ); + + return app.exec(); +} + diff --git a/tests/src/quickgui/app/main.qml b/tests/src/quickgui/app/main.qml new file mode 100644 index 000000000000..2dea4e2b23c4 --- /dev/null +++ b/tests/src/quickgui/app/main.qml @@ -0,0 +1,134 @@ +/*************************************************************************** + main.qml + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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. * + * * + ***************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QgisQuick 0.1 as QgsQuick +import "." + +ApplicationWindow { + id: window + visible: true + visibility: "Maximized" + title: qsTr("QGIS Quick Test App") + + // Some info + Button { + id: logbutton + text: "Log" + onClicked: logPanel.visible = true + z: 1 + } + + Label { + text: positionMarker.gpsPositionLabel + z: 1 + x: logbutton.width + 10 + } + + QgsQuick.MapCanvas { + id: mapCanvas + + height: parent.height + width: parent.width + + mapSettings.project: __project + mapSettings.layers: __layers + + QgsQuick.IdentifyKit { + id: identifyKit + mapSettings: mapCanvas.mapSettings + } + + onClicked: { + + var screenPoint = Qt.point( mouse.x, mouse.y ); + var res = identifyKit.identifyOne(screenPoint); + if (res.valid) + { + featurePanel.show_panel( + res.layer, + res.feature, + "Edit" ) + } + } + } + + Item { + anchors.fill: mapCanvas + transform: QgsQuick.MapTransform { + mapSettings: mapCanvas.mapSettings + } + + QgsQuick.FeatureHighlight { + color: "red" + width: 20 + model: featurePanel.visible ? featurePanel.currentFeatureModel : null + mapSettings: mapCanvas.mapSettings + } + + z: 1 // make sure items from here are on top of the Z-order + } + + + Drawer { + id: logPanel + visible: false + modal: true + interactive: true + dragMargin: 0 // prevents opening the drawer by dragging. + height: window.height + width: QgsQuick.Utils.dp * 700 + edge: Qt.RightEdge + z: 2 // make sure items from here are on top of the Z-order + + background: Rectangle { + color: "white" + } + + QgsQuick.MessageLog { + id: messageLog + width: parent.width + height: parent.height + model: QgsQuick.MessageLogModel {} + visible: true + } + } + + QgsQuick.PositionMarker { + id: positionMarker + mapSettings: mapCanvas.mapSettings + simulatePositionLongLatRad: __use_simulated_position ? [-97.36, 36.93, 2] : undefined + } + + QgsQuick.ScaleBar { + id: scaleBar + y: window.height - height + height: 50 + mapSettings: mapCanvas.mapSettings + preferredWidth: 115 * QgsQuick.Utils.dp + z: 1 + } + + FeaturePanel { + id: featurePanel + height: window.height + width: QgsQuick.Utils.dp * 700 + edge: Qt.RightEdge + mapSettings: mapCanvas.mapSettings + project: __project + } + +} diff --git a/tests/src/quickgui/app/qml.qrc b/tests/src/quickgui/app/qml.qrc new file mode 100644 index 000000000000..94dd1b25a29f --- /dev/null +++ b/tests/src/quickgui/app/qml.qrc @@ -0,0 +1,6 @@ + + + main.qml + FeaturePanel.qml + + diff --git a/tests/src/quickgui/app/qtquickcontrols2.conf b/tests/src/quickgui/app/qtquickcontrols2.conf new file mode 100644 index 000000000000..1764b16fb484 --- /dev/null +++ b/tests/src/quickgui/app/qtquickcontrols2.conf @@ -0,0 +1,15 @@ +; This file can be edited to change the style of the application +; See Styling Qt Quick Controls 2 in the documentation for details: +; http://doc.qt.io/qt-5/qtquickcontrols2-styles.html + +[Controls] +Style=Default + +[Universal] +Theme=Light +;Accent=Steel + +[Material] +Theme=Light +;Accent=BlueGrey +;Primary=BlueGray diff --git a/tests/src/quickgui/testqgsquickutils.cpp b/tests/src/quickgui/testqgsquickutils.cpp new file mode 100644 index 000000000000..d6abc854a72a --- /dev/null +++ b/tests/src/quickgui/testqgsquickutils.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** + testqgsquickutils.cpp + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 +#include +#include +#include +#include + +#include "qgsapplication.h" +#include "qgscoordinatereferencesystem.h" +#include "qgscoordinatetransformcontext.h" +#include "qgspoint.h" +#include "qgspointxy.h" +#include "qgstest.h" + +#include "qgsquickutils.h" + +class TestQgsQuickUtils: public QObject +{ + Q_OBJECT + private slots: + void init() {} // will be called before each testfunction is executed. + void cleanup() {} // will be called after every testfunction. + + void crs_and_geometry(); + void formatting(); +}; + +void TestQgsQuickUtils::crs_and_geometry() +{ + QgsCoordinateReferenceSystem crs3857 = QgsQuickUtils::instance()->coordinateReferenceSystemFromEpsgId( 3857 ); + QVERIFY( crs3857.authid() == "EPSG:3857" ); + + QgsCoordinateReferenceSystem crsGPS = QgsQuickUtils::instance()->coordinateReferenceSystemFromEpsgId( 4326 ); + QVERIFY( crsGPS.authid() == "EPSG:4326" ); + + QgsPointXY pointXY = QgsQuickUtils::instance()->pointXYFactory( 49.9, 16.3 ); + QVERIFY( pointXY.x() == 49.9 ); + QVERIFY( pointXY.y() == 16.3 ); + + QgsPoint point = QgsQuickUtils::instance()->pointFactory( 1.0, -1.0 ); + QVERIFY( point.x() == 1.0 ); + QVERIFY( point.y() == -1.0 ); + + QgsPointXY transformedPoint = QgsQuickUtils::instance()->transformPoint( crsGPS, + crs3857, + QgsCoordinateTransformContext(), + pointXY ); + QVERIFY( fabs( transformedPoint.x() - 5554843 ) < 1.0 ); + QVERIFY( fabs( transformedPoint.y() - 1839491 ) < 1.0 ); + + QgsQuickMapSettings ms; + ms.setDestinationCrs( crsGPS ); + ms.setExtent( QgsRectangle( 49, 16, 50, 17 ) ); + ms.setOutputSize( QSize( 1000, 500 ) ); + double sutm = QgsQuickUtils::instance()->screenUnitsToMeters( &ms, 1 ); + QVERIFY( fabs( sutm - 213 ) < 1.0 ); +} + +void TestQgsQuickUtils::formatting() +{ + QgsPoint point( -2.234521, 34.4444421 ); + QString point2str = QgsQuickUtils::instance()->qgsPointToString( point, 3 ); + QVERIFY( point2str == "-2.235, 34.444" ); + + point2str = QgsQuickUtils::instance()->qgsPointToString( point, 2 ); + QVERIFY( point2str == "-2.23, 34.44" ); + + point2str = QgsQuickUtils::instance()->qgsPointToString( point, 1 ); + QVERIFY( point2str == "-2.2, 34.4" ); + + point2str = QgsQuickUtils::instance()->qgsPointToString( point, 0 ); + QVERIFY( point2str == "-2, 34" ); + + QString dist2str = QgsQuickUtils::instance()->distanceToString( 1222.234, 2 ); + QVERIFY( dist2str == "1.22 km" ); + + dist2str = QgsQuickUtils::instance()->distanceToString( 1222.234, 1 ); + QVERIFY( dist2str == "1.2 km" ); + + dist2str = QgsQuickUtils::instance()->distanceToString( 1222.234, 0 ); + QVERIFY( dist2str == "1 km" ); + + dist2str = QgsQuickUtils::instance()->distanceToString( 700.22, 1 ); + QVERIFY( dist2str == "700.2 m" ); + + dist2str = QgsQuickUtils::instance()->distanceToString( 0.22, 0 ); + QVERIFY( dist2str == "220 mm" ); +} + +QGSTEST_MAIN( TestQgsQuickUtils ) +#include "testqgsquickutils.moc" diff --git a/tests/testdata/quickapp_project.qgs b/tests/testdata/quickapp_project.qgs new file mode 100644 index 000000000000..5cc9aa6a5bc7 --- /dev/null +++ b/tests/testdata/quickapp_project.qgs @@ -0,0 +1,1018 @@ + + + + + + + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + points20151123133104693 + polys20151123133114244 + lines20151123133101198 + + + + + + + + + + + + degrees + + -119.35580667290255974 + 25.42676323270561767 + -82.46631384217860727 + 48.75598131139545188 + + 0 + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + 0 + + + + + + + + + + + + + + + + + + -117.62319839219053108 + 23.20820580488508966 + -82.32264950769274492 + 46.18290982947509349 + + lines20151123133101198 + ./lines.shp + + + + Roads + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + 0 + 0 + + + + + true + + + + + + ogr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../src/quickgui/app/qgis-data + + 0 + ../src/quickgui/app/qgis-data + + 0 + generatedlayout + + + + + + + + + COALESCE( "Name", '<NULL>' ) + + + + + -118.88888888888877204 + 22.80020703933767834 + -83.33333333333315807 + 46.87198067632875365 + + points20151123133104693 + ./points.shp + + + + Planes + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + ogr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../src/quickgui/app/qgis-data + + 0 + ../src/quickgui/app/qgis-data + + 0 + generatedlayout + + + + + + + + + COALESCE( "Class", '<NULL>' ) + + + + + -118.92286230599032137 + 24.50786971868489061 + -83.79001199101509201 + 46.72617265077044379 + + polys20151123133114244 + ./polys.shp + + + + Land + + + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + WGS84 + true + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + ogr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../src/quickgui/app/qgis-data + + 0 + ../src/quickgui/app/qgis-data + + 0 + generatedlayout + + + + + + + + + COALESCE( "Name", '<NULL>' ) + + + + + + + + + + 8 + + conditions unknown + false + + false + + + + false + + + + + + None + + + + + + + + + + + + + MU + 2 + true + + false + + + + + + + + + + 255 + + + + true + + false + + + + + EPSG:4326 + 1 + 3452 + +proj=longlat +datum=WGS84 +no_defs + + + NONE + + + + + false + + + + m2 + meters + + + to vertex and segment + 1 + + lines20151123133101198 + points20151123133104693 + polys20151123133114244 + + 20 + + 20.000000 + 20.000000 + 20.000000 + + + to_vertex_and_segment + to_vertex_and_segment + to_vertex_and_segment + + current_layer + + + 1 + 1 + 1 + + + enabled + enabled + enabled + + + + 90 + + 255 + 255 + 255 + 255 + 255 + 0 + 255 + + + + + + + + From 4d1c12a91b4f84519b98a4badfb34fecfa76d738 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Thu, 1 Mar 2018 15:49:35 +0100 Subject: [PATCH 02/31] fix build after rebase: QgsMessageLog changes --- src/quickgui/qgsquickfeaturemodel.cpp | 13 +++++++------ src/quickgui/qgsquickmessagelogmodel.cpp | 6 ++++-- src/quickgui/qgsquickmessagelogmodel.h | 7 ++++--- src/quickgui/qgsquickpositionkit.cpp | 3 ++- src/quickgui/qgsquickutils.cpp | 8 ++++---- src/quickgui/qgsquickutils.h | 3 ++- tests/src/quickgui/CMakeLists.txt | 9 ++++----- tests/src/quickgui/app/CMakeLists.txt | 2 +- 8 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/quickgui/qgsquickfeaturemodel.cpp b/src/quickgui/qgsquickfeaturemodel.cpp index 878dfcbf81da..c904827fae68 100644 --- a/src/quickgui/qgsquickfeaturemodel.cpp +++ b/src/quickgui/qgsquickfeaturemodel.cpp @@ -15,6 +15,7 @@ #include +#include "qgis.h" #include "qgsmessagelog.h" #include "qgsvectorlayer.h" @@ -160,7 +161,7 @@ bool QgsQuickFeatureModel::save() QgsFeature feat = mFeature; if ( !mLayer->updateFeature( feat ) ) - QgsMessageLog::logMessage( tr( "Cannot update feature" ), "QgsQuick", QgsMessageLog::WARNING ); + QgsMessageLog::logMessage( tr( "Cannot update feature" ), "QgsQuick", Qgis::Warning ); rv = commit(); if ( rv ) @@ -169,7 +170,7 @@ bool QgsQuickFeatureModel::save() if ( mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( feat ) ) setFeature( feat ); else - QgsMessageLog::logMessage( tr( "Feature %1 could not be fetched after commit" ).arg( mFeature.id() ), "QgsQuick", QgsMessageLog::WARNING ); + QgsMessageLog::logMessage( tr( "Feature %1 could not be fetched after commit" ).arg( mFeature.id() ), "QgsQuick", Qgis::Warning ); } return rv; } @@ -187,7 +188,7 @@ bool QgsQuickFeatureModel::deleteFeature() } if ( !mLayer->deleteFeature( mFeature.id() ) ) - QgsMessageLog::logMessage( tr( "Cannot delete feature" ), "QgsQuick", QgsMessageLog::WARNING ); + QgsMessageLog::logMessage( tr( "Cannot delete feature" ), "QgsQuick", Qgis::Warning ); rv = commit(); return rv; @@ -247,7 +248,7 @@ void QgsQuickFeatureModel::create() startEditing(); if ( !mLayer->addFeature( mFeature ) ) { - QgsMessageLog::logMessage( tr( "Feature could not be added" ), "QgsQuick", QgsMessageLog::CRITICAL ); + QgsMessageLog::logMessage( tr( "Feature could not be added" ), "QgsQuick", Qgis::Critical ); } commit(); } @@ -256,7 +257,7 @@ bool QgsQuickFeatureModel::commit() { if ( !mLayer->commitChanges() ) { - QgsMessageLog::logMessage( tr( "Could not save changes. Rolling back." ), "QgsQuick", QgsMessageLog::CRITICAL ); + QgsMessageLog::logMessage( tr( "Could not save changes. Rolling back." ), "QgsQuick", Qgis::Critical ); mLayer->rollBack(); return false; } @@ -274,7 +275,7 @@ bool QgsQuickFeatureModel::startEditing() if ( !mLayer->startEditing() ) { - QgsMessageLog::logMessage( tr( "Cannot start editing" ), "QgsQuick", QgsMessageLog::WARNING ); + QgsMessageLog::logMessage( tr( "Cannot start editing" ), "QgsQuick", Qgis::Warning ); return false; } else diff --git a/src/quickgui/qgsquickmessagelogmodel.cpp b/src/quickgui/qgsquickmessagelogmodel.cpp index 02a4826576ba..015d7909c5dc 100644 --- a/src/quickgui/qgsquickmessagelogmodel.cpp +++ b/src/quickgui/qgsquickmessagelogmodel.cpp @@ -15,6 +15,8 @@ #include +#include "qgis.h" +#include "qgsmessagelog.h" #include "qgsapplication.h" #include "qgsquickmessagelogmodel.h" @@ -23,7 +25,7 @@ QgsQuickMessageLogModel::QgsQuickMessageLogModel( QObject *parent ) : QAbstractListModel( parent ) , mMessageLog( QgsApplication::messageLog() ) { - connect( mMessageLog, static_cast( &QgsMessageLog::messageReceived ), this, &QgsQuickMessageLogModel::onMessageReceived ); + connect( mMessageLog, static_cast( &QgsMessageLog::messageReceived ), this, &QgsQuickMessageLogModel::onMessageReceived ); } QHash QgsQuickMessageLogModel::roleNames() const @@ -57,7 +59,7 @@ QVariant QgsQuickMessageLogModel::data( const QModelIndex &index, int role ) con return QVariant(); } -void QgsQuickMessageLogModel::onMessageReceived( const QString &message, const QString &tag, QgsMessageLog::MessageLevel level ) +void QgsQuickMessageLogModel::onMessageReceived( const QString &message, const QString &tag, Qgis::MessageLevel level ) { beginInsertRows( QModelIndex(), 0, 0 ); mMessages.prepend( LogMessage( tag, message, level ) ); diff --git a/src/quickgui/qgsquickmessagelogmodel.h b/src/quickgui/qgsquickmessagelogmodel.h index 0629638b231f..dfceab9a89cd 100644 --- a/src/quickgui/qgsquickmessagelogmodel.h +++ b/src/quickgui/qgsquickmessagelogmodel.h @@ -18,6 +18,7 @@ #include +#include "qgis.h" #include "qgsmessagelog.h" #include "qgis_quick.h" @@ -42,7 +43,7 @@ class QUICK_EXPORT QgsQuickMessageLogModel : public QAbstractListModel LogMessage() {} - LogMessage( const QString &tag, const QString &message, QgsMessageLog::MessageLevel level ) + LogMessage( const QString &tag, const QString &message, Qgis::MessageLevel level ) { this->tag = tag; this->message = message; @@ -51,7 +52,7 @@ class QUICK_EXPORT QgsQuickMessageLogModel : public QAbstractListModel QString tag; QString message; - QgsMessageLog::MessageLevel level; + Qgis::MessageLevel level; }; enum Roles @@ -70,7 +71,7 @@ class QUICK_EXPORT QgsQuickMessageLogModel : public QAbstractListModel QVariant data( const QModelIndex &index, int role ) const override; private slots: - void onMessageReceived( const QString &message, const QString &tag, QgsMessageLog::MessageLevel level ); + void onMessageReceived( const QString &message, const QString &tag, Qgis::MessageLevel level ); private: QgsMessageLog *mMessageLog; diff --git a/src/quickgui/qgsquickpositionkit.cpp b/src/quickgui/qgsquickpositionkit.cpp index cbb88d2270d4..80663b46e747 100644 --- a/src/quickgui/qgsquickpositionkit.cpp +++ b/src/quickgui/qgsquickpositionkit.cpp @@ -13,6 +13,7 @@ * * ***************************************************************************/ +#include "qgis.h" #include "qgsmessagelog.h" #include "qgsquickpositionkit.h" @@ -39,7 +40,7 @@ QGeoPositionInfoSource *QgsQuickPositionKit::gpsSource() QgsMessageLog::logMessage( tr( "Unable to create default GPS Position Source" ) + "(" + QString::number( ( long )source->error() ) + ")" , "QgsQuick" - , QgsMessageLog::WARNING ); + , Qgis::Warning ); delete source; return 0; } diff --git a/src/quickgui/qgsquickutils.cpp b/src/quickgui/qgsquickutils.cpp index 826aba7c18c3..806326f3961e 100644 --- a/src/quickgui/qgsquickutils.cpp +++ b/src/quickgui/qgsquickutils.cpp @@ -21,10 +21,10 @@ #include #include +#include "qgis.h" #include "qgscoordinatereferencesystem.h" #include "qgscoordinatetransform.h" #include "qgsdistancearea.h" -#include "qgsmessagelog.h" #include "qgsvectorlayer.h" #include "qgsquickmapsettings.h" @@ -142,7 +142,7 @@ void QgsQuickUtils::copyFile( QString sourcePath, QString targetPath ) if ( !QDir::root().mkpath( targetPath ) ) { - QgsApplication::messageLog()->logMessage( tr( "Could not create folder %1" ).arg( targetPath ), "QgsQuick", QgsMessageLog::CRITICAL ); + QgsApplication::messageLog()->logMessage( tr( "Could not create folder %1" ).arg( targetPath ), "QgsQuick", Qgis::Critical ); return; } @@ -154,7 +154,7 @@ void QgsQuickUtils::copyFile( QString sourcePath, QString targetPath ) qDebug() << "Couldn't rename file! Trying to copy instead"; if ( !QFile( sourcePath ).copy( dir.absoluteFilePath( filename ) ) ) { - QgsApplication::messageLog()->logMessage( tr( "File %1 could not be copied to folder %2.", "QgsQuick", QgsMessageLog::CRITICAL ).arg( sourcePath, targetPath ) ); + QgsApplication::messageLog()->logMessage( tr( "File %1 could not be copied to folder %2.", "QgsQuick", Qgis::Critical ).arg( sourcePath, targetPath ) ); return; } } @@ -172,7 +172,7 @@ QString QgsQuickUtils::getFileName( QString path ) return filename; } -void QgsQuickUtils::logMessage( const QString &message, const QString &tag, QgsMessageLog::MessageLevel level ) +void QgsQuickUtils::logMessage( const QString &message, const QString &tag, Qgis::MessageLevel level ) { QgsMessageLog::logMessage( message, tag, level ); } diff --git a/src/quickgui/qgsquickutils.h b/src/quickgui/qgsquickutils.h index 0e65d56349ba..52526e4458c8 100644 --- a/src/quickgui/qgsquickutils.h +++ b/src/quickgui/qgsquickutils.h @@ -21,6 +21,7 @@ #include #include +#include "qgis.h" #include "qgsfeature.h" #include "qgsmessagelog.h" #include "qgspoint.h" @@ -105,7 +106,7 @@ class QUICK_EXPORT QgsQuickUtils: public QObject Q_INVOKABLE QString getFileName( QString path ); Q_INVOKABLE void logMessage( const QString &message, const QString &tag = QString( "QgsQuick" ), - QgsMessageLog::MessageLevel level = QgsMessageLog::WARNING ); + Qgis::MessageLevel level = Qgis::Warning ); // Themes diff --git a/tests/src/quickgui/CMakeLists.txt b/tests/src/quickgui/CMakeLists.txt index 9f3c9340318a..af99c766567b 100644 --- a/tests/src/quickgui/CMakeLists.txt +++ b/tests/src/quickgui/CMakeLists.txt @@ -61,15 +61,14 @@ MACRO (ADD_QGIS_TEST testname testsrc) ADD_EXECUTABLE(qgis_${testname} ${qgis_${testname}_SRCS}) SET_TARGET_PROPERTIES(qgis_${testname} PROPERTIES AUTOMOC TRUE) TARGET_LINK_LIBRARIES(qgis_${testname} - ${QT_QTXML_LIBRARY} - ${QT_QTCORE_LIBRARY} - ${QT_QTSVG_LIBRARY} - ${QT_QTTEST_LIBRARY} + ${Qt5Xml_LIBRARIES} + ${Qt5Core_LIBRARIES} + ${Qt5Svg_LIBRARIES} + ${Qt5Test_LIBRARIES} Qt5::Gui Qt5::Qml Qt5::Quick Qt5::Xml - Qt5::Widgets qgis_core qgis_quick) diff --git a/tests/src/quickgui/app/CMakeLists.txt b/tests/src/quickgui/app/CMakeLists.txt index a83d98c300d7..5c573425a1db 100644 --- a/tests/src/quickgui/app/CMakeLists.txt +++ b/tests/src/quickgui/app/CMakeLists.txt @@ -62,7 +62,7 @@ ADD_EXECUTABLE(${QGIS_QUICK_APP_NAME} ${QGIS_QUICK_APP_MOC_SRCS} ) -TARGET_LINK_LIBRARIES(${QGIS_QUICK_APP_NAME} Qt5::Gui Qt5::Qml Qt5::Quick Qt5::Xml Qt5::Widgets qgis_core qgis_quick) +TARGET_LINK_LIBRARIES(${QGIS_QUICK_APP_NAME} Qt5::Gui Qt5::Qml Qt5::Quick Qt5::Xml qgis_core qgis_quick) SET_TARGET_PROPERTIES(${QGIS_QUICK_APP_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) TARGET_COMPILE_DEFINITIONS(${QGIS_QUICK_APP_NAME} PRIVATE "-DDQT_NO_FOREACH") From b099921f1a95027d39bf886a2313a371515bd585 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 2 Mar 2018 13:42:11 +0100 Subject: [PATCH 03/31] spell check, add docs --- doc/qgsquick.dox | 2 +- src/quickgui/plugin/qgsquickfeatureform.qml | 6 +- src/quickgui/plugin/qgsquickplugin.h | 12 +++ src/quickgui/qgsquickattributeformmodel.h | 24 +++++ .../qgsquickattributeformmodelbase.cpp | 4 +- src/quickgui/qgsquickattributeformmodelbase.h | 2 +- src/quickgui/qgsquickcoordinatetransformer.h | 35 +++++- src/quickgui/qgsquickfeature.h | 11 ++ src/quickgui/qgsquickfeaturehighlight.h | 16 ++- src/quickgui/qgsquickfeaturemodel.h | 27 ++++- src/quickgui/qgsquickhighlightsgnode.h | 1 + src/quickgui/qgsquickidentifykit.h | 12 ++- src/quickgui/qgsquickmapcanvasmap.cpp | 31 ++---- src/quickgui/qgsquickmapcanvasmap.h | 58 +++++----- src/quickgui/qgsquickmapsettings.h | 101 ++++++++++++++++-- src/quickgui/qgsquickmaptransform.h | 13 +++ src/quickgui/qgsquickmessagelogmodel.h | 1 + src/quickgui/qgsquickpositionkit.cpp | 25 +++++ src/quickgui/qgsquickpositionkit.h | 36 +++++-- src/quickgui/qgsquickscalebarkit.h | 27 ++++- .../qgsquicksimulatedpositionsource.h | 2 - src/quickgui/qgsquicksubmodel.h | 29 +++++ src/quickgui/qgsquickutils.cpp | 11 +- src/quickgui/qgsquickutils.h | 19 ++-- tests/src/quickgui/app/FeaturePanel.qml | 2 +- 25 files changed, 411 insertions(+), 96 deletions(-) diff --git a/doc/qgsquick.dox b/doc/qgsquick.dox index e424d46df3a1..1d6d44d8f07d 100644 --- a/doc/qgsquick.dox +++ b/doc/qgsquick.dox @@ -1,4 +1,4 @@ -/*! \page qgsquick QGIS Quick Documention +/*! \page qgsquick QGIS Quick Documentation \tableofcontents diff --git a/src/quickgui/plugin/qgsquickfeatureform.qml b/src/quickgui/plugin/qgsquickfeatureform.qml index e685325a55dd..4d793e0c97bc 100644 --- a/src/quickgui/plugin/qgsquickfeatureform.qml +++ b/src/quickgui/plugin/qgsquickfeatureform.qml @@ -29,7 +29,7 @@ import QgisQuick 0.1 as QgsQuick Item { signal saved - signal cancelled + signal canceled signal aboutToSave property QgsQuick.AttributeFormModel model @@ -443,7 +443,7 @@ Item { onClicked: { Qt.inputMethod.hide() - cancelled() + canceled() } } } @@ -462,7 +462,7 @@ Item { model.featureModel.deleteFeature() visible = false - cancelled() + canceled() } onRejected: { visible = false diff --git a/src/quickgui/plugin/qgsquickplugin.h b/src/quickgui/plugin/qgsquickplugin.h index edab34634be7..0c78bc408b27 100644 --- a/src/quickgui/plugin/qgsquickplugin.h +++ b/src/quickgui/plugin/qgsquickplugin.h @@ -21,12 +21,24 @@ #include #include +/** + * \ingroup quick + * + * Qgis Qml Extension Plugin responsible for exposing C++ Qgis classes to QML + * + * \since QGIS 3.2 + */ class QgisQuickPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA( IID "org.qt-project.Qt.QQmlExtensionInterface" ) public: + + /** + * Registers the QGIS QML types in the given uri + * \param uri an identifier for the plugin generated by the QML engine + */ void registerTypes( const char *uri ); }; diff --git a/src/quickgui/qgsquickattributeformmodel.h b/src/quickgui/qgsquickattributeformmodel.h index c27809fdc34b..a86212a5b986 100644 --- a/src/quickgui/qgsquickattributeformmodel.h +++ b/src/quickgui/qgsquickattributeformmodel.h @@ -42,8 +42,12 @@ class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel { Q_OBJECT + //! Feature model with attributes Q_PROPERTY( QgsQuickFeatureModel *featureModel READ featureModel WRITE setFeatureModel NOTIFY featureModelChanged ) + //! Whether use tabs layout Q_PROPERTY( bool hasTabs READ hasTabs WRITE setHasTabs NOTIFY hasTabsChanged ) + + //! Returns true if all constraints defined on fields are satisfied with the current attribute values Q_PROPERTY( bool constraintsValid READ constraintsValid NOTIFY constraintsValidChanged ) public: @@ -67,24 +71,44 @@ class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel Q_ENUM( FeatureRoles ) + //! create new attribute form model QgsQuickAttributeFormModel( QObject *parent = nullptr ); + //! Return whether model has tabs layout bool hasTabs() const; + + //! Set tabs layout void setHasTabs( bool hasTabs ); + //! Return feature model associated QgsQuickFeatureModel *featureModel() const; + + //! Set feature model void setFeatureModel( QgsQuickFeatureModel *featureModel ); + //! Whether all constraints are valid for feature model bool constraintsValid() const; + //! Update QgsFeature based on changes Q_INVOKABLE void save(); + + //! Create new QgsFeature Q_INVOKABLE void create(); + + //! Return attribute value with name Q_INVOKABLE QVariant attribute( const QString &name ); signals: + //! feature model changed void featureModelChanged(); + + //! has tabs changed void hasTabsChanged(); + + //! feature changed void featureChanged(); + + //! constraints valid changed void constraintsValidChanged(); protected: diff --git a/src/quickgui/qgsquickattributeformmodelbase.cpp b/src/quickgui/qgsquickattributeformmodelbase.cpp index 6af4ae565cbe..b8d1c41677b4 100644 --- a/src/quickgui/qgsquickattributeformmodelbase.cpp +++ b/src/quickgui/qgsquickattributeformmodelbase.cpp @@ -136,7 +136,7 @@ void QgsQuickAttributeFormModelBase::onLayerChanged() } else { - root = generateRootContainer(); + root = generateRootContainer(); //#spellok mTemporaryContainer = root; } @@ -187,7 +187,7 @@ void QgsQuickAttributeFormModelBase::onFeatureChanged() updateVisibility(); } -QgsAttributeEditorContainer *QgsQuickAttributeFormModelBase::generateRootContainer() const +QgsAttributeEditorContainer *QgsQuickAttributeFormModelBase::generateRootContainer() const //#spellok { QgsAttributeEditorContainer *root = new QgsAttributeEditorContainer( QString(), nullptr ); QgsFields fields = mLayer->fields(); diff --git a/src/quickgui/qgsquickattributeformmodelbase.h b/src/quickgui/qgsquickattributeformmodelbase.h index 3ec80f3e3731..b94d6cac856e 100644 --- a/src/quickgui/qgsquickattributeformmodelbase.h +++ b/src/quickgui/qgsquickattributeformmodelbase.h @@ -90,7 +90,7 @@ class QgsQuickAttributeFormModelBase : public QStandardItemModel * Generates a root container for autogenerated layouts, so we can just use the same * form logic to deal with them. */ - QgsAttributeEditorContainer *generateRootContainer() const; + QgsAttributeEditorContainer *generateRootContainer() const; //#spellok QgsAttributeEditorContainer *invisibleRootContainer() const; diff --git a/src/quickgui/qgsquickcoordinatetransformer.h b/src/quickgui/qgsquickcoordinatetransformer.h index c9235bf62fcc..79192b7e5c0d 100644 --- a/src/quickgui/qgsquickcoordinatetransformer.h +++ b/src/quickgui/qgsquickcoordinatetransformer.h @@ -35,36 +35,65 @@ class QUICK_EXPORT QgsQuickCoordinateTransformer : public QObject { Q_OBJECT + //! projected (destination) position Q_PROPERTY( QgsPoint projectedPosition READ projectedPosition NOTIFY projectedPositionChanged ) + + //! Source position Q_PROPERTY( QgsPoint sourcePosition READ sourcePosition WRITE setSourcePosition NOTIFY sourcePositionChanged ) + + //! Destination CRS Q_PROPERTY( QgsCoordinateReferenceSystem destinationCrs READ destinationCrs WRITE setDestinationCrs NOTIFY destinationCrsChanged ) + + //! Source CRS, default 4326 Q_PROPERTY( QgsCoordinateReferenceSystem sourceCrs READ sourceCrs WRITE setSourceCrs NOTIFY sourceCrsChanged ) + + //! Map settings, for getting transformation context Q_PROPERTY( QgsQuickMapSettings *mapSettings MEMBER mMapSettings NOTIFY mapSettingsChanged ) public: + //! create new coordinate transformer explicit QgsQuickCoordinateTransformer( QObject *parent = 0 ); + + //! Return projected position (in destination CRS) QgsPoint projectedPosition() const; + //! Return source position (in source CRS) QgsPoint sourcePosition() const; + + //! Set source position (in source CRS) void setSourcePosition( QgsPoint sourcePosition ); + //! Return destination CRS QgsCoordinateReferenceSystem destinationCrs() const; + + //! Set destination CRS void setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ); + //! Return source CRS QgsCoordinateReferenceSystem sourceCrs() const; - void setSourceCrs( const QgsCoordinateReferenceSystem &sourceCrs ); - private: - void updatePosition(); + //! Set source CRS + void setSourceCrs( const QgsCoordinateReferenceSystem &sourceCrs ); signals: + //! projected position changed void projectedPositionChanged(); + + //! source position changed void sourcePositionChanged(); + + //! destination CRS changed void destinationCrsChanged(); + + //! source CRS changed void sourceCrsChanged(); + + //! map settings changed void mapSettingsChanged(); private: + void updatePosition(); + QgsPoint mProjectedPosition; QgsPoint mSourcePosition; QgsCoordinateTransform mCoordinateTransform; diff --git a/src/quickgui/qgsquickfeature.h b/src/quickgui/qgsquickfeature.h index 6ce4e3b3eafc..d27ea54dd709 100644 --- a/src/quickgui/qgsquickfeature.h +++ b/src/quickgui/qgsquickfeature.h @@ -36,17 +36,28 @@ class QUICK_EXPORT QgsQuickFeature { Q_GADGET + //! feature Q_PROPERTY( QgsVectorLayer *layer READ layer ) + //! layer to which \a feature belogs Q_PROPERTY( QgsFeature feature READ feature ) + //! whether is feature valid Q_PROPERTY( bool valid READ valid ) public: + //! create new feature QgsQuickFeature(); + + //! create new feature QgsQuickFeature( const QgsFeature &feature, QgsVectorLayer *layer ); + //! Return layer QgsVectorLayer *layer() const; + + //! Return feature QgsFeature feature() const; + + //! Return whether is feature valid bool valid() const; private: diff --git a/src/quickgui/qgsquickfeaturehighlight.h b/src/quickgui/qgsquickfeaturehighlight.h index ff66419cdd6e..8c3f8faa5eed 100644 --- a/src/quickgui/qgsquickfeaturehighlight.h +++ b/src/quickgui/qgsquickfeaturehighlight.h @@ -38,19 +38,33 @@ class QUICK_EXPORT QgsQuickFeatureHighlight : public QQuickItem { Q_OBJECT + //! map settings + Q_PROPERTY( QgsQuickMapSettings *mapSettings MEMBER mMapSettings NOTIFY mapSettingsChanged ) + //! feature model (for geometry) Q_PROPERTY( QgsQuickFeatureModel *model MEMBER mModel NOTIFY modelChanged ) + //! color of the highlighed geometry Q_PROPERTY( QColor color MEMBER mColor NOTIFY colorChanged ) + //! pen width of the highlight Q_PROPERTY( unsigned int width MEMBER mWidth NOTIFY widthChanged ) - Q_PROPERTY( QgsQuickMapSettings *mapSettings MEMBER mMapSettings NOTIFY mapSettingsChanged ) public: + //! create new feature highlight explicit QgsQuickFeatureHighlight( QQuickItem *parent = 0 ); signals: + //! model changed void modelChanged(); + + //! color changed void colorChanged(); + + //! map canvas changed void mapCanvasChanged(); + + //! width changed void widthChanged(); + + //! map settings changed void mapSettingsChanged(); private slots: diff --git a/src/quickgui/qgsquickfeaturemodel.h b/src/quickgui/qgsquickfeaturemodel.h index 5885cb827a0a..b868a2d26616 100644 --- a/src/quickgui/qgsquickfeaturemodel.h +++ b/src/quickgui/qgsquickfeaturemodel.h @@ -35,8 +35,11 @@ class QUICK_EXPORT QgsQuickFeatureModel : public QAbstractListModel { Q_OBJECT + //! feature Q_PROPERTY( QgsFeature feature READ feature WRITE setFeature NOTIFY featureChanged ) + //! layer to which \a feature belogs Q_PROPERTY( QgsVectorLayer *layer READ layer WRITE setLayer NOTIFY layerChanged ) + //! feature roles Q_ENUMS( FeatureRoles ) public: @@ -45,12 +48,16 @@ class QUICK_EXPORT QgsQuickFeatureModel : public QAbstractListModel AttributeName = Qt::UserRole + 1, //!< Attribute's display name (the original field name or a custom alias) AttributeValue, //!< Value of the feature's attribute Field, //!< Field definition (QgsField) - RememberAttribute + RememberAttribute //!< Remember attribute value for next feature }; + //! Create new feature model explicit QgsQuickFeatureModel( QObject *parent = 0 ); + + //! Create new feature model explicit QgsQuickFeatureModel( const QgsFeature &feat, QObject *parent = 0 ); + //! Set feature to feature model void setFeature( const QgsFeature &feature ); /** @@ -58,8 +65,10 @@ class QUICK_EXPORT QgsQuickFeatureModel : public QAbstractListModel */ QgsFeature feature() const; - + //! Set feature model layer void setLayer( QgsVectorLayer *layer ); + + //! Return feature model layer QgsVectorLayer *layer() const; @@ -86,21 +95,31 @@ class QUICK_EXPORT QgsQuickFeatureModel : public QAbstractListModel * Will reset the feature to the original values and dismiss any buffered edits. */ Q_INVOKABLE void reset(); + + //! Add mFeature to mLayer Q_INVOKABLE void create(); + /** + * Suppress layer's QgsEditFormConfig + * + * \sa QgsEditFormConfig::suppress + */ Q_INVOKABLE bool suppressFeatureForm() const; + //! Reset remembered attributes Q_INVOKABLE void resetAttributes(); + //! Get remembered attributes QVector rememberedAttributes() const; public slots: signals: + //! feature changed void featureChanged(); - void layerChanged(); - void warning( const QString &text ); + //! layer changed + void layerChanged(); private: bool commit(); diff --git a/src/quickgui/qgsquickhighlightsgnode.h b/src/quickgui/qgsquickhighlightsgnode.h index 62fc99bdd096..e950edacf2b0 100644 --- a/src/quickgui/qgsquickhighlightsgnode.h +++ b/src/quickgui/qgsquickhighlightsgnode.h @@ -36,6 +36,7 @@ class QUICK_NO_EXPORT QgsQuickHighlightSGNode : public QSGNode { public: + //! Create new QT Quick scene node based on geometry QgsQuickHighlightSGNode( const QVector &points, QgsWkbTypes::GeometryType type, const QColor &color, qreal width ); private: diff --git a/src/quickgui/qgsquickidentifykit.h b/src/quickgui/qgsquickidentifykit.h index d6f698fc0747..61a32b66d85c 100644 --- a/src/quickgui/qgsquickidentifykit.h +++ b/src/quickgui/qgsquickidentifykit.h @@ -34,7 +34,7 @@ class QgsVectorLayer; /** * \ingroup quick - * Convinient set of tools to get a list of QgsFeatures in a defined radius from a point. + * Convenient set of tools to get a list of QgsFeatures in a defined radius from a point. * Also possible to get a feature with the closest distance to the point or feature(s) from * specified QgsVectorLayer * @@ -45,6 +45,7 @@ class QgsVectorLayer; class QUICK_EXPORT QgsQuickIdentifyKit : public QObject { Q_OBJECT + //! map settings. Set directly when creating QML object Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) /** @@ -58,12 +59,18 @@ class QUICK_EXPORT QgsQuickIdentifyKit : public QObject Q_PROPERTY( long featuresLimit MEMBER mFeaturesLimit NOTIFY featuresLimitChanged ) public: + //! create new identify kit explicit QgsQuickIdentifyKit( QObject *parent = 0 ); + //! Getter for map settings QgsQuickMapSettings *mapSettings() const; + + //! Set map settings void setMapSettings( QgsQuickMapSettings *mapSettings ); + //! Return search radius for looking for features. double searchRadiusMm() const; + //! Set search radius void setSearchRadiusMm( double searchRadiusMm ); /** @@ -88,8 +95,11 @@ class QUICK_EXPORT QgsQuickIdentifyKit : public QObject signals: + //! map settings changed void mapSettingsChanged(); + //! search radius changed void searchRadiusMmChanged(); + //! features limit changed void featuresLimitChanged(); private: diff --git a/src/quickgui/qgsquickmapcanvasmap.cpp b/src/quickgui/qgsquickmapcanvasmap.cpp index d98c0552fb05..e5ac215dd386 100644 --- a/src/quickgui/qgsquickmapcanvasmap.cpp +++ b/src/quickgui/qgsquickmapcanvasmap.cpp @@ -62,11 +62,13 @@ QgsQuickMapCanvasMap::~QgsQuickMapCanvasMap() QgsQuickMapSettings *QgsQuickMapCanvasMap::mapSettings() const { + Q_ASSERT( mMapSettings ); return mMapSettings; } void QgsQuickMapCanvasMap::zoom( QPointF center, qreal scale ) { + Q_ASSERT( mMapSettings ); QgsRectangle extent = mMapSettings->extent(); QgsPoint oldCenter( extent.center() ); QgsPoint mousePos( mMapSettings->screenToCoordinate( center ) ); @@ -80,6 +82,7 @@ void QgsQuickMapCanvasMap::zoom( QPointF center, qreal scale ) void QgsQuickMapCanvasMap::pan( QPointF oldPos, QPointF newPos ) { + Q_ASSERT( mMapSettings ); QgsPoint start = mMapSettings->screenToCoordinate( oldPos.toPoint() ); QgsPoint end = mMapSettings->screenToCoordinate( newPos.toPoint() ); @@ -99,6 +102,8 @@ void QgsQuickMapCanvasMap::pan( QPointF oldPos, QPointF newPos ) void QgsQuickMapCanvasMap::refreshMap() { + Q_ASSERT( mMapSettings ); + stopRendering(); // if any... QgsMapSettings mapSettings = mMapSettings->mapSettings(); @@ -191,6 +196,7 @@ void QgsQuickMapCanvasMap::onWindowChanged( QQuickWindow *window ) void QgsQuickMapCanvasMap::onScreenChanged( QScreen *screen ) { + Q_ASSERT( mMapSettings ); if ( screen ) mMapSettings->setOutputDpi( screen->physicalDotsPerInch() ); } @@ -205,6 +211,7 @@ void QgsQuickMapCanvasMap::onExtentChanged() void QgsQuickMapCanvasMap::updateTransform() { + Q_ASSERT( mMapSettings ); QgsMapSettings currentMapSettings = mMapSettings->mapSettings(); QgsMapToPixel mtp = currentMapSettings.mapToPixel(); @@ -269,16 +276,6 @@ bool QgsQuickMapCanvasMap::isRendering() const return mJob; } -QgsRectangle QgsQuickMapCanvasMap::extent() const -{ - return mMapSettings->extent(); -} - -void QgsQuickMapCanvasMap::setExtent( const QgsRectangle &extent ) -{ - mMapSettings->setExtent( extent ); -} - QSGNode *QgsQuickMapCanvasMap::updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * ) { if ( mDirty ) @@ -317,19 +314,10 @@ QSGNode *QgsQuickMapCanvasMap::updatePaintNode( QSGNode *oldNode, QQuickItem::Up return node; } -QgsCoordinateReferenceSystem QgsQuickMapCanvasMap::destinationCrs() const -{ - return mMapSettings->destinationCrs(); -} - -void QgsQuickMapCanvasMap::setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ) -{ - mMapSettings->setDestinationCrs( destinationCrs ); -} - void QgsQuickMapCanvasMap::geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry ) { Q_UNUSED( oldGeometry ) + Q_ASSERT( mMapSettings ); // The Qt documentation advices to call the base method here. // However, this introduces instabilities and heavy performance impacts on Android. // It seems on desktop disabling it prevents us from downsizing the window... @@ -342,6 +330,7 @@ void QgsQuickMapCanvasMap::geometryChanged( const QRectF &newGeometry, const QRe void QgsQuickMapCanvasMap::onLayersChanged() { + Q_ASSERT( mMapSettings ); if ( mMapSettings->extent().isEmpty() ) zoomToFullExtent(); @@ -382,6 +371,7 @@ void QgsQuickMapCanvasMap::stopRendering() void QgsQuickMapCanvasMap::zoomToFullExtent() { + Q_ASSERT( mMapSettings ); QgsRectangle extent; Q_FOREACH ( QgsMapLayer *layer, mMapSettings->layers() ) { @@ -402,6 +392,7 @@ void QgsQuickMapCanvasMap::zoomToFullExtent() void QgsQuickMapCanvasMap::refresh() { + Q_ASSERT( mMapSettings ); if ( mMapSettings->outputSize().isNull() ) return; // the map image size has not been set yet diff --git a/src/quickgui/qgsquickmapcanvasmap.h b/src/quickgui/qgsquickmapcanvasmap.h index 1a5463f3c3ac..ea9a3ed6b271 100644 --- a/src/quickgui/qgsquickmapcanvasmap.h +++ b/src/quickgui/qgsquickmapcanvasmap.h @@ -36,6 +36,10 @@ class QgsLabelingResults; * according to the current map settings. Client code is expected to use * MapCanvas item rather than using this class directly. * + * QgsQuickMapCanvasMap instance internally creates QgsQuickMapSettings in + * constructor. The map settings for other QgsQuick components should + * be initialized from QgsQuickMapCanvasMap's mapSettings + * * \note QML Type: MapCanvasMap * * \sa QgsQuickMapCanvas @@ -46,9 +50,11 @@ class QUICK_EXPORT QgsQuickMapCanvasMap : public QQuickItem { Q_OBJECT - Q_PROPERTY( QgsCoordinateReferenceSystem destinationCrs READ destinationCrs WRITE setDestinationCrs NOTIFY destinationCrsChanged ) + //! map settings. The map settings should be source of map settings for all other components Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings ) + //! do not refresh canvas Q_PROPERTY( bool freeze READ freeze WRITE setFreeze NOTIFY freezeChanged ) + //! QgsMapRendererParallelJob is running Q_PROPERTY( bool isRendering READ isRendering NOTIFY isRenderingChanged ) /** @@ -57,71 +63,74 @@ class QUICK_EXPORT QgsQuickMapCanvasMap : public QQuickItem * Default is 250 [ms]. */ Q_PROPERTY( int mapUpdateInterval READ mapUpdateInterval WRITE setMapUpdateInterval NOTIFY mapUpdateIntervalChanged ) + //! allow increamental rendering - automatic refresh of map canvas while a rendering job is ongoing Q_PROPERTY( bool incrementalRendering READ incrementalRendering WRITE setIncrementalRendering NOTIFY incrementalRenderingChanged ) public: + //! create map canvas map QgsQuickMapCanvasMap( QQuickItem *parent = 0 ); ~QgsQuickMapCanvasMap(); - QgsPoint toMapCoordinates( QPoint canvasCoordinates ); - + //! Return map settings associated QgsQuickMapSettings *mapSettings() const; - QgsUnitTypes::DistanceUnit mapUnits() const; - void setMapUnits( const QgsUnitTypes::DistanceUnit &mapUnits ); - - QList layerSet() const; - void setLayerSet( const QList &layerSet ); - - bool hasCrsTransformEnabled() const; - void setCrsTransformEnabled( bool hasCrsTransformEnabled ); - - QgsCoordinateReferenceSystem destinationCrs() const; - void setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ); - - QgsRectangle extent() const; - void setExtent( const QgsRectangle &extent ); - + //! Return map canvas for Qt Quick scene graph changes virtual QSGNode *updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * ); + //! Return whether canvas refresh is frozen bool freeze() const; + + //! Freeze canvas refresh void setFreeze( bool freeze ); + //! Whether renderer job is running bool isRendering() const; + //! Return current map update interval for incremental rendering int mapUpdateInterval() const; + + //! Set map update interval for incremental rendering void setMapUpdateInterval( int mapUpdateInterval ); + //! Return whether incremental rendering is on bool incrementalRendering() const; + //! Set incremental rendering void setIncrementalRendering( bool incrementalRendering ); signals: + //! rendering starting void renderStarting(); + //! canvas refreshed void mapCanvasRefreshed(); - void extentChanged(); - - void destinationCrsChanged(); - + //! freeze changed void freezeChanged(); + //! is rendering changed void isRenderingChanged(); + //! map update interval for incremental rendering changed void mapUpdateIntervalChanged(); + //! incremental rendering changed void incrementalRenderingChanged(); protected: + //! geometry changed void geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry ); public slots: + //! stop rendering void stopRendering(); - void zoomToFullExtent(); - + //! Zoom the map by adjusting map settings extent void zoom( QPointF center, qreal scale ); + + //! Pan the map void pan( QPointF oldPos, QPointF newPos ); + + //! Refresh the map void refresh(); private slots: @@ -141,6 +150,7 @@ class QUICK_EXPORT QgsQuickMapCanvasMap : public QQuickItem void destroyJob( QgsMapRendererJob *job ); QgsMapSettings prepareMapSettings() const; void updateTransform(); + void zoomToFullExtent(); QgsQuickMapSettings *mMapSettings; diff --git a/src/quickgui/qgsquickmapsettings.h b/src/quickgui/qgsquickmapsettings.h index 5ceaff700448..caa50f9f042e 100644 --- a/src/quickgui/qgsquickmapsettings.h +++ b/src/quickgui/qgsquickmapsettings.h @@ -33,38 +33,67 @@ class QgsProject; * The QgsQuickMapSettings class encapsulates QgsMapSettings class to offer * settings of configuration of map rendering via QML properties. * + * On top of QgsMapSettings functionality, when QgsProject is attached, + * it automatically loads its default settings from the project. + * * \note QML Type: MapSettings * + * \sa QgsMapCanvas + * * \since QGIS 3.2 */ class QUICK_EXPORT QgsQuickMapSettings : public QObject { Q_OBJECT + //! QGIS project Q_PROPERTY( QgsProject *project READ project WRITE setProject NOTIFY projectChanged ) + //! \see QgsMapSettings::extent() Q_PROPERTY( QgsRectangle extent READ extent WRITE setExtent NOTIFY extentChanged ) + //! \see QgsMapSettings::visibleExtent() Q_PROPERTY( QgsRectangle visibleExtent READ visibleExtent NOTIFY visibleExtentChanged ) + //! \see QgsMapSettings::mapUnitsPerPixel() Q_PROPERTY( double mapUnitsPerPixel READ mapUnitsPerPixel NOTIFY mapUnitsPerPixelChanged ) + //! \see QgsMapSettings::rotation() Q_PROPERTY( double rotation READ rotation WRITE setRotation NOTIFY rotationChanged ) + //! \see QgsMapSettings::outputSize() Q_PROPERTY( QSize outputSize READ outputSize WRITE setOutputSize NOTIFY outputSizeChanged ) + //! \see QgsMapSettings::outputDpi() Q_PROPERTY( double outputDpi READ outputDpi WRITE setOutputDpi NOTIFY outputDpiChanged ) + //! \see QgsMapSettings::destinationCrs() Q_PROPERTY( QgsCoordinateReferenceSystem destinationCrs READ destinationCrs WRITE setDestinationCrs NOTIFY destinationCrsChanged ) + //! \see QgsMapSettings::layers() Q_PROPERTY( QList layers READ layers WRITE setLayers NOTIFY layersChanged ) public: + //! Create new map settings QgsQuickMapSettings( QObject *parent = 0 ); ~QgsQuickMapSettings(); + //! Clone map settings + QgsMapSettings mapSettings() const; + + //! \see QgsMapSettings::extent() QgsRectangle extent() const; + + //! \see QgsMapSettings::setExtent() void setExtent( const QgsRectangle &extent ); + /** + * Attach \a project with the map settings. + * When project is loaded, map settings are automatically populated from it's stored values + */ void setProject( QgsProject *project ); + //! Return associated QGIS project QgsProject *project() const; + //! Move current map extent to have center point defined by \a center Q_INVOKABLE void setCenter( const QgsPoint ¢er ); + //! \see QgsMapSettings::mapUnitsPerPixel() double mapUnitsPerPixel() const; + //! \see QgsMapSettings::visibleExtent() QgsRectangle visibleExtent() const; /** @@ -77,9 +106,9 @@ class QUICK_EXPORT QgsQuickMapSettings : public QObject /** * Convert a map coordinate to screen pixel coordinates * - * @param p A coordinate in map coordinates + * \param p A coordinate in map coordinates * - * @return A coordinate in pixel / screen space + * \return A coordinate in pixel / screen space */ Q_INVOKABLE QPointF coordinateToScreen( const QgsPoint &p ) const; @@ -87,9 +116,9 @@ class QUICK_EXPORT QgsQuickMapSettings : public QObject /** * Convert a screen coordinate to a map coordinate * - * @param p A coordinate in pixel / screen coordinates + * \param p A coordinate in pixel / screen coordinates * - * @return A coordinate in map coordinates + * \return A coordinate in map coordinates */ Q_INVOKABLE QgsPoint screenToCoordinate( const QPointF &p ) const; @@ -103,35 +132,95 @@ class QUICK_EXPORT QgsQuickMapSettings : public QObject */ void setTransformContext( const QgsCoordinateTransformContext &context ); + //! Return rotation double rotation() const; - void setRotation( double rotation ); - QgsMapSettings mapSettings() const; + //! Set rotation + void setRotation( double rotation ); + /** + * Return output size + * \see QgsMapSettings::outputSize() + */ QSize outputSize() const; + + /** + * Set output size and emit outputSizeChanged + * \see QgsMapSettings::setOutputSize() + */ void setOutputSize( const QSize &outputSize ); + /** + * Return output DPI + * \see QgsMapSettings::outputDpi() + */ double outputDpi() const; + + /** + * Set output DPI and emit outputDpiChanged + * \see QgsMapSettings::setOutputDpi() + */ void setOutputDpi( double outputDpi ); + /** + * Return destination CRS + * \see QgsMapSettings::destinationCrs() + */ QgsCoordinateReferenceSystem destinationCrs() const; + + /** + * Set destination CRS and emit destinationCrsChanged + * \see QgsMapSettings::setDestinationCrs() + */ void setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs ); + /** + * Return layers + * \see QgsMapSettings::layers() + */ QList layers() const; + + /** + * Set layers and emit layersChanged + * \see QgsMapSettings::setLayers() + */ void setLayers( const QList &layers ); signals: + //! project changed void projectChanged(); + + //! extent changed void extentChanged(); + + //! destination CRS changed void destinationCrsChanged(); + + //! map units per pixel changed void mapUnitsPerPixelChanged(); + + //! rotation changed void rotationChanged(); + + //! visible extent changed void visibleExtentChanged(); + + //! output size changed void outputSizeChanged(); + + //! output DPI changed void outputDpiChanged(); + + //! layers changed void layersChanged(); private slots: + + /** + * Read map canvas settings stored in a QGIS project file + * + * \param doc parsed DOM of a QgsProject + */ void onReadProject( const QDomDocument &doc ); private: diff --git a/src/quickgui/qgsquickmaptransform.h b/src/quickgui/qgsquickmaptransform.h index 37824fc0361b..5b3ad7e49604 100644 --- a/src/quickgui/qgsquickmaptransform.h +++ b/src/quickgui/qgsquickmaptransform.h @@ -37,18 +37,31 @@ class QgsQuickMapSettings; class QUICK_EXPORT QgsQuickMapTransform : public QQuickTransform { Q_OBJECT + + //! map settings Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) public: + //! create new map transform QgsQuickMapTransform(); ~QgsQuickMapTransform(); + /** + * Apply transformation based on current map settings to a matrix. + * + * Also optimize resulting matrix after transformation + * \param matrix Matrix to be transformed + */ void applyTo( QMatrix4x4 *matrix ) const; + //! Return map settings QgsQuickMapSettings *mapSettings() const; + + //! Set map settings void setMapSettings( QgsQuickMapSettings *mapSettings ); signals: + //! Map settings changed void mapSettingsChanged(); private slots: diff --git a/src/quickgui/qgsquickmessagelogmodel.h b/src/quickgui/qgsquickmessagelogmodel.h index dfceab9a89cd..6d32d17c45bc 100644 --- a/src/quickgui/qgsquickmessagelogmodel.h +++ b/src/quickgui/qgsquickmessagelogmodel.h @@ -63,6 +63,7 @@ class QUICK_EXPORT QgsQuickMessageLogModel : public QAbstractListModel }; public: + //! Create new message log model QgsQuickMessageLogModel( QObject *parent = nullptr ); QHash roleNames() const override; diff --git a/src/quickgui/qgsquickpositionkit.cpp b/src/quickgui/qgsquickpositionkit.cpp index 80663b46e747..edf702d6943d 100644 --- a/src/quickgui/qgsquickpositionkit.cpp +++ b/src/quickgui/qgsquickpositionkit.cpp @@ -133,3 +133,28 @@ void QgsQuickPositionKit::onUpdateTimeout() emit hasPositionChanged(); } } + +bool QgsQuickPositionKit::hasPosition() const +{ + return mHasPosition; +} + +QgsPoint QgsQuickPositionKit::position() const +{ + return mPosition; +} + +qreal QgsQuickPositionKit::accuracy() const +{ + return mAccuracy; +} + +qreal QgsQuickPositionKit::direction() const +{ + return mDirection; +} + +bool QgsQuickPositionKit::simulated() const +{ + return mIsSimulated; +} diff --git a/src/quickgui/qgsquickpositionkit.h b/src/quickgui/qgsquickpositionkit.h index 473030085c2c..638404e9a26d 100644 --- a/src/quickgui/qgsquickpositionkit.h +++ b/src/quickgui/qgsquickpositionkit.h @@ -25,7 +25,7 @@ /** * \ingroup quick - * Convinient set of tools to read GPS position and accuracy. + * Convenient set of tools to read GPS position and accuracy. * * Also, if one can use use_simulated_location to specify simulated position. * Simulated position source generates random points in circles around the selected @@ -65,20 +65,36 @@ class QUICK_EXPORT QgsQuickPositionKit : public QObject Q_PROPERTY( bool isSimulated READ simulated NOTIFY isSimulatedChanged ) public: + //! Create new position kit explicit QgsQuickPositionKit( QObject *parent = 0 ); - bool hasPosition() const { return mHasPosition; } - QgsPoint position() const { return mPosition; } - qreal accuracy() const { return mAccuracy; } - qreal direction() const { return mDirection; } - bool simulated() const { return mIsSimulated; } + //! Return if GPS position is available + bool hasPosition() const; + + //! Return GPS position in WGS84 coords + QgsPoint position() const; + + //! Return GPS horizontal accuracy in meters, -1 if not available + qreal accuracy() const; + + //! Return GPS direction, bearing in degrees clockwise from north to direction of travel. -1 if not available + qreal direction() const; + + //! Return whether GPS source is simulated. + bool simulated() const; /** * Use simulated GPS source. * + * Simulated GPS source emulates point on circle around defined point in specified radius + * * We do not want to have the origin point as property * We basically want to set it once based on project/map cente and keep * it that way regardless of mapsettings change (e.g. zoom etc) + * + * \param longitude longitude of the centre of the emulated points + * \param latitude latitude of the centre of the emulated points + * \param radius distance of emulated points from the centre (in degrees WSG84) */ Q_INVOKABLE void use_simulated_location( double longitude, double latitude, double radius ); @@ -88,10 +104,14 @@ class QUICK_EXPORT QgsQuickPositionKit : public QObject Q_INVOKABLE void use_gps_location(); signals: + //! GPS position changed void positionChanged(); + + //! hasPosition changed void hasPositionChanged(); + + //! changed if GPS position is simulated or not void isSimulatedChanged(); - void statusChanged(); public slots: @@ -99,8 +119,6 @@ class QUICK_EXPORT QgsQuickPositionKit : public QObject void positionUpdated( const QGeoPositionInfo &info ); void onUpdateTimeout(); - protected: - protected: QgsPoint mPosition; qreal mAccuracy; diff --git a/src/quickgui/qgsquickscalebarkit.h b/src/quickgui/qgsquickscalebarkit.h index 900cdb1a44eb..67a9b4a9afc4 100644 --- a/src/quickgui/qgsquickscalebarkit.h +++ b/src/quickgui/qgsquickscalebarkit.h @@ -44,6 +44,9 @@ class QUICK_EXPORT QgsQuickScaleBarKit : public QObject { Q_OBJECT + /** + * Associated map settings + */ Q_PROPERTY( QgsQuickMapSettings *mapSettings MEMBER mMapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) /** @@ -62,26 +65,48 @@ class QUICK_EXPORT QgsQuickScaleBarKit : public QObject Q_PROPERTY( int distance READ distance NOTIFY scaleBarChanged ) /** - * Calculated width of scalebar in pixels representing distance + units. Differs minimum possible from prefferedWidth to + * Calculated width of scalebar in pixels representing distance + units. Differs minimum possible from preferredWidth to * get "nice" distance number. */ Q_PROPERTY( int width READ width NOTIFY scaleBarChanged ) public: + //! create new scale bar kit explicit QgsQuickScaleBarKit( QObject *parent = 0 ); ~QgsQuickScaleBarKit(); + //! Set map settings void setMapSettings( QgsQuickMapSettings *mapSettings ); + + //! Returns calculated width in pixels int width() const; + + /** + * Returns distance corensponding to width + * + * \see QgsQuickScaleBarKit::units() + */ int distance() const; + + /** + * Returns units of distance (m, km) + * + * \see QgsQuickScaleBarKit::distance() + */ QString units() const; signals: + //! width, distance and/or units changed void scaleBarChanged(); + + //! map settings changed void mapSettingsChanged(); + + //! preferred width changed void preferredWidthChanged(); public slots: + //! recalculate width, distance and units void updateScaleBar(); private: diff --git a/src/quickgui/qgsquicksimulatedpositionsource.h b/src/quickgui/qgsquicksimulatedpositionsource.h index d1e45af77a47..4f2fded39570 100644 --- a/src/quickgui/qgsquicksimulatedpositionsource.h +++ b/src/quickgui/qgsquicksimulatedpositionsource.h @@ -45,8 +45,6 @@ * * \since QGIS 3.2 */ - -//! class QUICK_NO_EXPORT QgsQuickSimulatedPositionSource : public QGeoPositionInfoSource { Q_OBJECT diff --git a/src/quickgui/qgsquicksubmodel.h b/src/quickgui/qgsquicksubmodel.h index 7c8156834f1d..9cfbe41ef82f 100644 --- a/src/quickgui/qgsquicksubmodel.h +++ b/src/quickgui/qgsquicksubmodel.h @@ -24,6 +24,8 @@ * * Helper class for submodels (e.g. tabs within feature model) * + * It uses internal mapping from internal indexes to indexes in the parent model + * * \note QML Type: SubModel * * \since QGIS 3.2 @@ -32,27 +34,54 @@ class QUICK_EXPORT QgsQuickSubModel : public QAbstractItemModel { Q_OBJECT + //! parent model (e.g QgsQuickAttributeFormModel) Q_PROPERTY( QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged ) + + //! root index of parent model Q_PROPERTY( QModelIndex rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged ) public: + //! Create new sub model QgsQuickSubModel( QObject *parent = nullptr ); + + //! Returns the index of the item in the model specified by the given row, column and parent index. QModelIndex index( int row, int column, const QModelIndex &parent ) const override; + + //! Returns the parent of the model item with the given index. If the item has no parent, an invalid QModelIndex is returned. QModelIndex parent( const QModelIndex &child ) const override; + + //! Returns the number of rows under the given parent. Returns 0 on invalid mModel int rowCount( const QModelIndex &parent ) const override; + + //! Returns the number of columns under the given parent. Returns 0 on invalid mModel int columnCount( const QModelIndex &parent ) const override; + + //! Returns the data stored under the given role for the item referred to by the index. Returns empty QVariant on invalid mModel QVariant data( const QModelIndex &index, int role ) const override; + + //! Sets the role data for the item at index to value. bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override; + + //! Returns the mModel's role names. Returns empty QHash on invalid mModel QHash roleNames() const override; + //! Return root index QModelIndex rootIndex() const; + + //! Set root index void setRootIndex( const QModelIndex &rootIndex ); + //! Return model QAbstractItemModel *model() const; + + //! Set model void setModel( QAbstractItemModel *model ); signals: + //! mModel is changed void modelChanged(); + + //! mRootIndex is changed void rootIndexChanged(); private slots: diff --git a/src/quickgui/qgsquickutils.cpp b/src/quickgui/qgsquickutils.cpp index 806326f3961e..64611f36fdfc 100644 --- a/src/quickgui/qgsquickutils.cpp +++ b/src/quickgui/qgsquickutils.cpp @@ -50,7 +50,7 @@ QgsQuickUtils::QgsQuickUtils( QObject *parent ) // calculate screen density for calculation of real pixel sizes from density-independent pixels int dpiX = QApplication::desktop()->physicalDpiX(); int dpiY = QApplication::desktop()->physicalDpiY(); - int dpi = dpiX < dpiY ? dpiX : dpiY; // In case of asymetrical DPI. Improbable + int dpi = dpiX < dpiY ? dpiX : dpiY; // In case of asymmetrical DPI. Improbable mScreenDensity = dpi / 160.; // 160 DPI is baseline for density-independent pixels in Android } @@ -122,14 +122,7 @@ bool QgsQuickUtils::fileExists( QString path ) { QFileInfo check_file( path ); // check if file exists and if yes: Is it really a file and no directory? - if ( check_file.exists() && check_file.isFile() ) - { - return true; - } - else - { - return false; - } + return ( check_file.exists() && check_file.isFile() ); } void QgsQuickUtils::copyFile( QString sourcePath, QString targetPath ) diff --git a/src/quickgui/qgsquickutils.h b/src/quickgui/qgsquickutils.h index 52526e4458c8..2cbfa02fdde8 100644 --- a/src/quickgui/qgsquickutils.h +++ b/src/quickgui/qgsquickutils.h @@ -59,13 +59,12 @@ class QUICK_EXPORT QgsQuickUtils: public QObject Q_PROPERTY( qreal dp READ screenDensity CONSTANT ) public: + //! return instance of the QgsQuickUtils singleton static QgsQuickUtils *instance(); //! Calculated density of the screen - see "dp" property for more details qreal screenDensity() const; - // CRS and geometry - /** * Create crs from epsg code in QML */ @@ -95,28 +94,32 @@ class QUICK_EXPORT QgsQuickUtils: public QObject Q_INVOKABLE double screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels ) const; /** - * Has QgsFeature a geometry that can be added to the layer (non-emptry, same geometry type)? + * Has QgsFeature a geometry that can be added to the layer (non-empty, same geometry type)? */ Q_INVOKABLE bool hasValidGeometry( QgsVectorLayer *layer, const QgsFeature &feat ); - // Common + //! Check if file on path exists Q_INVOKABLE bool fileExists( QString path ); + + //! Copy file from sourcePath to targetPath Q_INVOKABLE void copyFile( QString sourcePath, QString targetPath ); + + //! Delete file on path from disk Q_INVOKABLE void remove( QString path ); + + //! Extract filename from path Q_INVOKABLE QString getFileName( QString path ); + + //! Log message in QgsMessageLog Q_INVOKABLE void logMessage( const QString &message, const QString &tag = QString( "QgsQuick" ), Qgis::MessageLevel level = Qgis::Warning ); - // Themes - /** * Get icon from custom theme dir or default if not found in the theme dir */ Q_INVOKABLE QUrl getThemeIcon( const QString &name ); - // Formatting - /** * point to string, e.g. -2.234521, 34.4444421 -> -2.234, 34.444 */ diff --git a/tests/src/quickgui/app/FeaturePanel.qml b/tests/src/quickgui/app/FeaturePanel.qml index 9e854319a64c..47593659a377 100644 --- a/tests/src/quickgui/app/FeaturePanel.qml +++ b/tests/src/quickgui/app/FeaturePanel.qml @@ -73,7 +73,7 @@ Drawer { onSaved: { featurePanel.visible = false } - onCancelled: featurePanel.visible = false + onCanceled: featurePanel.visible = false } } From 7784fd20df9359cdd29f4db57c3696074769d563 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 2 Mar 2018 13:57:49 +0100 Subject: [PATCH 04/31] add to CI --- .ci/travis/linux/docker-build-test.sh | 1 + .docker/qgis3-build-deps.dockerfile | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.ci/travis/linux/docker-build-test.sh b/.ci/travis/linux/docker-build-test.sh index 761810d14534..4f8a7ffb88ba 100755 --- a/.ci/travis/linux/docker-build-test.sh +++ b/.ci/travis/linux/docker-build-test.sh @@ -40,6 +40,7 @@ echo "${bold}Running cmake...${endbold}" cmake \ -GNinja \ -DWITH_3D=ON \ + -DWITH_QUICK=ON \ -DWITH_STAGED_PLUGINS=ON \ -DWITH_GRASS=OFF \ -DSUPPRESS_QT_WARNINGS=ON \ diff --git a/.docker/qgis3-build-deps.dockerfile b/.docker/qgis3-build-deps.dockerfile index a620f726c3bd..6c05693e4e87 100644 --- a/.docker/qgis3-build-deps.dockerfile +++ b/.docker/qgis3-build-deps.dockerfile @@ -33,11 +33,17 @@ RUN apt-get update \ libqca-qt5-2-dev \ libqca-qt5-2-plugins \ libqt53drender5 \ + libqt5concurrent5 \ libqt5opengl5-dev \ + libqt5positioning5 \ + libqt5qml5 \ + libqt5quick5 \ + libqt5quickcontrols2-5 \ libqt5scintilla2-dev \ libqt5sql5-sqlite \ libqt5svg5-dev \ libqt5webkit5-dev \ + libqt5xml5 \ libqt5xmlpatterns5-dev \ libqwt-qt5-dev \ libspatialindex-dev \ From ba4a71741127734810e05da0953ddcf733c40172 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 2 Mar 2018 14:51:16 +0100 Subject: [PATCH 05/31] fix PyQgsDocCoverage test (add docs) --- src/quickgui/qgsquickattributeformmodel.h | 30 ++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/quickgui/qgsquickattributeformmodel.h b/src/quickgui/qgsquickattributeformmodel.h index a86212a5b986..804c06f77375 100644 --- a/src/quickgui/qgsquickattributeformmodel.h +++ b/src/quickgui/qgsquickattributeformmodel.h @@ -51,22 +51,24 @@ class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel Q_PROPERTY( bool constraintsValid READ constraintsValid NOTIFY constraintsValidChanged ) public: + + //! Feature fieds's roles enum FeatureRoles { - ElementType = Qt::UserRole + 1, - Name, - AttributeValue, - AttributeEditable, - EditorWidget, - EditorWidgetConfig, - RememberValue, - Field, - FieldIndex, - Group, - AttributeEditorElement, - CurrentlyVisible, - ConstraintValid, - ConstraintDescription + ElementType = Qt::UserRole + 1, //!< Element type + Name, //!< Field Name + AttributeValue, //!< Field Value + AttributeEditable, //!< Field editable + EditorWidget, //!< Widget + EditorWidgetConfig, //!< Config + RememberValue, //!< Remember value + Field, //!< Field + FieldIndex, //!< Index + Group, //!< Group + AttributeEditorElement, //!< Attibute editor element + CurrentlyVisible, //!< Field visible + ConstraintValid, //!< Contraint valid + ConstraintDescription //!< Contraint description }; Q_ENUM( FeatureRoles ) From 438b2f75ea700b69c97e0ffcc7c32c9d8b66cc47 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 2 Mar 2018 15:26:28 +0100 Subject: [PATCH 06/31] fix spelling --- src/quickgui/qgsquickattributeformmodel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quickgui/qgsquickattributeformmodel.h b/src/quickgui/qgsquickattributeformmodel.h index 804c06f77375..5c13b756d66e 100644 --- a/src/quickgui/qgsquickattributeformmodel.h +++ b/src/quickgui/qgsquickattributeformmodel.h @@ -65,7 +65,7 @@ class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel Field, //!< Field FieldIndex, //!< Index Group, //!< Group - AttributeEditorElement, //!< Attibute editor element + AttributeEditorElement, //!< Attribute editor element CurrentlyVisible, //!< Field visible ConstraintValid, //!< Contraint valid ConstraintDescription //!< Contraint description From 643030b364f152aba7163d1d89f6a5ed63338273 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 2 Mar 2018 09:42:02 -0400 Subject: [PATCH 07/31] allow to save a Docker deps image for PR having [dockerdeps] in their title --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bb8380168688..139bf2d97344 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,8 @@ matrix: env: - TRAVIS_CONFIG=linux - DOCKER_COMPOSE=${TRAVIS_BUILD_DIR}/.docker/docker-compose.travis.yml - - DOCKER_TAG=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && echo $TRAVIS_BRANCH | sed 's/master/latest/' || echo "latest" ) - - DOCKER_DEPS_PUSH=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && [[ $TRAVIS_EVENT_TYPE =~ push ]] && echo "true" || echo "false" ) + - DOCKER_TAG=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && ( [[ $TRAVIS_EVENT_TYPE =~ pull_request ]] && [[ $TRAVIS_COMMIT_MESSAGE =~ \[dockerdeps\] ]] && echo "PR-$TRAVIS_PULL_REQUEST" ) || echo $TRAVIS_BRANCH | sed 's/master/latest/' || echo "latest" ) + - DOCKER_DEPS_PUSH=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && ( [[ $TRAVIS_EVENT_TYPE =~ push ]] || ( [[ $TRAVIS_EVENT_TYPE =~ pull_request ]] && [[ $TRAVIS_COMMIT_MESSAGE =~ \[dockerdeps\] ]] ) ) && echo "true" || echo "false" ) - DOCKER_DEPS_IMAGE_REBUILD=$( [[ $TRAVIS_COMMIT_MESSAGE =~ '[docker] update dependencies' ]] && echo "true" || echo "false" ) # on cron job, QGIS image is built and push without testing - DOCKER_QGIS_IMAGE_BUILD_PUSH=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && [[ $TRAVIS_EVENT_TYPE =~ cron ]] && echo "true" || echo "false" ) From 7432536373b28383ce6da33cdad094ff53bd74c7 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 2 Mar 2018 09:59:05 -0400 Subject: [PATCH 08/31] fix docker tag --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 139bf2d97344..b9ce3597f1cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ matrix: env: - TRAVIS_CONFIG=linux - DOCKER_COMPOSE=${TRAVIS_BUILD_DIR}/.docker/docker-compose.travis.yml - - DOCKER_TAG=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && ( [[ $TRAVIS_EVENT_TYPE =~ pull_request ]] && [[ $TRAVIS_COMMIT_MESSAGE =~ \[dockerdeps\] ]] && echo "PR-$TRAVIS_PULL_REQUEST" ) || echo $TRAVIS_BRANCH | sed 's/master/latest/' || echo "latest" ) + - DOCKER_TAG=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && ( ( [[ $TRAVIS_EVENT_TYPE =~ pull_request ]] && [[ $TRAVIS_COMMIT_MESSAGE =~ \[dockerdeps\] ]] ) && echo "PR-$TRAVIS_PULL_REQUEST" || echo $TRAVIS_BRANCH | sed 's/master/latest/' ) || echo "latest" ) - DOCKER_DEPS_PUSH=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && ( [[ $TRAVIS_EVENT_TYPE =~ push ]] || ( [[ $TRAVIS_EVENT_TYPE =~ pull_request ]] && [[ $TRAVIS_COMMIT_MESSAGE =~ \[dockerdeps\] ]] ) ) && echo "true" || echo "false" ) - DOCKER_DEPS_IMAGE_REBUILD=$( [[ $TRAVIS_COMMIT_MESSAGE =~ '[docker] update dependencies' ]] && echo "true" || echo "false" ) # on cron job, QGIS image is built and push without testing From 715142b529d5e0c049b1c4060c8101499170160c Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Mon, 19 Mar 2018 15:03:51 +0100 Subject: [PATCH 09/31] port fix for https://github.com/opengisch/QField/issues/125 --- src/quickgui/qgsquickattributeformmodelbase.cpp | 5 ++--- src/quickgui/qgsquickattributeformmodelbase.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/quickgui/qgsquickattributeformmodelbase.cpp b/src/quickgui/qgsquickattributeformmodelbase.cpp index b8d1c41677b4..de3fd80a3db3 100644 --- a/src/quickgui/qgsquickattributeformmodelbase.cpp +++ b/src/quickgui/qgsquickattributeformmodelbase.cpp @@ -222,16 +222,15 @@ void QgsQuickAttributeFormModelBase::updateAttributeValue( QStandardItem *item ) } } -void QgsQuickAttributeFormModelBase::flatten( QgsAttributeEditorContainer *container, QStandardItem *parent, const QString &visibilityExpressions, QVector &items ) +void QgsQuickAttributeFormModelBase::flatten( QgsAttributeEditorContainer *container, QStandardItem *parent, const QString &parentVisibilityExpressions, QVector &items ) { - QString visibilityExpression = visibilityExpressions; - Q_FOREACH ( QgsAttributeEditorElement *element, container->children() ) { switch ( element->type() ) { case QgsAttributeEditorElement::AeTypeContainer: { + QString visibilityExpression = parentVisibilityExpressions; QgsAttributeEditorContainer *container = static_cast( element ); if ( container->visibilityExpression().enabled() ) { diff --git a/src/quickgui/qgsquickattributeformmodelbase.h b/src/quickgui/qgsquickattributeformmodelbase.h index b94d6cac856e..5ab1f018fd8c 100644 --- a/src/quickgui/qgsquickattributeformmodelbase.h +++ b/src/quickgui/qgsquickattributeformmodelbase.h @@ -96,7 +96,7 @@ class QgsQuickAttributeFormModelBase : public QStandardItemModel void updateAttributeValue( QStandardItem *item ); - void flatten( QgsAttributeEditorContainer *container, QStandardItem *parent, const QString &visibilityExpressions, QVector &items ); + void flatten( QgsAttributeEditorContainer *container, QStandardItem *parent, const QString &parentVisibilityExpressions, QVector &items ); void updateVisibility( int fieldIndex = -1 ); From 0c2e64d0a57f2bdbcaa49659e1386355123d1073 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Mon, 19 Mar 2018 15:07:02 +0100 Subject: [PATCH 10/31] port fix https://github.com/opengisch/QField/issues/87 --- src/quickgui/qgsquickattributeformmodelbase.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/quickgui/qgsquickattributeformmodelbase.cpp b/src/quickgui/qgsquickattributeformmodelbase.cpp index de3fd80a3db3..388aab62bccd 100644 --- a/src/quickgui/qgsquickattributeformmodelbase.cpp +++ b/src/quickgui/qgsquickattributeformmodelbase.cpp @@ -325,16 +325,13 @@ void QgsQuickAttributeFormModelBase::updateVisibility( int fieldIndex ) for ( ; constraintIterator != mConstraints.constEnd(); ++constraintIterator ) { QStandardItem *item = constraintIterator.key(); - if ( item->data( QgsQuickAttributeFormModel::FieldIndex ) == fieldIndex || fieldIndex == -1 ) - { - QgsExpression exp = constraintIterator.value(); - exp.prepare( &mExpressionContext ); - bool constraintSatisfied = exp.evaluate( &mExpressionContext ).toBool(); + QgsExpression exp = constraintIterator.value(); + exp.prepare( &mExpressionContext ); + bool constraintSatisfied = exp.evaluate( &mExpressionContext ).toBool(); - if ( constraintSatisfied != item->data( QgsQuickAttributeFormModel::ConstraintValid ).toBool() ) - { - item->setData( constraintSatisfied, QgsQuickAttributeFormModel::ConstraintValid ); - } + if ( constraintSatisfied != item->data( QgsQuickAttributeFormModel::ConstraintValid ).toBool() ) + { + item->setData( constraintSatisfied, QgsQuickAttributeFormModel::ConstraintValid ); } if ( !item->data( QgsQuickAttributeFormModel::ConstraintValid ).toBool() ) From fa5b4e16eed324fc6cad9280c38f0b06c5910efc Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Mon, 19 Mar 2018 15:27:34 +0100 Subject: [PATCH 11/31] port https://github.com/opengisch/QField/issues/173 --- src/quickgui/plugin/editor/qgsquicktextedit.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quickgui/plugin/editor/qgsquicktextedit.qml b/src/quickgui/plugin/editor/qgsquicktextedit.qml index 21d69d5dcfa4..11a269c31226 100644 --- a/src/quickgui/plugin/editor/qgsquicktextedit.qml +++ b/src/quickgui/plugin/editor/qgsquicktextedit.qml @@ -39,7 +39,7 @@ Item { text: value || '' - inputMethodHints: field.isNumeric || widget == 'Range' ? Qt.ImhFormattedNumbersOnly : Qt.ImhNone + inputMethodHints: field.isNumeric || widget == 'Range' ? field.precision === 0 ? Qt.ImhDigitsOnly : Qt.ImhFormattedNumbersOnly : Qt.ImhNone // Make sure we do not input more characters than allowed for strings states: [ From 1521ebb8312d413cb7a9d7b6f679f6f05f0f8445 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Mon, 19 Mar 2018 15:29:43 +0100 Subject: [PATCH 12/31] port https://github.com/opengisch/QField/issues/118 --- src/quickgui/plugin/editor/qgsquickcheckbox.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quickgui/plugin/editor/qgsquickcheckbox.qml b/src/quickgui/plugin/editor/qgsquickcheckbox.qml index 0645056a80ea..2dacb7e1f56c 100644 --- a/src/quickgui/plugin/editor/qgsquickcheckbox.qml +++ b/src/quickgui/plugin/editor/qgsquickcheckbox.qml @@ -33,7 +33,7 @@ Item { CheckBox { property var currentValue: value - checked: value === config['CheckedState'] + checked: value == config['CheckedState'] onCheckedChanged: { valueChanged( checked ? config['CheckedState'] : config['UncheckedState'], false ) @@ -42,7 +42,7 @@ Item { // Workaround to get a signal when the value has changed onCurrentValueChanged: { - checked = currentValue === config['CheckedState'] + checked = currentValue == config['CheckedState'] } } } From 592cd1d456f2e32608bfc8094a742aec6d971682 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Mon, 19 Mar 2018 15:44:08 +0100 Subject: [PATCH 13/31] port https://github.com/opengisch/QField/commit/97c7a4ae2bd2c1c6bbc0b9359847e8d492e69f12 --- src/quickgui/qgsquickmapcanvasmap.cpp | 4 +--- src/quickgui/qgsquickmapcanvasmap.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/quickgui/qgsquickmapcanvasmap.cpp b/src/quickgui/qgsquickmapcanvasmap.cpp index e5ac215dd386..4ee8ad39cdaf 100644 --- a/src/quickgui/qgsquickmapcanvasmap.cpp +++ b/src/quickgui/qgsquickmapcanvasmap.cpp @@ -362,9 +362,7 @@ void QgsQuickMapCanvasMap::stopRendering() disconnect( mJob, &QgsMapRendererJob::renderingLayersFinished, this, &QgsQuickMapCanvasMap::renderJobUpdated ); disconnect( mJob, &QgsMapRendererJob::finished, this, &QgsQuickMapCanvasMap::renderJobFinished ); - // Destroy job in separate worker thread, killing an iterator may take some time - // and reduce responsiveness - mZombieJobs.addFuture( QtConcurrent::run( this, &QgsQuickMapCanvasMap::destroyJob, mJob ) ); + mJob->cancelWithoutBlocking(); mJob = nullptr; } } diff --git a/src/quickgui/qgsquickmapcanvasmap.h b/src/quickgui/qgsquickmapcanvasmap.h index ea9a3ed6b271..1209383bb794 100644 --- a/src/quickgui/qgsquickmapcanvasmap.h +++ b/src/quickgui/qgsquickmapcanvasmap.h @@ -165,7 +165,6 @@ class QUICK_EXPORT QgsQuickMapCanvasMap : public QQuickItem bool mDirty; bool mFreeze; QList mLayerConnections; - QFutureSynchronizer mZombieJobs; QTimer mMapUpdateTimer; int mMapUpdateInterval; bool mIncrementalRendering; From 8ef5f1c49b4fd208e7091304b2db8741498ce7a3 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Mon, 19 Mar 2018 16:08:01 +0100 Subject: [PATCH 14/31] allow to port https://github.com/opengisch/QField/issues/189 --- src/quickgui/qgsquickfeaturemodel.cpp | 8 ++++++++ src/quickgui/qgsquickfeaturemodel.h | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/quickgui/qgsquickfeaturemodel.cpp b/src/quickgui/qgsquickfeaturemodel.cpp index c904827fae68..4bb6c1dd3a40 100644 --- a/src/quickgui/qgsquickfeaturemodel.cpp +++ b/src/quickgui/qgsquickfeaturemodel.cpp @@ -228,7 +228,15 @@ void QgsQuickFeatureModel::resetAttributes() if ( !fields.at( i ).defaultValueDefinition().expression().isEmpty() ) { QgsExpression exp( fields.at( i ).defaultValueDefinition().expression() ); + exp.prepare( &expressionContext ); + if ( exp.hasParserError() ) + QgsMessageLog::logMessage( tr( "Default value expression for %1:%2 has parser error: %3" ).arg( mLayer->name(), fields.at( i ).name(), exp.parserErrorString() ), QStringLiteral( "QgsQuick" ), Qgis::Warning ); + QVariant value = exp.evaluate( &expressionContext ); + + if ( exp.hasEvalError() ) + QgsMessageLog::logMessage( tr( "Default value expression for %1:%2 has evaluation error: %3" ).arg( mLayer->name(), fields.at( i ).name(), exp.evalErrorString() ), QStringLiteral( "QgsQuick" ), Qgis::Warning ); + mFeature.setAttribute( i, value ); } else diff --git a/src/quickgui/qgsquickfeaturemodel.h b/src/quickgui/qgsquickfeaturemodel.h index b868a2d26616..60ea2739ad1c 100644 --- a/src/quickgui/qgsquickfeaturemodel.h +++ b/src/quickgui/qgsquickfeaturemodel.h @@ -107,7 +107,7 @@ class QUICK_EXPORT QgsQuickFeatureModel : public QAbstractListModel Q_INVOKABLE bool suppressFeatureForm() const; //! Reset remembered attributes - Q_INVOKABLE void resetAttributes(); + Q_INVOKABLE virtual void resetAttributes(); //! Get remembered attributes QVector rememberedAttributes() const; @@ -121,7 +121,7 @@ class QUICK_EXPORT QgsQuickFeatureModel : public QAbstractListModel //! layer changed void layerChanged(); - private: + protected: bool commit(); bool startEditing(); From 599eb0c5478c428a9274c0eb9bb98abfdf4f8e7c Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Tue, 20 Mar 2018 08:55:48 +0100 Subject: [PATCH 15/31] allow subclasses to implement polygon support, which is not in the base implementation yet --- src/quickgui/qgsquickhighlightsgnode.cpp | 4 ++++ src/quickgui/qgsquickhighlightsgnode.h | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/quickgui/qgsquickhighlightsgnode.cpp b/src/quickgui/qgsquickhighlightsgnode.cpp index 78b98ced28fd..3aa8e19e6e97 100644 --- a/src/quickgui/qgsquickhighlightsgnode.cpp +++ b/src/quickgui/qgsquickhighlightsgnode.cpp @@ -49,6 +49,10 @@ QgsQuickHighlightSGNode::QgsQuickHighlightSGNode( const QVector &point } } +QgsQuickHighlightSGNode::~QgsQuickHighlightSGNode() +{ +} + QSGGeometryNode *QgsQuickHighlightSGNode::createLineGeometry( const QVector &points, qreal width ) { QSGGeometryNode *node = new QSGGeometryNode; diff --git a/src/quickgui/qgsquickhighlightsgnode.h b/src/quickgui/qgsquickhighlightsgnode.h index e950edacf2b0..360c43a9f90c 100644 --- a/src/quickgui/qgsquickhighlightsgnode.h +++ b/src/quickgui/qgsquickhighlightsgnode.h @@ -29,20 +29,23 @@ * * This is used to transform (render) QgsGeometry to node for QtQuick scene graph. * + * Note: support for multi-part geometries and polygons is not implemented + * * \note QML Type: not exported * * \since QGIS 3.2 */ -class QUICK_NO_EXPORT QgsQuickHighlightSGNode : public QSGNode +class QUICK_EXPORT QgsQuickHighlightSGNode : public QSGNode { public: //! Create new QT Quick scene node based on geometry QgsQuickHighlightSGNode( const QVector &points, QgsWkbTypes::GeometryType type, const QColor &color, qreal width ); + virtual ~QgsQuickHighlightSGNode(); - private: + protected: QSGGeometryNode *createLineGeometry( const QVector &points, qreal width ); QSGGeometryNode *createPointGeometry( const QgsPoint &point, qreal width ); - QSGGeometryNode *createPolygonGeometry( const QVector &points ); + virtual QSGGeometryNode *createPolygonGeometry( const QVector &points ); QSGFlatColorMaterial mMaterial; }; From d3860605b23f2e64b5f7ea3a03212eadd7882b66 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Tue, 20 Mar 2018 09:38:58 +0100 Subject: [PATCH 16/31] add support for qtpositioning point --- src/quickgui/qgsquickutils.cpp | 4 ++++ src/quickgui/qgsquickutils.h | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/quickgui/qgsquickutils.cpp b/src/quickgui/qgsquickutils.cpp index 64611f36fdfc..d0b631093910 100644 --- a/src/quickgui/qgsquickutils.cpp +++ b/src/quickgui/qgsquickutils.cpp @@ -73,6 +73,10 @@ QgsPoint QgsQuickUtils::pointFactory( double x, double y ) const return QgsPoint( x, y ); } +QgsPoint QgsQuickUtils::coordinateToPoint( const QGeoCoordinate& coor ) const +{ + return QgsPoint( coor.longitude(), coor.latitude(), coor.altitude() ); +} QgsPointXY QgsQuickUtils::transformPoint( const QgsCoordinateReferenceSystem &srcCrs, const QgsCoordinateReferenceSystem &destCrs, diff --git a/src/quickgui/qgsquickutils.h b/src/quickgui/qgsquickutils.h index 2cbfa02fdde8..5401607881d8 100644 --- a/src/quickgui/qgsquickutils.h +++ b/src/quickgui/qgsquickutils.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "qgis.h" #include "qgsfeature.h" @@ -80,6 +81,11 @@ class QUICK_EXPORT QgsQuickUtils: public QObject */ Q_INVOKABLE QgsPoint pointFactory( double x, double y ) const; + /** + * Convert QGeoCoordinate to QgsPoint + */ + Q_INVOKABLE QgsPoint coordinateToPoint( const QGeoCoordinate& coor ) const; + /** * Transform point between different crs from QML */ From 4eff7ac503f25854ef8163c3632d673ba2eff718 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 21 Mar 2018 09:33:04 +0100 Subject: [PATCH 17/31] generete qgis_quick.h by CMake --- src/quickgui/CMakeLists.txt | 9 +++++++-- src/quickgui/qgis_quick.h | 33 --------------------------------- 2 files changed, 7 insertions(+), 35 deletions(-) delete mode 100644 src/quickgui/qgis_quick.h diff --git a/src/quickgui/CMakeLists.txt b/src/quickgui/CMakeLists.txt index 190c301e6d12..892537386507 100644 --- a/src/quickgui/CMakeLists.txt +++ b/src/quickgui/CMakeLists.txt @@ -20,7 +20,6 @@ SET(QGIS_QUICK_GUI_MOC_HDRS ) SET(QGIS_QUICK_GUI_HDRS - qgis_quick.h qgsquickhighlightsgnode.h ) @@ -107,9 +106,15 @@ TARGET_LINK_LIBRARIES(qgis_quick Qt5::Quick Qt5::Qml Qt5::Xml Qt5::Concurrent Qt IF(CMAKE_SYSTEM_NAME STREQUAL "Android") TARGET_LINK_LIBRARIES(qgis_quick Qt5::AndroidExtras) ENDIF(CMAKE_SYSTEM_NAME STREQUAL "Android") - TARGET_COMPILE_DEFINITIONS(qgis_quick PRIVATE "-DDQT_NO_FOREACH") +GENERATE_EXPORT_HEADER( + qgis_quick + BASE_NAME QUICK + EXPORT_FILE_NAME qgis_quick.h +) +SET(QGIS_CORE_HDRS ${QGIS_QUICK_GUI_HDRS} ${CMAKE_CURRENT_BINARY_DIR}/qgis_core.h) + # Installation INSTALL(TARGETS qgis_quick RUNTIME DESTINATION ${QGIS_BIN_DIR} diff --git a/src/quickgui/qgis_quick.h b/src/quickgui/qgis_quick.h deleted file mode 100644 index d98a615a8f6f..000000000000 --- a/src/quickgui/qgis_quick.h +++ /dev/null @@ -1,33 +0,0 @@ -/*************************************************************************** - qgis_quick.h - -------------------------------------- - Date : Nov 2017 - Copyright : (C) 2017 by Peter Petrik - Email : zilolv 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 QGIS_QUICK_H -#define QGIS_QUICK_H - -#ifndef QUICK_EXPORT -# ifdef qgis_quick_EXPORTS -/* We are building this library */ -# define QUICK_EXPORT __attribute__((visibility("default"))) -# else -/* We are using this library */ -# define QUICK_EXPORT __attribute__((visibility("default"))) -# endif -#endif //QUICK_EXPORT - -#ifndef QUICK_NO_EXPORT -# define QUICK_NO_EXPORT __attribute__((visibility("hidden"))) -#endif - -#endif // QGIS_QUICK_H From c5d75738443b6cfe45770b1a6baa83b33c14aa90 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 21 Mar 2018 10:07:28 +0100 Subject: [PATCH 18/31] remove qDebug, use QgsDebugMsg --- src/quickgui/plugin/qgsquickplugin.cpp | 5 ++--- src/quickgui/qgsquickidentifykit.cpp | 6 +++--- src/quickgui/qgsquickmessagelogmodel.cpp | 3 ++- src/quickgui/qgsquickpositionkit.cpp | 4 +++- src/quickgui/qgsquickscalebarkit.cpp | 3 +-- src/quickgui/qgsquickutils.cpp | 11 ++++++----- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/quickgui/plugin/qgsquickplugin.cpp b/src/quickgui/plugin/qgsquickplugin.cpp index 7e3d176c9559..0935da5951dd 100644 --- a/src/quickgui/plugin/qgsquickplugin.cpp +++ b/src/quickgui/plugin/qgsquickplugin.cpp @@ -16,6 +16,7 @@ #include #include "qgsfeature.h" +#include "qgslogger.h" #include "qgsmaplayer.h" #include "qgsmessagelog.h" #include "qgspointxy.h" @@ -50,7 +51,7 @@ static QObject *_utilsProvider( QQmlEngine *engine, QJSEngine *scriptEngine ) void QgisQuickPlugin::registerTypes( const char *uri ) { - qDebug( "REGISTERING QQmlExtensionInterface: QgisQuick" ); + QgsDebugMsg( QStringLiteral( "REGISTERING QQmlExtensionInterface: QgisQuick" ) ); qRegisterMetaType< QList >( "QList" ); qRegisterMetaType< QgsAttributes > ( "QgsAttributes" ); @@ -81,7 +82,5 @@ void QgisQuickPlugin::registerTypes( const char *uri ) qmlRegisterSingletonType< QgsQuickUtils >( uri, 0, 1, "Utils", _utilsProvider ); qmlRegisterUncreatableType< QgsMessageLog >( uri, 0, 1, "QgsMessageLog", "Expose MessageLevel" ); - - qDebug( "REGISTERING FINISHED" ); } diff --git a/src/quickgui/qgsquickidentifykit.cpp b/src/quickgui/qgsquickidentifykit.cpp index 26fb9328d262..9826d81fec38 100644 --- a/src/quickgui/qgsquickidentifykit.cpp +++ b/src/quickgui/qgsquickidentifykit.cpp @@ -15,6 +15,7 @@ #include "qgsproject.h" +#include "qgslogger.h" #include "qgsrenderer.h" #include "qgsvectorlayer.h" @@ -79,7 +80,7 @@ QList QgsQuickIdentifyKit::identify( const QPointF &point ) } } - qDebug() << "IdentifyKit identified " << results.count() << " results"; + QgsDebugMsg( QString( "IdentifyKit identified %1 results" ).arg( results.count() ) ); return results; } @@ -178,8 +179,7 @@ QgsFeatureList QgsQuickIdentifyKit::identify( QgsVectorLayer *layer, const QPoin results = identifyVectorLayer( layer, mapPoint ); - qDebug() << "IdentifyKit identified " << results.count() << " results for layer " << layer->name(); - + QgsDebugMsg( QString( "IdentifyKit identified %1 results for layer %2" ).arg( results.count() ).arg( layer->name() ) ); return results; } diff --git a/src/quickgui/qgsquickmessagelogmodel.cpp b/src/quickgui/qgsquickmessagelogmodel.cpp index 015d7909c5dc..5c49ccd467bf 100644 --- a/src/quickgui/qgsquickmessagelogmodel.cpp +++ b/src/quickgui/qgsquickmessagelogmodel.cpp @@ -16,6 +16,7 @@ #include #include "qgis.h" +#include "qgslogger.h" #include "qgsmessagelog.h" #include "qgsapplication.h" @@ -63,6 +64,6 @@ void QgsQuickMessageLogModel::onMessageReceived( const QString &message, const Q { beginInsertRows( QModelIndex(), 0, 0 ); mMessages.prepend( LogMessage( tag, message, level ) ); - qDebug() << "Next message " << tag << " : " << message; + QgsDebugMsg( QString( "Next message %1 : %2" ).arg( tag ).arg( message ) ); endInsertRows(); } diff --git a/src/quickgui/qgsquickpositionkit.cpp b/src/quickgui/qgsquickpositionkit.cpp index edf702d6943d..f703a8b025ae 100644 --- a/src/quickgui/qgsquickpositionkit.cpp +++ b/src/quickgui/qgsquickpositionkit.cpp @@ -14,6 +14,7 @@ ***************************************************************************/ #include "qgis.h" +#include "qgslogger.h" #include "qgsmessagelog.h" #include "qgsquickpositionkit.h" @@ -89,7 +90,8 @@ void QgsQuickPositionKit::replacePositionSource( QGeoPositionInfoSource *source this, SLOT( positionUpdated( QGeoPositionInfo ) ) ); connect( mSource, SIGNAL( updateTimeout() ), this, SLOT( onUpdateTimeout() ) ); mSource->startUpdates(); - qDebug() << "Position source changed: " << mSource->sourceName(); + + QgsDebugMsg( QString( "Position source changed: %1" ).arg( mSource->sourceName() ) ); } } diff --git a/src/quickgui/qgsquickscalebarkit.cpp b/src/quickgui/qgsquickscalebarkit.cpp index 2ca2cf023027..0404804c4001 100644 --- a/src/quickgui/qgsquickscalebarkit.cpp +++ b/src/quickgui/qgsquickscalebarkit.cpp @@ -17,8 +17,8 @@ #include #include -#include "qgspointxy.h" #include "qgsdistancearea.h" +#include "qgspointxy.h" #include "qgsquickmapsettings.h" #include "qgsquickscalebarkit.h" @@ -113,7 +113,6 @@ void QgsQuickScaleBarKit::updateScaleBar() mDistance = round_digit * base; mWidth = mPreferredWidth * mDistance / dist; - //qDebug() << "Scale: " << mWidth << "px -> " << mDistance << " " << mUnits; emit scaleBarChanged(); } diff --git a/src/quickgui/qgsquickutils.cpp b/src/quickgui/qgsquickutils.cpp index d0b631093910..c18e6c996614 100644 --- a/src/quickgui/qgsquickutils.cpp +++ b/src/quickgui/qgsquickutils.cpp @@ -25,6 +25,7 @@ #include "qgscoordinatereferencesystem.h" #include "qgscoordinatetransform.h" #include "qgsdistancearea.h" +#include "qgslogger.h" #include "qgsvectorlayer.h" #include "qgsquickmapsettings.h" @@ -37,7 +38,7 @@ QgsQuickUtils *QgsQuickUtils::instance() { if ( !sInstance ) { - qDebug() << "QgsQuickUtils created: " << QThread::currentThreadId(); + QgsDebugMsg( QString( "QgsQuickUtils created: %1" ).arg( long( QThread::currentThreadId() ) ) ); sInstance = new QgsQuickUtils(); } return sInstance; @@ -73,7 +74,7 @@ QgsPoint QgsQuickUtils::pointFactory( double x, double y ) const return QgsPoint( x, y ); } -QgsPoint QgsQuickUtils::coordinateToPoint( const QGeoCoordinate& coor ) const +QgsPoint QgsQuickUtils::coordinateToPoint( const QGeoCoordinate &coor ) const { return QgsPoint( coor.longitude(), coor.latitude(), coor.altitude() ); } @@ -133,7 +134,7 @@ void QgsQuickUtils::copyFile( QString sourcePath, QString targetPath ) { if ( !fileExists( sourcePath ) ) { - qDebug() << "Source file does not exist!" << sourcePath; + QgsDebugMsg( QString( "Source file does not exist! %1" ).arg( sourcePath ) ); return; } @@ -148,7 +149,7 @@ void QgsQuickUtils::copyFile( QString sourcePath, QString targetPath ) if ( !QFile( sourcePath ).rename( dir.absoluteFilePath( filename ) ) ) { - qDebug() << "Couldn't rename file! Trying to copy instead"; + QgsDebugMsg( QString( "Couldn't rename file! Trying to copy instead! %1" ).arg( filename ) ); if ( !QFile( sourcePath ).copy( dir.absoluteFilePath( filename ) ) ) { QgsApplication::messageLog()->logMessage( tr( "File %1 could not be copied to folder %2.", "QgsQuick", Qgis::Critical ).arg( sourcePath, targetPath ) ); @@ -178,7 +179,7 @@ QUrl QgsQuickUtils::getThemeIcon( const QString &name ) { QString extension( ".svg" ); QString path = "qrc:/" + name + extension; - qDebug() << "Using icon " << name << " from " << path; + QgsDebugMsg( QString( "Using icon %1 from %2" ).arg( name ).arg( path ) ); return QUrl( path ); } From 99b52d2c1d2903a156c919e01e9fa20f9315c3bb Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 21 Mar 2018 10:13:42 +0100 Subject: [PATCH 19/31] move imports to cpp file --- src/quickgui/plugin/qgsquickplugin.cpp | 4 ++++ src/quickgui/plugin/qgsquickplugin.h | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/quickgui/plugin/qgsquickplugin.cpp b/src/quickgui/plugin/qgsquickplugin.cpp index 0935da5951dd..d99f72a6d824 100644 --- a/src/quickgui/plugin/qgsquickplugin.cpp +++ b/src/quickgui/plugin/qgsquickplugin.cpp @@ -15,6 +15,10 @@ #include +#include +#include +#include + #include "qgsfeature.h" #include "qgslogger.h" #include "qgsmaplayer.h" diff --git a/src/quickgui/plugin/qgsquickplugin.h b/src/quickgui/plugin/qgsquickplugin.h index 0c78bc408b27..770dd5b09c2f 100644 --- a/src/quickgui/plugin/qgsquickplugin.h +++ b/src/quickgui/plugin/qgsquickplugin.h @@ -17,9 +17,6 @@ #define QGSQUICKPLUGIN_H #include -#include -#include -#include /** * \ingroup quick From bf9bd936759e9a8885baf51e2050ece17271ebed Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 21 Mar 2018 13:33:57 +0100 Subject: [PATCH 20/31] allow to use different position kit --- src/quickgui/plugin/qgsquickpositionmarker.qml | 7 ++----- src/quickgui/plugin/qgsquickscalebar.qml | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/quickgui/plugin/qgsquickpositionmarker.qml b/src/quickgui/plugin/qgsquickpositionmarker.qml index 14c31be0eb45..e04f332684f8 100644 --- a/src/quickgui/plugin/qgsquickpositionmarker.qml +++ b/src/quickgui/plugin/qgsquickpositionmarker.qml @@ -24,6 +24,7 @@ Item { property int size: 48 * QgsQuick.Utils.dp property QgsQuick.MapSettings mapSettings // required to be connected from parent! + property QgsQuick.PositionKit positionKit: QgsQuick.PositionKit {id: positionKit} property var simulatePositionLongLatRad // use in debug mode to simulate movement around some GPS location // longitude, latitude, and radius, all in degrees WSG84 @@ -35,7 +36,7 @@ Item { property alias mapPosition: wgs84toMapCrs.projectedPosition // in map coordinates property alias gpsPosition: positionKit.position // in WGS84 coordinates property alias gpsAccuracy: positionKit.accuracy // in meters - property alias positionKit: positionKit + property var gpsPositionLabel: { if (positionKit.hasPosition) { QgsQuick.Utils.qgsPointToString(positionKit.position, 3) // e.g -2.243, 45.441 @@ -90,10 +91,6 @@ Item { onProjectedPositionChanged: update_location() } - QgsQuick.PositionKit { - id: positionKit - } - // GPS accuracy circle-shaped indicator around positionMarker Rectangle { id: accuracyIndicator diff --git a/src/quickgui/plugin/qgsquickscalebar.qml b/src/quickgui/plugin/qgsquickscalebar.qml index 9e8ed6f90bdb..7fcbc0ce47ac 100644 --- a/src/quickgui/plugin/qgsquickscalebar.qml +++ b/src/quickgui/plugin/qgsquickscalebar.qml @@ -20,10 +20,7 @@ Item { id: scaleBar property alias mapSettings: scaleBarKit.mapSettings property alias preferredWidth: scaleBarKit.preferredWidth - - QgsQuick.ScaleBarKit { - id: scaleBarKit - } + property QgsQuick.ScaleBarKit scaleBarKit: QgsQuick.ScaleBarKit {id: scaleBarKit} property int textWidth: fontMetrics.averageCharacterWidth * 8 property color barColor: "white" From c907fd8cf17566487317b22e262d5b1da7f3d0b8 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 21 Mar 2018 14:08:09 +0100 Subject: [PATCH 21/31] use QStringLiteral for debug messages --- src/quickgui/qgsquickidentifykit.cpp | 6 +++--- src/quickgui/qgsquickmessagelogmodel.cpp | 2 +- src/quickgui/qgsquickpositionkit.cpp | 2 +- src/quickgui/qgsquickutils.cpp | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/quickgui/qgsquickidentifykit.cpp b/src/quickgui/qgsquickidentifykit.cpp index 9826d81fec38..661c9a9fb179 100644 --- a/src/quickgui/qgsquickidentifykit.cpp +++ b/src/quickgui/qgsquickidentifykit.cpp @@ -80,7 +80,7 @@ QList QgsQuickIdentifyKit::identify( const QPointF &point ) } } - QgsDebugMsg( QString( "IdentifyKit identified %1 results" ).arg( results.count() ) ); + QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results" ).arg( results.count() ) ); return results; } @@ -172,14 +172,14 @@ QgsFeatureList QgsQuickIdentifyKit::identify( QgsVectorLayer *layer, const QPoin if ( !mMapSettings ) { - qWarning() << "Unable to use IdentifyKit without mapSettings property set."; + QgsDebugMsg( QStringLiteral( "Unable to use IdentifyKit without mapSettings property set." ) ); return results; } QgsPointXY mapPoint = mMapSettings->mapSettings().mapToPixel().toMapCoordinates( point.toPoint() ); results = identifyVectorLayer( layer, mapPoint ); - QgsDebugMsg( QString( "IdentifyKit identified %1 results for layer %2" ).arg( results.count() ).arg( layer->name() ) ); + QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results for layer %2" ).arg( results.count() ).arg( layer->name() ) ); return results; } diff --git a/src/quickgui/qgsquickmessagelogmodel.cpp b/src/quickgui/qgsquickmessagelogmodel.cpp index 5c49ccd467bf..c445c42148eb 100644 --- a/src/quickgui/qgsquickmessagelogmodel.cpp +++ b/src/quickgui/qgsquickmessagelogmodel.cpp @@ -64,6 +64,6 @@ void QgsQuickMessageLogModel::onMessageReceived( const QString &message, const Q { beginInsertRows( QModelIndex(), 0, 0 ); mMessages.prepend( LogMessage( tag, message, level ) ); - QgsDebugMsg( QString( "Next message %1 : %2" ).arg( tag ).arg( message ) ); + QgsDebugMsg( QStringLiteral( "Next message %1 : %2" ).arg( tag, message ) ); endInsertRows(); } diff --git a/src/quickgui/qgsquickpositionkit.cpp b/src/quickgui/qgsquickpositionkit.cpp index f703a8b025ae..448414a9a1c6 100644 --- a/src/quickgui/qgsquickpositionkit.cpp +++ b/src/quickgui/qgsquickpositionkit.cpp @@ -91,7 +91,7 @@ void QgsQuickPositionKit::replacePositionSource( QGeoPositionInfoSource *source connect( mSource, SIGNAL( updateTimeout() ), this, SLOT( onUpdateTimeout() ) ); mSource->startUpdates(); - QgsDebugMsg( QString( "Position source changed: %1" ).arg( mSource->sourceName() ) ); + QgsDebugMsg( QStringLiteral( "Position source changed: %1" ).arg( mSource->sourceName() ) ); } } diff --git a/src/quickgui/qgsquickutils.cpp b/src/quickgui/qgsquickutils.cpp index c18e6c996614..fe14cb5f38d4 100644 --- a/src/quickgui/qgsquickutils.cpp +++ b/src/quickgui/qgsquickutils.cpp @@ -38,7 +38,7 @@ QgsQuickUtils *QgsQuickUtils::instance() { if ( !sInstance ) { - QgsDebugMsg( QString( "QgsQuickUtils created: %1" ).arg( long( QThread::currentThreadId() ) ) ); + QgsDebugMsg( QStringLiteral( "QgsQuickUtils created: %1" ).arg( long( QThread::currentThreadId() ) ) ); sInstance = new QgsQuickUtils(); } return sInstance; @@ -134,7 +134,7 @@ void QgsQuickUtils::copyFile( QString sourcePath, QString targetPath ) { if ( !fileExists( sourcePath ) ) { - QgsDebugMsg( QString( "Source file does not exist! %1" ).arg( sourcePath ) ); + QgsDebugMsg( QStringLiteral( "Source file does not exist! %1" ).arg( sourcePath ) ); return; } @@ -149,7 +149,7 @@ void QgsQuickUtils::copyFile( QString sourcePath, QString targetPath ) if ( !QFile( sourcePath ).rename( dir.absoluteFilePath( filename ) ) ) { - QgsDebugMsg( QString( "Couldn't rename file! Trying to copy instead! %1" ).arg( filename ) ); + QgsDebugMsg( QStringLiteral( "Couldn't rename file! Trying to copy instead! %1" ).arg( filename ) ); if ( !QFile( sourcePath ).copy( dir.absoluteFilePath( filename ) ) ) { QgsApplication::messageLog()->logMessage( tr( "File %1 could not be copied to folder %2.", "QgsQuick", Qgis::Critical ).arg( sourcePath, targetPath ) ); @@ -179,7 +179,7 @@ QUrl QgsQuickUtils::getThemeIcon( const QString &name ) { QString extension( ".svg" ); QString path = "qrc:/" + name + extension; - QgsDebugMsg( QString( "Using icon %1 from %2" ).arg( name ).arg( path ) ); + QgsDebugMsg( QStringLiteral( "Using icon %1 from %2" ).arg( name, path ) ); return QUrl( path ); } From c39a56542bed0ee1b1d7016c3268d174d32d8712 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 21 Mar 2018 14:12:11 +0100 Subject: [PATCH 22/31] create proto capture panel in the external resource widget --- src/quickgui/plugin/editor/qgsquickexternalresource.qml | 8 ++++++++ src/quickgui/plugin/qgsquickfeatureform.qml | 9 --------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/quickgui/plugin/editor/qgsquickexternalresource.qml b/src/quickgui/plugin/editor/qgsquickexternalresource.qml index 2c26dc3e5b0e..a2ea40b4a384 100644 --- a/src/quickgui/plugin/editor/qgsquickexternalresource.qml +++ b/src/quickgui/plugin/editor/qgsquickexternalresource.qml @@ -32,6 +32,14 @@ Item { height: Math.max(image.height, button.height) + QgsQuick.PhotoCapture { + id: photoCapturePanel + visible: false + height: window.height + width: window.width + edge: Qt.RightEdge + } + Image { property var currentValue: value diff --git a/src/quickgui/plugin/qgsquickfeatureform.qml b/src/quickgui/plugin/qgsquickfeatureform.qml index 4d793e0c97bc..99d48803c758 100644 --- a/src/quickgui/plugin/qgsquickfeatureform.qml +++ b/src/quickgui/plugin/qgsquickfeatureform.qml @@ -268,7 +268,6 @@ Item { property var field: Field property var constraintValid: ConstraintValid property var homePath: form.project ? form.project.homePath : "" - property var photoCapturePanel: photoPanel active: widget !== 'Hidden' source: 'qgsquick' + widget.toLowerCase() + '.qml' @@ -468,12 +467,4 @@ Item { visible = false } } - - QgsQuick.PhotoCapture { - id: photoPanel - height: window.height - width: window.width - edge: Qt.RightEdge - } - } From a02950ba91a8a32ae123145604f927bbf2ce2ae4 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 21 Mar 2018 14:13:44 +0100 Subject: [PATCH 23/31] restore travis.yml file from master --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b9ce3597f1cd..bb8380168688 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,8 @@ matrix: env: - TRAVIS_CONFIG=linux - DOCKER_COMPOSE=${TRAVIS_BUILD_DIR}/.docker/docker-compose.travis.yml - - DOCKER_TAG=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && ( ( [[ $TRAVIS_EVENT_TYPE =~ pull_request ]] && [[ $TRAVIS_COMMIT_MESSAGE =~ \[dockerdeps\] ]] ) && echo "PR-$TRAVIS_PULL_REQUEST" || echo $TRAVIS_BRANCH | sed 's/master/latest/' ) || echo "latest" ) - - DOCKER_DEPS_PUSH=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && ( [[ $TRAVIS_EVENT_TYPE =~ push ]] || ( [[ $TRAVIS_EVENT_TYPE =~ pull_request ]] && [[ $TRAVIS_COMMIT_MESSAGE =~ \[dockerdeps\] ]] ) ) && echo "true" || echo "false" ) + - DOCKER_TAG=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && echo $TRAVIS_BRANCH | sed 's/master/latest/' || echo "latest" ) + - DOCKER_DEPS_PUSH=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && [[ $TRAVIS_EVENT_TYPE =~ push ]] && echo "true" || echo "false" ) - DOCKER_DEPS_IMAGE_REBUILD=$( [[ $TRAVIS_COMMIT_MESSAGE =~ '[docker] update dependencies' ]] && echo "true" || echo "false" ) # on cron job, QGIS image is built and push without testing - DOCKER_QGIS_IMAGE_BUILD_PUSH=$( [[ $TRAVIS_REPO_SLUG =~ qgis/QGIS ]] && [[ $TRAVIS_EVENT_TYPE =~ cron ]] && echo "true" || echo "false" ) From ebabc4ee5e3a5810d36b42d3bd31ba474b0e3cb0 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Wed, 21 Mar 2018 14:33:32 +0100 Subject: [PATCH 24/31] fix docs and intent --- src/quickgui/qgsquickfeaturemodel.h | 2 ++ src/quickgui/qgsquickhighlightsgnode.h | 4 ++++ src/quickgui/qgsquickutils.h | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/quickgui/qgsquickfeaturemodel.h b/src/quickgui/qgsquickfeaturemodel.h index 60ea2739ad1c..215d674d6b15 100644 --- a/src/quickgui/qgsquickfeaturemodel.h +++ b/src/quickgui/qgsquickfeaturemodel.h @@ -122,7 +122,9 @@ class QUICK_EXPORT QgsQuickFeatureModel : public QAbstractListModel void layerChanged(); protected: + //! commit model changes bool commit(); + //! start editing model bool startEditing(); QgsVectorLayer *mLayer = nullptr; diff --git a/src/quickgui/qgsquickhighlightsgnode.h b/src/quickgui/qgsquickhighlightsgnode.h index 360c43a9f90c..d95ec697d7f0 100644 --- a/src/quickgui/qgsquickhighlightsgnode.h +++ b/src/quickgui/qgsquickhighlightsgnode.h @@ -40,11 +40,15 @@ class QUICK_EXPORT QgsQuickHighlightSGNode : public QSGNode public: //! Create new QT Quick scene node based on geometry QgsQuickHighlightSGNode( const QVector &points, QgsWkbTypes::GeometryType type, const QColor &color, qreal width ); + //! Destroy node virtual ~QgsQuickHighlightSGNode(); protected: + //! Construct line geometry from points QSGGeometryNode *createLineGeometry( const QVector &points, qreal width ); + //! Construct point geometry from qgs point QSGGeometryNode *createPointGeometry( const QgsPoint &point, qreal width ); + //! Construct polygon geometry from points (not implemented) virtual QSGGeometryNode *createPolygonGeometry( const QVector &points ); QSGFlatColorMaterial mMaterial; diff --git a/src/quickgui/qgsquickutils.h b/src/quickgui/qgsquickutils.h index 5401607881d8..4704cf8197ff 100644 --- a/src/quickgui/qgsquickutils.h +++ b/src/quickgui/qgsquickutils.h @@ -84,7 +84,7 @@ class QUICK_EXPORT QgsQuickUtils: public QObject /** * Convert QGeoCoordinate to QgsPoint */ - Q_INVOKABLE QgsPoint coordinateToPoint( const QGeoCoordinate& coor ) const; + Q_INVOKABLE QgsPoint coordinateToPoint( const QGeoCoordinate &coor ) const; /** * Transform point between different crs from QML From c379c4ee00a5f81f6a1e194da2103c4bffe0b205 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 12:07:17 +0200 Subject: [PATCH 25/31] remove unused QDebug imports --- src/quickgui/qgsquickfeaturemodel.cpp | 2 -- src/quickgui/qgsquickmessagelogmodel.cpp | 2 -- src/quickgui/qgsquickscalebarkit.cpp | 1 - src/quickgui/qgsquicksubmodel.cpp | 1 - src/quickgui/qgsquickutils.cpp | 1 - 5 files changed, 7 deletions(-) diff --git a/src/quickgui/qgsquickfeaturemodel.cpp b/src/quickgui/qgsquickfeaturemodel.cpp index 4bb6c1dd3a40..a21aa12d6791 100644 --- a/src/quickgui/qgsquickfeaturemodel.cpp +++ b/src/quickgui/qgsquickfeaturemodel.cpp @@ -13,8 +13,6 @@ * * ***************************************************************************/ -#include - #include "qgis.h" #include "qgsmessagelog.h" #include "qgsvectorlayer.h" diff --git a/src/quickgui/qgsquickmessagelogmodel.cpp b/src/quickgui/qgsquickmessagelogmodel.cpp index c445c42148eb..0652f5693634 100644 --- a/src/quickgui/qgsquickmessagelogmodel.cpp +++ b/src/quickgui/qgsquickmessagelogmodel.cpp @@ -13,8 +13,6 @@ * * ***************************************************************************/ -#include - #include "qgis.h" #include "qgslogger.h" #include "qgsmessagelog.h" diff --git a/src/quickgui/qgsquickscalebarkit.cpp b/src/quickgui/qgsquickscalebarkit.cpp index 0404804c4001..3cc84ca0228e 100644 --- a/src/quickgui/qgsquickscalebarkit.cpp +++ b/src/quickgui/qgsquickscalebarkit.cpp @@ -15,7 +15,6 @@ #include #include -#include #include "qgsdistancearea.h" #include "qgspointxy.h" diff --git a/src/quickgui/qgsquicksubmodel.cpp b/src/quickgui/qgsquicksubmodel.cpp index bdb85b81e566..dbe4d65b00b3 100644 --- a/src/quickgui/qgsquicksubmodel.cpp +++ b/src/quickgui/qgsquicksubmodel.cpp @@ -14,7 +14,6 @@ ***************************************************************************/ #include "qgsquicksubmodel.h" -#include QgsQuickSubModel::QgsQuickSubModel( QObject *parent ) : QAbstractItemModel( parent ) diff --git a/src/quickgui/qgsquickutils.cpp b/src/quickgui/qgsquickutils.cpp index fe14cb5f38d4..4303ab098722 100644 --- a/src/quickgui/qgsquickutils.cpp +++ b/src/quickgui/qgsquickutils.cpp @@ -13,7 +13,6 @@ * * ***************************************************************************/ -#include #include #include #include From beae2904b0b606e49f4725c51cbe6bd0f6170ffb Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 12:15:15 +0200 Subject: [PATCH 26/31] rename Btn to Button --- src/quickgui/plugin/qgsquickfeatureform.qml | 12 ++++---- src/quickgui/plugin/qgsquickphotopanel.qml | 34 ++++++++++----------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/quickgui/plugin/qgsquickfeatureform.qml b/src/quickgui/plugin/qgsquickfeatureform.qml index 99d48803c758..e99a3e9b3408 100644 --- a/src/quickgui/plugin/qgsquickfeatureform.qml +++ b/src/quickgui/plugin/qgsquickfeatureform.qml @@ -37,9 +37,9 @@ Item { property bool allowRememberAttribute: false // when adding new feature, add checkbox to be able to save the same value for the next feature as default property QgsQuick.Project project - property var saveBtnIcon: QgsQuick.Utils.getThemeIcon( "ic_save_white" ) - property var deleteBtnIcon: QgsQuick.Utils.getThemeIcon( "ic_delete_forever_white" ) - property var closeBtnIcon: QgsQuick.Utils.getThemeIcon( "ic_clear_white" ) + property var saveButtonIcon: QgsQuick.Utils.getThemeIcon( "ic_save_white" ) + property var deleteButtonIcon: QgsQuick.Utils.getThemeIcon( "ic_delete_forever_white" ) + property var closeButtonIcon: QgsQuick.Utils.getThemeIcon( "ic_clear_white" ) property FeatureFormStyling style: FeatureFormStyling {} @@ -362,7 +362,7 @@ Item { visible: form.state !== "ReadOnly" contentItem: Image { - source: form.saveBtnIcon + source: form.saveButtonIcon sourceSize: Qt.size(width, height) } @@ -386,7 +386,7 @@ Item { visible: form.state === "Edit" contentItem: Image { - source: form.deleteBtnIcon + source: form.deleteButtonIcon sourceSize: Qt.size(width, height) } @@ -431,7 +431,7 @@ Item { Layout.preferredHeight: form.style.toolbutton.size contentItem: Image { - source: form.closeBtnIcon + source: form.closeButtonIcon sourceSize: Qt.size(width, height) } diff --git a/src/quickgui/plugin/qgsquickphotopanel.qml b/src/quickgui/plugin/qgsquickphotopanel.qml index de4fd6c20191..effbe2ed8040 100644 --- a/src/quickgui/plugin/qgsquickphotopanel.qml +++ b/src/quickgui/plugin/qgsquickphotopanel.qml @@ -31,9 +31,9 @@ Drawer { property color borderColor: "black" // icons: - property var captureBtnIcon: QgsQuick.Utils.getThemeIcon("ic_camera_alt_border") - property var okBtnIcon: QgsQuick.Utils.getThemeIcon("ic_check_black") - property var cancelBtnIcon: QgsQuick.Utils.getThemeIcon("ic_clear_black") + property var captureButtonIcon: QgsQuick.Utils.getThemeIcon("ic_camera_alt_border") + property var okButtonIcon: QgsQuick.Utils.getThemeIcon("ic_check_black") + property var cancelButtonIcon: QgsQuick.Utils.getThemeIcon("ic_clear_black") id: photoPanel @@ -98,7 +98,7 @@ Drawer { autoOrientation: true Rectangle { - id: captureBtn + id: captureButton property int borderWidth: 10 * QgsQuick.Utils.dp width: parent.width/20 height: parent.width/20 @@ -125,12 +125,12 @@ Drawer { } Image { - id: captureBtnImage + id: captureButtonImage fillMode: Image.PreserveAspectFit anchors.centerIn: parent - sourceSize.height: captureBtn.height/2 - height: captureBtn.height/2 - source: photoPanel.captureBtnIcon + sourceSize.height: captureButton.height/2 + height: captureButton.height/2 + source: photoPanel.captureButtonIcon } } @@ -143,7 +143,7 @@ Drawer { // Cancel button Rectangle { - id: cancelBtn + id: cancelButton visible: camera.imageCapture.capturedImagePath != "" property int borderWidth: 10 * QgsQuick.Utils.dp @@ -152,7 +152,7 @@ Drawer { color: photoPanel.bgColor border.color: photoPanel.borderColor anchors.right: parent.right - anchors.top: confirmBtn.bottom + anchors.top: confirmButton.bottom border.width: borderWidth radius: width*0.5 antialiasing: true @@ -171,15 +171,15 @@ Drawer { Image { fillMode: Image.PreserveAspectFit anchors.centerIn: parent - sourceSize.height: captureBtn.height/2 - height: captureBtn.height/2 - source: photoPanel.cancelBtnIcon + sourceSize.height: captureButton.height/2 + height: captureButton.height/2 + source: photoPanel.cancelButtonIcon } } // OK button Rectangle { - id: confirmBtn + id: confirmButton visible: camera.imageCapture.capturedImagePath != "" property int borderWidth: 10 * QgsQuick.Utils.dp @@ -209,9 +209,9 @@ Drawer { Image { fillMode: Image.PreserveAspectFit anchors.centerIn: parent - sourceSize.height: captureBtn.height/2 - height: captureBtn.height/2 - source: photoPanel.okBtnIcon + sourceSize.height: captureButton.height/2 + height: captureButton.height/2 + source: photoPanel.okButtonIcon } } } From bbbb5face63246322ba9b0ca42a3f33b5b6814ad Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 12:56:08 +0200 Subject: [PATCH 27/31] use parent fill --- src/quickgui/plugin/qgsquickfeatureform.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/quickgui/plugin/qgsquickfeatureform.qml b/src/quickgui/plugin/qgsquickfeatureform.qml index e99a3e9b3408..b15e034aaeb8 100644 --- a/src/quickgui/plugin/qgsquickfeatureform.qml +++ b/src/quickgui/plugin/qgsquickfeatureform.qml @@ -164,8 +164,7 @@ Item { * The main form content area */ Rectangle { - width: parent.width - height: parent.height + anchors.fill: parent color: form.style.backgroundColor opacity: form.style.backgroundOpacity } From 0263e2d0e11aabc95ed6a062ae7b70a290d8ee4a Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 13:00:16 +0200 Subject: [PATCH 28/31] add unread property to message log --- src/quickgui/plugin/qgsquickmessagelog.qml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/quickgui/plugin/qgsquickmessagelog.qml b/src/quickgui/plugin/qgsquickmessagelog.qml index 4024f17caa4c..88de4167698d 100644 --- a/src/quickgui/plugin/qgsquickmessagelog.qml +++ b/src/quickgui/plugin/qgsquickmessagelog.qml @@ -22,6 +22,7 @@ Item { property color bgColor: "white" property color separatorColor: "gray" property int separatorSize: 1 * QgsQuick.Utils.dp + property bool unreadMessages: false id: item @@ -53,4 +54,18 @@ Item { } } } + + Connections { + target: model + + onRowsInserted: { + if ( !visible ) + unreadMessages = true + } + } + + onVisibleChanged: { + if ( visible ) + unreadMessages = false + } } From 8ca9b08b10fad91b1b4a9cc595b71f7a981215b8 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 13:26:54 +0200 Subject: [PATCH 29/31] some code cleaning --- src/quickgui/qgsquickattributeformmodel.cpp | 2 +- src/quickgui/qgsquickattributeformmodel.h | 6 +++--- src/quickgui/qgsquickattributeformmodelbase.cpp | 14 +++----------- src/quickgui/qgsquickattributeformmodelbase.h | 10 +++++----- src/quickgui/qgsquickcoordinatetransformer.cpp | 1 - src/quickgui/qgsquickcoordinatetransformer.h | 2 +- src/quickgui/qgsquickfeaturehighlight.cpp | 3 --- src/quickgui/qgsquickfeaturehighlight.h | 6 +++--- src/quickgui/qgsquickidentifykit.cpp | 2 -- src/quickgui/qgsquickidentifykit.h | 4 +--- src/quickgui/qgsquickmapcanvasmap.cpp | 3 --- src/quickgui/qgsquickmapcanvasmap.h | 6 +++--- src/quickgui/qgsquickmapsettings.cpp | 1 - src/quickgui/qgsquickmapsettings.h | 2 +- src/quickgui/qgsquickpositionkit.cpp | 1 - src/quickgui/qgsquickpositionkit.h | 2 +- src/quickgui/qgsquickscalebarkit.cpp | 1 - src/quickgui/qgsquickscalebarkit.h | 2 +- src/quickgui/qgsquicksubmodel.cpp | 1 - src/quickgui/qgsquicksubmodel.h | 2 +- src/quickgui/qgsquickutils.h | 2 +- 21 files changed, 25 insertions(+), 48 deletions(-) diff --git a/src/quickgui/qgsquickattributeformmodel.cpp b/src/quickgui/qgsquickattributeformmodel.cpp index 69396fb9f3ce..041ccd734258 100644 --- a/src/quickgui/qgsquickattributeformmodel.cpp +++ b/src/quickgui/qgsquickattributeformmodel.cpp @@ -62,7 +62,7 @@ void QgsQuickAttributeFormModel::create() mSourceModel->create(); } -QVariant QgsQuickAttributeFormModel::attribute( const QString &name ) +QVariant QgsQuickAttributeFormModel::attribute( const QString &name ) const { return mSourceModel->attribute( name ); } diff --git a/src/quickgui/qgsquickattributeformmodel.h b/src/quickgui/qgsquickattributeformmodel.h index 5c13b756d66e..058373f97851 100644 --- a/src/quickgui/qgsquickattributeformmodel.h +++ b/src/quickgui/qgsquickattributeformmodel.h @@ -52,7 +52,7 @@ class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel public: - //! Feature fieds's roles + //! Feature fields's roles enum FeatureRoles { ElementType = Qt::UserRole + 1, //!< Element type @@ -73,7 +73,7 @@ class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel Q_ENUM( FeatureRoles ) - //! create new attribute form model + //! Create new attribute form model QgsQuickAttributeFormModel( QObject *parent = nullptr ); //! Return whether model has tabs layout @@ -98,7 +98,7 @@ class QUICK_EXPORT QgsQuickAttributeFormModel : public QSortFilterProxyModel Q_INVOKABLE void create(); //! Return attribute value with name - Q_INVOKABLE QVariant attribute( const QString &name ); + Q_INVOKABLE QVariant attribute( const QString &name ) const; signals: //! feature model changed diff --git a/src/quickgui/qgsquickattributeformmodelbase.cpp b/src/quickgui/qgsquickattributeformmodelbase.cpp index 388aab62bccd..a17af5d4f592 100644 --- a/src/quickgui/qgsquickattributeformmodelbase.cpp +++ b/src/quickgui/qgsquickattributeformmodelbase.cpp @@ -23,16 +23,9 @@ QgsQuickAttributeFormModelBase::QgsQuickAttributeFormModelBase( QObject *parent ) : QStandardItemModel( 0, 1, parent ) - , mFeatureModel( nullptr ) - , mLayer( nullptr ) - , mTemporaryContainer( nullptr ) { } -QgsQuickAttributeFormModelBase::~QgsQuickAttributeFormModelBase() -{ - delete mTemporaryContainer; -} QHash QgsQuickAttributeFormModelBase::roleNames() const { @@ -127,7 +120,6 @@ void QgsQuickAttributeFormModelBase::onLayerChanged() if ( mLayer ) { QgsAttributeEditorContainer *root; - delete mTemporaryContainer; mTemporaryContainer = nullptr; if ( mLayer->editFormConfig().layout() == QgsEditFormConfig::TabLayout ) @@ -137,7 +129,7 @@ void QgsQuickAttributeFormModelBase::onLayerChanged() else { root = generateRootContainer(); //#spellok - mTemporaryContainer = root; + mTemporaryContainer.reset( root ); } setHasTabs( !root->children().isEmpty() && QgsAttributeEditorElement::AeTypeContainer == root->children().first()->type() ); @@ -204,7 +196,7 @@ QgsAttributeEditorContainer *QgsQuickAttributeFormModelBase::generateRootContain QgsAttributeEditorContainer *QgsQuickAttributeFormModelBase::invisibleRootContainer() const { - return mTemporaryContainer ? mTemporaryContainer : mLayer->editFormConfig().invisibleRootContainer(); + return mTemporaryContainer ? mTemporaryContainer.get() : mLayer->editFormConfig().invisibleRootContainer(); } void QgsQuickAttributeFormModelBase::updateAttributeValue( QStandardItem *item ) @@ -348,7 +340,7 @@ bool QgsQuickAttributeFormModelBase::constraintsValid() const return mConstraintsValid; } -QVariant QgsQuickAttributeFormModelBase::attribute( const QString &name ) +QVariant QgsQuickAttributeFormModelBase::attribute( const QString &name ) const { if ( !mLayer ) return QVariant(); diff --git a/src/quickgui/qgsquickattributeformmodelbase.h b/src/quickgui/qgsquickattributeformmodelbase.h index 5ab1f018fd8c..632bc448f968 100644 --- a/src/quickgui/qgsquickattributeformmodelbase.h +++ b/src/quickgui/qgsquickattributeformmodelbase.h @@ -54,7 +54,7 @@ class QgsQuickAttributeFormModelBase : public QStandardItemModel public: explicit QgsQuickAttributeFormModelBase( QObject *parent = nullptr ); - ~QgsQuickAttributeFormModelBase(); + ~QgsQuickAttributeFormModelBase() = default; QHash roleNames() const override; @@ -72,7 +72,7 @@ class QgsQuickAttributeFormModelBase : public QStandardItemModel bool constraintsValid() const; - QVariant attribute( const QString &name ); + QVariant attribute( const QString &name ) const; signals: void featureModelChanged(); @@ -102,9 +102,9 @@ class QgsQuickAttributeFormModelBase : public QStandardItemModel void setConstraintsValid( bool constraintsValid ); - QgsQuickFeatureModel *mFeatureModel; - QgsVectorLayer *mLayer; - QgsAttributeEditorContainer *mTemporaryContainer; + QgsQuickFeatureModel *mFeatureModel = nullptr; + QgsVectorLayer *mLayer = nullptr; + std::unique_ptr mTemporaryContainer; bool mHasTabs; typedef QPair > VisibilityExpression; diff --git a/src/quickgui/qgsquickcoordinatetransformer.cpp b/src/quickgui/qgsquickcoordinatetransformer.cpp index 2572ab05ab4f..eb1fc5cd9a6d 100644 --- a/src/quickgui/qgsquickcoordinatetransformer.cpp +++ b/src/quickgui/qgsquickcoordinatetransformer.cpp @@ -19,7 +19,6 @@ QgsQuickCoordinateTransformer::QgsQuickCoordinateTransformer( QObject *parent ) : QObject( parent ) - , mMapSettings( nullptr ) { mCoordinateTransform.setSourceCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ) ); mCoordinateTransform.setContext( QgsCoordinateTransformContext() ); diff --git a/src/quickgui/qgsquickcoordinatetransformer.h b/src/quickgui/qgsquickcoordinatetransformer.h index 79192b7e5c0d..2a8924f0658b 100644 --- a/src/quickgui/qgsquickcoordinatetransformer.h +++ b/src/quickgui/qgsquickcoordinatetransformer.h @@ -97,7 +97,7 @@ class QUICK_EXPORT QgsQuickCoordinateTransformer : public QObject QgsPoint mProjectedPosition; QgsPoint mSourcePosition; QgsCoordinateTransform mCoordinateTransform; - QgsQuickMapSettings *mMapSettings; + QgsQuickMapSettings *mMapSettings = nullptr; }; #endif // QGSQUICKCOORDINATETRANSFORMER_H diff --git a/src/quickgui/qgsquickfeaturehighlight.cpp b/src/quickgui/qgsquickfeaturehighlight.cpp index 1b0d251a70e3..f84429416be8 100644 --- a/src/quickgui/qgsquickfeaturehighlight.cpp +++ b/src/quickgui/qgsquickfeaturehighlight.cpp @@ -23,9 +23,6 @@ QgsQuickFeatureHighlight::QgsQuickFeatureHighlight( QQuickItem *parent ) : QQuickItem( parent ) - , mModel( nullptr ) - , mDirty( false ) - , mMapSettings( nullptr ) { setFlags( QQuickItem::ItemHasContents ); setAntialiasing( true ); diff --git a/src/quickgui/qgsquickfeaturehighlight.h b/src/quickgui/qgsquickfeaturehighlight.h index 8c3f8faa5eed..3280291d4ddb 100644 --- a/src/quickgui/qgsquickfeaturehighlight.h +++ b/src/quickgui/qgsquickfeaturehighlight.h @@ -75,10 +75,10 @@ class QUICK_EXPORT QgsQuickFeatureHighlight : public QQuickItem virtual QSGNode *updatePaintNode( QSGNode *n, UpdatePaintNodeData * ) override; QColor mColor; - QgsQuickFeatureModel *mModel; - bool mDirty; + QgsQuickFeatureModel *mModel = nullptr; + bool mDirty = false; unsigned int mWidth; - QgsQuickMapSettings *mMapSettings; + QgsQuickMapSettings *mMapSettings = nullptr; }; #endif // QGSQUICKFEATUREHIGHLIGHT_H diff --git a/src/quickgui/qgsquickidentifykit.cpp b/src/quickgui/qgsquickidentifykit.cpp index 661c9a9fb179..c6d4a39941cc 100644 --- a/src/quickgui/qgsquickidentifykit.cpp +++ b/src/quickgui/qgsquickidentifykit.cpp @@ -13,7 +13,6 @@ * * ***************************************************************************/ - #include "qgsproject.h" #include "qgslogger.h" #include "qgsrenderer.h" @@ -24,7 +23,6 @@ QgsQuickIdentifyKit::QgsQuickIdentifyKit( QObject *parent ) : QObject( parent ) - , mMapSettings( nullptr ) , mSearchRadiusMm( 8 ) , mFeaturesLimit( 100 ) { diff --git a/src/quickgui/qgsquickidentifykit.h b/src/quickgui/qgsquickidentifykit.h index 61a32b66d85c..5abea91d5680 100644 --- a/src/quickgui/qgsquickidentifykit.h +++ b/src/quickgui/qgsquickidentifykit.h @@ -27,7 +27,6 @@ #include "qgis_quick.h" #include "qgsquickfeature.h" -class QgsQuickProject; class QgsMapLayer; class QgsQuickMapSettings; class QgsVectorLayer; @@ -103,8 +102,7 @@ class QUICK_EXPORT QgsQuickIdentifyKit : public QObject void featuresLimitChanged(); private: - QgsQuickProject *mProject; - QgsQuickMapSettings *mMapSettings; + QgsQuickMapSettings *mMapSettings = nullptr; double searchRadiusMU( const QgsRenderContext &context ) const; double searchRadiusMU() const; diff --git a/src/quickgui/qgsquickmapcanvasmap.cpp b/src/quickgui/qgsquickmapcanvasmap.cpp index 4ee8ad39cdaf..6db762e3f1c6 100644 --- a/src/quickgui/qgsquickmapcanvasmap.cpp +++ b/src/quickgui/qgsquickmapcanvasmap.cpp @@ -32,9 +32,6 @@ QgsQuickMapCanvasMap::QgsQuickMapCanvasMap( QQuickItem *parent ) : QQuickItem( parent ) , mMapSettings( new QgsQuickMapSettings() ) , mPinching( false ) - , mJob( nullptr ) - , mCache( nullptr ) - , mLabelingResults( nullptr ) , mDirty( false ) , mFreeze( false ) , mIncrementalRendering( false ) diff --git a/src/quickgui/qgsquickmapcanvasmap.h b/src/quickgui/qgsquickmapcanvasmap.h index 1209383bb794..c4b54625731e 100644 --- a/src/quickgui/qgsquickmapcanvasmap.h +++ b/src/quickgui/qgsquickmapcanvasmap.h @@ -156,9 +156,9 @@ class QUICK_EXPORT QgsQuickMapCanvasMap : public QQuickItem bool mPinching; QPoint mPinchStartPoint; - QgsMapRendererParallelJob *mJob; - QgsMapRendererCache *mCache; - QgsLabelingResults *mLabelingResults; + QgsMapRendererParallelJob *mJob = nullptr; + QgsMapRendererCache *mCache = nullptr; + QgsLabelingResults *mLabelingResults = nullptr; QImage mImage; QgsMapSettings mImageMapSettings; QTimer mRefreshTimer; diff --git a/src/quickgui/qgsquickmapsettings.cpp b/src/quickgui/qgsquickmapsettings.cpp index 0c4cd7a16e67..363a955ad963 100644 --- a/src/quickgui/qgsquickmapsettings.cpp +++ b/src/quickgui/qgsquickmapsettings.cpp @@ -23,7 +23,6 @@ QgsQuickMapSettings::QgsQuickMapSettings( QObject *parent ) : QObject( parent ) - , mProject( 0 ) { // Connect signals for derived values connect( this, &QgsQuickMapSettings::destinationCrsChanged, this, &QgsQuickMapSettings::mapUnitsPerPixelChanged ); diff --git a/src/quickgui/qgsquickmapsettings.h b/src/quickgui/qgsquickmapsettings.h index caa50f9f042e..cfae5652b3d1 100644 --- a/src/quickgui/qgsquickmapsettings.h +++ b/src/quickgui/qgsquickmapsettings.h @@ -224,7 +224,7 @@ class QUICK_EXPORT QgsQuickMapSettings : public QObject void onReadProject( const QDomDocument &doc ); private: - QgsProject *mProject; + QgsProject *mProject = nullptr; QgsMapSettings mMapSettings; }; diff --git a/src/quickgui/qgsquickpositionkit.cpp b/src/quickgui/qgsquickpositionkit.cpp index 448414a9a1c6..be7cfcdb2c99 100644 --- a/src/quickgui/qgsquickpositionkit.cpp +++ b/src/quickgui/qgsquickpositionkit.cpp @@ -26,7 +26,6 @@ QgsQuickPositionKit::QgsQuickPositionKit( QObject *parent ) , mDirection( -1 ) , mHasPosition( false ) , mIsSimulated( false ) - , mSource( nullptr ) { use_gps_location(); } diff --git a/src/quickgui/qgsquickpositionkit.h b/src/quickgui/qgsquickpositionkit.h index 638404e9a26d..dd26a51ef885 100644 --- a/src/quickgui/qgsquickpositionkit.h +++ b/src/quickgui/qgsquickpositionkit.h @@ -128,7 +128,7 @@ class QUICK_EXPORT QgsQuickPositionKit : public QObject // Simulated source bool mIsSimulated; - QGeoPositionInfoSource *mSource; + QGeoPositionInfoSource *mSource = nullptr; private: void replacePositionSource( QGeoPositionInfoSource *source ); diff --git a/src/quickgui/qgsquickscalebarkit.cpp b/src/quickgui/qgsquickscalebarkit.cpp index 3cc84ca0228e..d8173742f6b9 100644 --- a/src/quickgui/qgsquickscalebarkit.cpp +++ b/src/quickgui/qgsquickscalebarkit.cpp @@ -25,7 +25,6 @@ QgsQuickScaleBarKit::QgsQuickScaleBarKit( QObject *parent ) : QObject( parent ) - , mMapSettings( 0 ) , mPreferredWidth( 300 ) , mWidth( mPreferredWidth ) , mDistance( 0 ) diff --git a/src/quickgui/qgsquickscalebarkit.h b/src/quickgui/qgsquickscalebarkit.h index 67a9b4a9afc4..561b927afdf0 100644 --- a/src/quickgui/qgsquickscalebarkit.h +++ b/src/quickgui/qgsquickscalebarkit.h @@ -110,7 +110,7 @@ class QUICK_EXPORT QgsQuickScaleBarKit : public QObject void updateScaleBar(); private: - QgsQuickMapSettings *mMapSettings; + QgsQuickMapSettings *mMapSettings = nullptr; int mPreferredWidth; // pixels int mWidth; // pixels diff --git a/src/quickgui/qgsquicksubmodel.cpp b/src/quickgui/qgsquicksubmodel.cpp index dbe4d65b00b3..806433c92f88 100644 --- a/src/quickgui/qgsquicksubmodel.cpp +++ b/src/quickgui/qgsquicksubmodel.cpp @@ -17,7 +17,6 @@ QgsQuickSubModel::QgsQuickSubModel( QObject *parent ) : QAbstractItemModel( parent ) - , mModel( 0 ) { } diff --git a/src/quickgui/qgsquicksubmodel.h b/src/quickgui/qgsquicksubmodel.h index 9cfbe41ef82f..61806e3c50e9 100644 --- a/src/quickgui/qgsquicksubmodel.h +++ b/src/quickgui/qgsquicksubmodel.h @@ -96,7 +96,7 @@ class QUICK_EXPORT QgsQuickSubModel : public QAbstractItemModel QModelIndex mapFromSource( const QModelIndex &sourceIndex ) const; QModelIndex mapToSource( const QModelIndex &index ) const; - QAbstractItemModel *mModel; + QAbstractItemModel *mModel = nullptr; QPersistentModelIndex mRootIndex; // Map internal id to parent index diff --git a/src/quickgui/qgsquickutils.h b/src/quickgui/qgsquickutils.h index 4704cf8197ff..d717280a02ba 100644 --- a/src/quickgui/qgsquickutils.h +++ b/src/quickgui/qgsquickutils.h @@ -142,7 +142,7 @@ class QUICK_EXPORT QgsQuickUtils: public QObject signals: private: - explicit QgsQuickUtils( QObject *parent = 0 ); + explicit QgsQuickUtils( QObject *parent = nullptr ); ~QgsQuickUtils(); static QgsQuickUtils *sInstance; From 35bd7e6b064164e02aab7772ea840f619fd81aa5 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 15:01:54 +0200 Subject: [PATCH 30/31] code cleanups --- .../plugin/qgsquickpositionmarker.qml | 4 +-- src/quickgui/qgsquickfeaturemodel.cpp | 3 --- src/quickgui/qgsquickscalebarkit.cpp | 5 ++-- src/quickgui/qgsquickscalebarkit.h | 4 +-- tests/src/quickgui/app/main.cpp | 26 +++++++++---------- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/quickgui/plugin/qgsquickpositionmarker.qml b/src/quickgui/plugin/qgsquickpositionmarker.qml index e04f332684f8..33ac7561997a 100644 --- a/src/quickgui/plugin/qgsquickpositionmarker.qml +++ b/src/quickgui/plugin/qgsquickpositionmarker.qml @@ -41,7 +41,7 @@ Item { if (positionKit.hasPosition) { QgsQuick.Utils.qgsPointToString(positionKit.position, 3) // e.g -2.243, 45.441 } else { - "GPS signal lost" + qsTr( "GPS signal lost" ).arg( layerName ) } } property var gpsAccuracyLabel: { @@ -49,7 +49,7 @@ Item { if (positionKit.hasPosition && positionKit.accuracy > 0) { QgsQuick.Utils.distanceToString(positionKit.accuracy, 0) // e.g 1 km or 15 m or 500 mm } else { - "GPS accuracy N/A" + qsTr( "GPS accuracy N/A" ) } } else { "" diff --git a/src/quickgui/qgsquickfeaturemodel.cpp b/src/quickgui/qgsquickfeaturemodel.cpp index a21aa12d6791..e6c75dc83286 100644 --- a/src/quickgui/qgsquickfeaturemodel.cpp +++ b/src/quickgui/qgsquickfeaturemodel.cpp @@ -85,9 +85,6 @@ int QgsQuickFeatureModel::rowCount( const QModelIndex &parent ) const QVariant QgsQuickFeatureModel::data( const QModelIndex &index, int role ) const { - if ( mLayer ) - qWarning() << "Get data " << mLayer->name(); - switch ( role ) { case AttributeName: diff --git a/src/quickgui/qgsquickscalebarkit.cpp b/src/quickgui/qgsquickscalebarkit.cpp index d8173742f6b9..83eff34abcbd 100644 --- a/src/quickgui/qgsquickscalebarkit.cpp +++ b/src/quickgui/qgsquickscalebarkit.cpp @@ -22,6 +22,7 @@ #include "qgsquickmapsettings.h" #include "qgsquickscalebarkit.h" #include "qgsquickutils.h" +#include "qgsunittypes.h" QgsQuickScaleBarKit::QgsQuickScaleBarKit( QObject *parent ) : QObject( parent ) @@ -88,11 +89,11 @@ void QgsQuickScaleBarKit::updateScaleBar() if ( dist > 1000.0 ) { dist = dist / 1000.0; // meters to kilometers - mUnits = "km"; + mUnits = QgsUnitTypes::toString( QgsUnitTypes::DistanceKilometers ); } else { - mUnits = "m"; + mUnits = QgsUnitTypes::toString( QgsUnitTypes::DistanceMeters ); } // we want to show nice round distances e.g. 200 km instead of e.g. 273 km diff --git a/src/quickgui/qgsquickscalebarkit.h b/src/quickgui/qgsquickscalebarkit.h index 561b927afdf0..27332dcf0165 100644 --- a/src/quickgui/qgsquickscalebarkit.h +++ b/src/quickgui/qgsquickscalebarkit.h @@ -72,8 +72,8 @@ class QUICK_EXPORT QgsQuickScaleBarKit : public QObject public: //! create new scale bar kit - explicit QgsQuickScaleBarKit( QObject *parent = 0 ); - ~QgsQuickScaleBarKit(); + explicit QgsQuickScaleBarKit( QObject *parent = nullptr ); + ~QgsQuickScaleBarKit() = default; //! Set map settings void setMapSettings( QgsQuickMapSettings *mapSettings ); diff --git a/tests/src/quickgui/app/main.cpp b/tests/src/quickgui/app/main.cpp index ae7693f4c717..e97b0d542342 100644 --- a/tests/src/quickgui/app/main.cpp +++ b/tests/src/quickgui/app/main.cpp @@ -13,7 +13,6 @@ * * ***************************************************************************/ -#include #include #include #include @@ -26,7 +25,7 @@ #include "qgslayertree.h" #include "qgsmessagelog.h" #include "qgsquickutils.h" - +#include "qgslogger.h" int main( int argc, char *argv[] ) { @@ -49,7 +48,7 @@ int main( int argc, char *argv[] ) // 2) Load QGIS Project QString dataDir( TEST_DATA_DIR ); // defined in CMakeLists.txt QString projectFile = dataDir + "/quickapp_project.qgs"; - qDebug() << "project file: " << projectFile; + QgsDebugMsg( QStringLiteral( "project file: %1" ).arg( projectFile ) ); QgsProject project; bool res = project.read( projectFile ); Q_ASSERT( res ); @@ -68,22 +67,23 @@ int main( int argc, char *argv[] ) if ( !component.errors().isEmpty() ) { - qDebug( "%s", QgsApplication::showSettings().toLocal8Bit().data() ); + QgsDebugMsg( QStringLiteral( "%s" ).arg( QgsApplication::showSettings().toLocal8Bit().data() ) ); - qDebug() << "****************************************"; - qDebug() << "***** QML errors: *****"; - qDebug() << "****************************************"; - for ( const QQmlError &error : component.errors() ) + QgsDebugMsg( QStringLiteral( "****************************************" ) ); + QgsDebugMsg( QStringLiteral( "***** QML errors: *****" ) ); + QgsDebugMsg( QStringLiteral( "****************************************" ) ); + const QList errors = component.errors(); + for ( const QQmlError &error : errors ) { - qDebug() << " " << error; + QgsDebugMsg( error.toString() ); } - qDebug() << "****************************************"; - qDebug() << "****************************************"; + QgsDebugMsg( QStringLiteral( "****************************************" ) ); + QgsDebugMsg( QStringLiteral( "****************************************" ) ); } - if ( object == 0 ) + if ( object == nullptr ) { - qDebug() << "FATAL ERROR: unable to create main.qml"; + QgsDebugMsg( QStringLiteral( "FATAL ERROR: unable to create main.qml" ) ); return EXIT_FAILURE; } From 5323dd6cb1bb6d7a1950621c3c46b85f5febb6da Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Mon, 23 Apr 2018 08:53:44 +0200 Subject: [PATCH 31/31] use new qt signals --- src/quickgui/qgsquickmapcanvasmap.cpp | 2 +- src/quickgui/qgsquickpositionkit.cpp | 11 ++++----- src/quickgui/qgsquickscalebarkit.cpp | 23 +++++++++---------- .../qgsquicksimulatedpositionsource.cpp | 2 +- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/quickgui/qgsquickmapcanvasmap.cpp b/src/quickgui/qgsquickmapcanvasmap.cpp index 6db762e3f1c6..ef942c960dda 100644 --- a/src/quickgui/qgsquickmapcanvasmap.cpp +++ b/src/quickgui/qgsquickmapcanvasmap.cpp @@ -183,7 +183,7 @@ void QgsQuickMapCanvasMap::renderJobFinished() void QgsQuickMapCanvasMap::onWindowChanged( QQuickWindow *window ) { - disconnect( this, SLOT( onScreenChanged( QScreen * ) ) ); + disconnect( window, &QQuickWindow::screenChanged, this, &QgsQuickMapCanvasMap::onScreenChanged ); if ( window ) { connect( window, &QQuickWindow::screenChanged, this, &QgsQuickMapCanvasMap::onScreenChanged ); diff --git a/src/quickgui/qgsquickpositionkit.cpp b/src/quickgui/qgsquickpositionkit.cpp index be7cfcdb2c99..21a653b299bb 100644 --- a/src/quickgui/qgsquickpositionkit.cpp +++ b/src/quickgui/qgsquickpositionkit.cpp @@ -42,7 +42,7 @@ QGeoPositionInfoSource *QgsQuickPositionKit::gpsSource() , "QgsQuick" , Qgis::Warning ); delete source; - return 0; + return nullptr; } else { @@ -76,18 +76,17 @@ void QgsQuickPositionKit::replacePositionSource( QGeoPositionInfoSource *source if ( mSource ) { - disconnect( mSource, 0, this, 0 ); + disconnect( mSource, nullptr, this, nullptr ); delete mSource; - mSource = 0; + mSource = nullptr; } mSource = source; if ( mSource ) { - connect( mSource, SIGNAL( positionUpdated( QGeoPositionInfo ) ), - this, SLOT( positionUpdated( QGeoPositionInfo ) ) ); - connect( mSource, SIGNAL( updateTimeout() ), this, SLOT( onUpdateTimeout() ) ); + connect( mSource, &QGeoPositionInfoSource::positionUpdated, this, &QgsQuickPositionKit::positionUpdated ); + connect( mSource, &QGeoPositionInfoSource::updateTimeout, this, &QgsQuickPositionKit::onUpdateTimeout ); mSource->startUpdates(); QgsDebugMsg( QStringLiteral( "Position source changed: %1" ).arg( mSource->sourceName() ) ); diff --git a/src/quickgui/qgsquickscalebarkit.cpp b/src/quickgui/qgsquickscalebarkit.cpp index 83eff34abcbd..9dbc4c11f4bd 100644 --- a/src/quickgui/qgsquickscalebarkit.cpp +++ b/src/quickgui/qgsquickscalebarkit.cpp @@ -31,11 +31,10 @@ QgsQuickScaleBarKit::QgsQuickScaleBarKit( QObject *parent ) , mDistance( 0 ) , mUnits( "" ) { - connect( this, SIGNAL( mapSettingsChanged() ), this, SLOT( updateScaleBar() ) ); - connect( this, SIGNAL( preferredWidthChanged() ), this, SLOT( updateScaleBar() ) ); + connect( this, &QgsQuickScaleBarKit::mapSettingsChanged, this, &QgsQuickScaleBarKit::updateScaleBar ); + connect( this, &QgsQuickScaleBarKit::preferredWidthChanged, this, &QgsQuickScaleBarKit::updateScaleBar ); } -QgsQuickScaleBarKit::~QgsQuickScaleBarKit() {} void QgsQuickScaleBarKit::setMapSettings( QgsQuickMapSettings *mapSettings ) @@ -46,7 +45,7 @@ void QgsQuickScaleBarKit::setMapSettings( QgsQuickMapSettings *mapSettings ) // If we have already something connected, disconnect it! if ( mMapSettings ) { - disconnect( mMapSettings, 0, this, 0 ); + disconnect( mMapSettings, nullptr, this, nullptr ); } mMapSettings = mapSettings; @@ -54,12 +53,12 @@ void QgsQuickScaleBarKit::setMapSettings( QgsQuickMapSettings *mapSettings ) // Connect all signals to change scale bar when needed! if ( mMapSettings ) { - connect( mMapSettings, SIGNAL( extentChanged() ), this, SLOT( updateScaleBar() ) ); - connect( mMapSettings, SIGNAL( destinationCrsChanged() ), this, SLOT( updateScaleBar() ) ); - connect( mMapSettings, SIGNAL( mapUnitsPerPixelChanged() ), this, SLOT( updateScaleBar() ) ); - connect( mMapSettings, SIGNAL( visibleExtentChanged() ), this, SLOT( updateScaleBar() ) ); - connect( mMapSettings, SIGNAL( outputSizeChanged() ), this, SLOT( updateScaleBar() ) ); - connect( mMapSettings, SIGNAL( outputDpiChanged() ), this, SLOT( updateScaleBar() ) ); + connect( mMapSettings, &QgsQuickMapSettings::extentChanged, this, &QgsQuickScaleBarKit::updateScaleBar ); + connect( mMapSettings, &QgsQuickMapSettings::destinationCrsChanged, this, &QgsQuickScaleBarKit::updateScaleBar ); + connect( mMapSettings, &QgsQuickMapSettings::mapUnitsPerPixelChanged, this, &QgsQuickScaleBarKit::updateScaleBar ); + connect( mMapSettings, &QgsQuickMapSettings::visibleExtentChanged, this, &QgsQuickScaleBarKit::updateScaleBar ); + connect( mMapSettings, &QgsQuickMapSettings::outputSizeChanged, this, &QgsQuickScaleBarKit::updateScaleBar ); + connect( mMapSettings, &QgsQuickMapSettings::outputDpiChanged, this, &QgsQuickScaleBarKit::updateScaleBar ); } emit mapSettingsChanged(); @@ -89,11 +88,11 @@ void QgsQuickScaleBarKit::updateScaleBar() if ( dist > 1000.0 ) { dist = dist / 1000.0; // meters to kilometers - mUnits = QgsUnitTypes::toString( QgsUnitTypes::DistanceKilometers ); + mUnits = QgsUnitTypes::toAbbreviatedString( QgsUnitTypes::DistanceKilometers ); } else { - mUnits = QgsUnitTypes::toString( QgsUnitTypes::DistanceMeters ); + mUnits = QgsUnitTypes::toAbbreviatedString( QgsUnitTypes::DistanceMeters ); } // we want to show nice round distances e.g. 200 km instead of e.g. 273 km diff --git a/src/quickgui/qgsquicksimulatedpositionsource.cpp b/src/quickgui/qgsquicksimulatedpositionsource.cpp index 1443f8280222..b2a99c526a2f 100644 --- a/src/quickgui/qgsquicksimulatedpositionsource.cpp +++ b/src/quickgui/qgsquicksimulatedpositionsource.cpp @@ -25,7 +25,7 @@ QgsQuickSimulatedPositionSource::QgsQuickSimulatedPositionSource( QObject *paren , mLongitude( longitude ) , mLatitude( latitude ) { - connect( mTimer, SIGNAL( timeout() ), this, SLOT( readNextPosition() ) ); + connect( mTimer, &QTimer::timeout, this, &QgsQuickSimulatedPositionSource::readNextPosition ); } void QgsQuickSimulatedPositionSource::startUpdates()