[FEATURE] Introduction of QGIS Quick library

This pull request is a subset of #6490

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

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:
qgis/QGIS-Enhancement-Proposals#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!
This commit is contained in:
Peter Petrik 2018-04-26 13:33:48 +02:00
parent f8165888e4
commit b1bf9b2809
37 changed files with 3287 additions and 3 deletions

View File

@ -40,6 +40,7 @@ echo "${bold}Running cmake...${endbold}"
cmake \
-GNinja \
-DUSE_CCACHE=OFF \
-DWITH_QUICK=ON \
-DWITH_3D=ON \
-DWITH_STAGED_PLUGINS=ON \
-DWITH_GRASS=OFF \

View File

@ -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 \
@ -77,6 +83,8 @@ RUN apt-get update \
qt3d-scene2d-plugin \
qt5keychain-dev \
qtbase5-dev \
qtdeclarative5-dev-tools \
qtdeclarative5-qtquick2-plugin \
qtpositioning5-dev \
qttools5-dev \
qttools5-dev-tools \

2
.gitignore vendored
View File

@ -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

View File

@ -71,6 +71,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)
@ -322,6 +324,18 @@ 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")
# following variable is used in qgsconfig.h
SET (HAVE_QUICK TRUE)
ENDIF (WITH_QUICK)
IF(WITH_QTWEBKIT)
SET(OPTIONAL_QTWEBKIT ${Qt5WebKitWidgets_LIBRARIES})
@ -373,6 +387,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
@ -513,6 +530,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)
@ -583,6 +601,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)
@ -610,6 +629,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)
@ -658,6 +678,7 @@ 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")
@ -673,6 +694,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})

View File

@ -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 <zilolv at gmail dot com>
# 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)

View File

@ -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}"
@ -64,5 +65,7 @@
#cmakedefine QGISDEBUG
#cmakedefine HAVE_QUICK
#endif

View File

@ -100,6 +100,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)

View File

@ -71,6 +71,7 @@ Nikos Alexandris
Paolo Cavallini
Paul Blottiere
Paul Ramsey
Peter Petrik
Pierre Auckenthaler
Raymond Nijssen
Richard Duivenvoorde

View File

@ -45,6 +45,10 @@ website:
<a href="https://qgis.org/api/1.7">1.7</a> and
<a href="https://qgis.org/api/1.6">1.6</a>
\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 <a

View File

@ -37,3 +37,8 @@ Contains classes related to implementation of QGIS plugins.
*/
/** @defgroup quick QgsQuick library
The QgsQuick library is built on top of the CORE library and Qt Quick/QML framework. It adds reusable GUI Quick Components, mainly for mobile devices.
*/

61
doc/qgsquick.dox Normal file
View File

@ -0,0 +1,61 @@
/*! \page qgsquick QGIS Quick Documentation
\tableofcontents
\section qgsquick_overview Overview
QGIS Quick is a QT Quick based GUI library primarily for mobile/tablet devices. Covering basic GIS components (e.g. MapCanvas, Scalebar),
it simplifies creation of a mobile applications for surveys, data gathering or other on-site work. Qt Quick provides tools
to create a rich application with a fluid and dynamic user interface. Additionally, Qt Quick Controls 2 provides highly
optimized controls for embedded/mobile devices with limited resources.
QGIS Quick consists of a Qt plugin that provides the QML components and of a shared library that can be used from C++ code.
\subsection qgsquick_overview_widgets QML Classes
\subsubsection qgsquick_overview_widgets_mapcanvas MapCanvas
\section qgsquick_styling Styling
Since the QGIS Quick library is meant to be reusable for a wide variety of applications with different styles/themes of the user
interface, some effort has been done to allow developers customize the colors and layouts of the components. Individual
components either have attributes for customization (e.g. ScaleBar has "barColor", "barBackgroundColor" properties) or more complex
components accept a custom styling object (e.g. FeatureForm has "style" property of type FeatureFormStyling with a hierarchy of color
and layout properties).
\section qgsquick_versioning_api Versioning and API stability
QML engine supports versioning of individual components with libraries - a single component may be available in multiple versions
with slightly different set of features. This is allows QML libraries to keep API compatibility with older code.
QGIS Quick library is currently in version 0.1 and since it is still a very new library, there are no API stability guarantees:
the following releases of QGIS may ship updates to components while keeping the same version or even remove some components.
Over time we expect that as the library will become stable, we will deliver stable API just like with the other QGIS libraries.
\section qgsquick_gui Designing scalable applications
Qt Quick uses pixel sizes for the visual items. When building applications that may run on devices with varying screen DPI,
this is a problem as the absolute pixel values make the application look different depending on the screen pixel density.
We recommend to use values device independent pixels ("dp"). It is a concept used on mobile devices, where an item of width of 10dp
will have always the same physical size (e.g. in millimeters) regardless of the screen density. To set width of an item to 10dp
in QML, one would write: "width: 10 * QgsQuick.Utils.dp".
\section qgsquick_lib Building the library
The QGIS Quick library is not built by default because QGIS application currently does not use it. In order to build the library
please make sure that WITH_QUICK variable in CMake configuration is set to ON.
It is recommended to build with CMake variable ENABLE_TESTS set to ON because that will also build a small example application
that uses Qt Quick components. In the generated project you should see target "qgis_quickapp".
The built QML plugin is installed to a dedicated directory - see QgsApplication::qmlImportPath(). When using QGIS Quick components,
it is necessary to either use QQmlEngine::addImportPath() to add that directory or to specify QML2_IMPORT_PATH environment variable.
\section qgsquick_demo_app Demo application
A demo application with some basic componets and functionality is available on https://github.com/lutraconsulting/qgis-quick-demo-app
The demo application repository contains also instructions on how to build the application, QGIS Quick and other dependencies on Android.
*/

View File

@ -432,6 +432,14 @@ Returns the path containing qgis_core, qgis_gui, qgispython (and other) librarie
static QString libexecPath();
%Docstring
Returns the path with utility executables (help viewer, crssync, ...)
%End
static QString qmlImportPath();
%Docstring
Returns the path where QML components are installed for QGIS Quick library. Returns
empty string when QGIS is built without Quick support
.. versionadded:: 3.2
%End
static void setPrefixPath( const QString &prefixPath, bool useDefaultPaths = false );

View File

@ -35,3 +35,6 @@ IF (WITH_CUSTOM_WIDGETS)
ADD_SUBDIRECTORY(customwidgets)
ENDIF (WITH_CUSTOM_WIDGETS)
IF (WITH_QUICK)
ADD_SUBDIRECTORY(quickgui)
ENDIF (WITH_QUICK)

View File

@ -90,6 +90,7 @@ QString ABISYM( QgsApplication::mPluginPath );
QString ABISYM( QgsApplication::mPkgDataPath );
QString ABISYM( QgsApplication::mLibraryPath );
QString ABISYM( QgsApplication::mLibexecPath );
QString ABISYM( QgsApplication::mQmlImportPath );
QString ABISYM( QgsApplication::mThemeName );
QString ABISYM( QgsApplication::mUIThemeName );
QString ABISYM( QgsApplication::mProfilePath );
@ -204,6 +205,9 @@ void QgsApplication::init( QString profileFolder )
ABISYM( mLibexecPath ) = ABISYM( mBuildOutputPath ) + '/' + QGIS_LIBEXEC_SUBDIR + '/' + ABISYM( mCfgIntDir ) + '/';
#else
ABISYM( mLibexecPath ) = ABISYM( mBuildOutputPath ) + '/' + QGIS_LIBEXEC_SUBDIR + '/';
#endif
#if defined( HAVE_QUICK )
ABISYM( mQmlImportPath ) = ABISYM( mBuildOutputPath ) + '/' + QGIS_QML_SUBDIR + '/';
#endif
}
else
@ -396,6 +400,9 @@ void QgsApplication::setPrefixPath( const QString &prefixPath, bool useDefaultPa
}
ABISYM( mLibraryPath ) = ABISYM( mPrefixPath ) + '/' + QGIS_LIB_SUBDIR + '/';
ABISYM( mLibexecPath ) = ABISYM( mPrefixPath ) + '/' + QGIS_LIBEXEC_SUBDIR + '/';
#if defined( HAVE_QUICK )
ABISYM( mQmlImportPath ) = ABISYM( mPrefixPath ) + '/' + QGIS_QML_SUBDIR + '/';
#endif
}
void QgsApplication::setPluginPath( const QString &pluginPath )
@ -1001,6 +1008,11 @@ QString QgsApplication::libexecPath()
return ABISYM( mLibexecPath );
}
QString QgsApplication::qmlImportPath()
{
return ABISYM( mQmlImportPath );
}
QgsApplication::endian_t QgsApplication::endian()
{
return ( htonl( 1 ) == 1 ) ? XDR : NDR;

View File

@ -405,6 +405,14 @@ class CORE_EXPORT QgsApplication : public QApplication
//! Returns the path with utility executables (help viewer, crssync, ...)
static QString libexecPath();
/**
* Returns the path where QML components are installed for QGIS Quick library. Returns
* empty string when QGIS is built without Quick support
*
* \since QGIS 3.2
*/
static QString qmlImportPath();
//! Alters prefix path - used by 3rd party apps
static void setPrefixPath( const QString &prefixPath, bool useDefaultPaths = false );
@ -752,6 +760,7 @@ class CORE_EXPORT QgsApplication : public QApplication
static QString ABISYM( mPkgDataPath );
static QString ABISYM( mLibraryPath );
static QString ABISYM( mLibexecPath );
static QString ABISYM( mQmlImportPath );
static QString ABISYM( mThemeName );
static QStringList ABISYM( mDefaultSvgPaths );
static QMap<QString, QString> ABISYM( mSystemEnvVars );

View File

@ -25,9 +25,12 @@ QgsRelationManager::QgsRelationManager( QgsProject *project )
: QObject( project )
, mProject( project )
{
connect( project, &QgsProject::readProject, this, &QgsRelationManager::readProject );
connect( project, &QgsProject::writeProject, this, &QgsRelationManager::writeProject );
connect( project, &QgsProject::layersRemoved, this, &QgsRelationManager::layersRemoved );
if ( mProject )
{
connect( project, &QgsProject::readProject, this, &QgsRelationManager::readProject );
connect( project, &QgsProject::writeProject, this, &QgsRelationManager::writeProject );
connect( project, &QgsProject::layersRemoved, this, &QgsRelationManager::layersRemoved );
}
}
void QgsRelationManager::setRelations( const QList<QgsRelation> &relations )

121
src/quickgui/CMakeLists.txt Normal file
View File

@ -0,0 +1,121 @@
############################################################
# sources
SET(QGIS_QUICK_GUI_MOC_HDRS
qgsquickmapcanvasmap.h
qgsquickmapsettings.h
qgsquickutils.h
)
SET(QGIS_QUICK_GUI_HDRS
)
SET(QGIS_QUICK_GUI_SRC
qgsquickmapcanvasmap.cpp
qgsquickmapsettings.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=)
############################################################
# 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 "-DQT_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}
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)

View File

@ -0,0 +1,121 @@
############################################################
# sources
SET(QGIS_QUICK_PLUGIN_MOC_HDRS
qgsquickplugin.h
)
SET(QGIS_QUICK_PLUGIN_SRC
qgsquickplugin.cpp
)
SET(QGIS_QUICK_PLUGIN_RESOURCES
qgsquickmapcanvas.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_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=" "-DQT_NO_FOREACH")
# Copy qml files to output directory, we need qml files in the same directory as the plugin shared library
FOREACH(qmlfile ${QGIS_QUICK_PLUGIN_RESOURCES})
ADD_CUSTOM_COMMAND(TARGET qgis_quick_plugin
PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${qmlfile} ${QGIS_QUICK_PLUGIN_RUNTIME_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${qmlfile}
)
ENDFOREACH(qmlfile)
IF(QMLPLUGINDUMP_FOUND)
# To create typeinfo file, no qml files must be in the directory, otherwise
# bunch of "QObject: Cannot create children for a parent that is in a different thread." errors
# appear and typeinfo file is not generated
SET(QGIS_QUICK_TYPEINFO_GENERATE_DIR ${CMAKE_CURRENT_BINARY_DIR}/QgisQuick)
# 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 ${CMAKE_COMMAND} -E make_directory ${QGIS_QUICK_TYPEINFO_GENERATE_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/qmldir ${QGIS_QUICK_TYPEINFO_GENERATE_DIR}
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:qgis_quick_plugin> ${QGIS_QUICK_TYPEINFO_GENERATE_DIR}
COMMAND ${QMLPLUGINDUMP_EXECUTABLE}
ARGS QgisQuick 0.1 . -v --output ${QGIS_QUICK_PLUGIN_TYPEINFO}
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}
)

View File

@ -0,0 +1,142 @@
/***************************************************************************
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')
}
}
}
}

View File

@ -0,0 +1,65 @@
/***************************************************************************
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 <qqml.h>
#include <QObject>
#include <QQmlEngine>
#include <QJSEngine>
#include "qgsfeature.h"
#include "qgslogger.h"
#include "qgsmaplayer.h"
#include "qgsmessagelog.h"
#include "qgspointxy.h"
#include "qgsproject.h"
#include "qgsrelationmanager.h"
#include "qgscoordinatetransformcontext.h"
#include "qgsvectorlayer.h"
#include "qgsquickmapcanvasmap.h"
#include "qgsquickmapsettings.h"
#include "qgsquickplugin.h"
#include "qgsquickutils.h"
static QObject *_utilsProvider( QQmlEngine *engine, QJSEngine *scriptEngine )
{
Q_UNUSED( engine )
Q_UNUSED( scriptEngine )
return new QgsQuickUtils(); // the object will be owned by QML engine and destroyed by the engine on exit
}
void QgisQuickPlugin::registerTypes( const char *uri )
{
qRegisterMetaType< QList<QgsMapLayer *> >( "QList<QgsMapLayer*>" );
qRegisterMetaType< QgsAttributes > ( "QgsAttributes" );
qRegisterMetaType< QgsCoordinateReferenceSystem >( "QgsCoordinateReferenceSystem" );
qRegisterMetaType< QgsCoordinateTransformContext >( "QgsCoordinateTransformContext" );
qRegisterMetaType< QgsFeature > ( "QgsFeature " );
qRegisterMetaType< QgsFeatureId > ( "QgsFeatureId" );
qRegisterMetaType< QgsPoint >( "QgsPoint" );
qRegisterMetaType< QgsPointXY >( "QgsPointXY" );
qmlRegisterType< QgsProject >( uri, 0, 1, "Project" );
qmlRegisterType< QgsQuickMapCanvasMap >( uri, 0, 1, "MapCanvasMap" );
qmlRegisterType< QgsQuickMapSettings >( uri, 0, 1, "MapSettings" );
qmlRegisterType< QgsVectorLayer >( uri, 0, 1, "VectorLayer" );
qmlRegisterSingletonType< QgsQuickUtils >( uri, 0, 1, "Utils", _utilsProvider );
qmlRegisterUncreatableType< QgsRelationManager >( uri, 0, 1, "RelationManager", "The relation manager is available from the Project. Try `qgisProject.relationManager`" );
qmlRegisterUncreatableType< QgsMessageLog >( uri, 0, 1, "QgsMessageLog", "Expose MessageLevel" );
}

View File

@ -0,0 +1,42 @@
/***************************************************************************
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 <QQmlExtensionPlugin>
/**
* \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 );
};
#endif // QGSQUICKPLUGIN_H

View File

@ -0,0 +1,18 @@
# 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
typeinfo qgisquick.qmltypes

View File

@ -0,0 +1,394 @@
/***************************************************************************
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 <QQuickWindow>
#include <QScreen>
#include <QSGSimpleTextureNode>
#include <QtConcurrent>
#include "qgsmaprendererparalleljob.h"
#include "qgsmessagelog.h"
#include "qgspallabeling.h"
#include "qgsproject.h"
#include "qgsvectorlayer.h"
#include "qgis.h"
#include "qgsquickmapcanvasmap.h"
#include "qgsquickmapsettings.h"
QgsQuickMapCanvasMap::QgsQuickMapCanvasMap( QQuickItem *parent )
: QQuickItem( parent )
, mMapSettings( new QgsQuickMapSettings() )
{
connect( this, &QQuickItem::windowChanged, this, &QgsQuickMapCanvasMap::onWindowChanged );
connect( &mRefreshTimer, &QTimer::timeout, this, &QgsQuickMapCanvasMap::refreshMap );
connect( &mMapUpdateTimer, &QTimer::timeout, this, &QgsQuickMapCanvasMap::renderJobUpdated );
Q_ASSERT( mMapSettings );
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 );
}
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 ) );
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 )
{
Q_ASSERT( mMapSettings );
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()
{
Q_ASSERT( mMapSettings );
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()
{
const QgsMapRendererJob::Errors errors = mJob->errors();
for ( const QgsMapRendererJob::Error &error : errors )
{
QgsMessageLog::logMessage( QStringLiteral( "%1 :: %2" ).arg( 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( window, &QQuickWindow::screenChanged, this, &QgsQuickMapCanvasMap::onScreenChanged );
if ( window )
{
connect( window, &QQuickWindow::screenChanged, this, &QgsQuickMapCanvasMap::onScreenChanged );
onScreenChanged( window->screen() );
}
}
void QgsQuickMapCanvasMap::onScreenChanged( QScreen *screen )
{
Q_ASSERT( mMapSettings );
if ( screen )
mMapSettings->setOutputDpi( screen->physicalDotsPerInch() );
}
void QgsQuickMapCanvasMap::onExtentChanged()
{
updateTransform();
// And trigger a new rendering job
refresh();
}
void QgsQuickMapCanvasMap::updateTransform()
{
Q_ASSERT( mMapSettings );
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 ( mMapUpdateTimer.interval() == 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;
}
QSGNode *QgsQuickMapCanvasMap::updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * )
{
if ( mDirty )
{
delete oldNode;
oldNode = nullptr;
mDirty = false;
}
QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>( 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() &&
!qgsDoubleNear( rect.width() / rect.height(), mImage.width() / mImage.height() ) )
{
if ( qgsDoubleNear( 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;
}
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...
// Be careful when re-enabling it.
// QQuickItem::geometryChanged( newGeometry, oldGeometry );
mMapSettings->setOutputSize( newGeometry.size().toSize() );
refresh();
}
void QgsQuickMapCanvasMap::onLayersChanged()
{
Q_ASSERT( mMapSettings );
if ( mMapSettings->extent().isEmpty() )
zoomToFullExtent();
for ( const QMetaObject::Connection &conn : qgis::as_const( mLayerConnections ) )
{
disconnect( conn );
}
mLayerConnections.clear();
const QList<QgsMapLayer *> layers = mMapSettings->layers();
for ( QgsMapLayer *layer : layers )
{
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 );
mJob->cancelWithoutBlocking();
mJob = nullptr;
}
}
void QgsQuickMapCanvasMap::zoomToFullExtent()
{
Q_ASSERT( mMapSettings );
QgsRectangle extent;
const QList<QgsMapLayer *> layers = mMapSettings->layers();
for ( QgsMapLayer *layer : 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()
{
Q_ASSERT( mMapSettings );
if ( mMapSettings->outputSize().isNull() )
return; // the map image size has not been set yet
if ( !mFreeze )
mRefreshTimer.start( 1 );
}

View File

@ -0,0 +1,202 @@
/***************************************************************************
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 <QtQuick/QQuickItem>
#include <QFutureSynchronizer>
#include <QTimer>
#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.
*
* QgsQuickMapCanvasMap instance internally creates QgsQuickMapSettings in
* constructor. The QgsProject should be attached to the QgsQuickMapSettings.
* The map settings for other QgsQuick components should be initialized from
* QgsQuickMapCanvasMap's mapSettings
*
* \note QML Type: MapCanvasMap
*
* \sa QgsQuickMapCanvas
*
* \since QGIS 3.2
*/
class QUICK_EXPORT QgsQuickMapCanvasMap : public QQuickItem
{
Q_OBJECT
/**
* The mapSettings property contains configuration for rendering of the map.
*
* It should be used as a primary source of map settings (and project) for
* all other components in the application.
*
* This is a readonly property.
*/
Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings )
/**
* When freeze property is set to true, the map canvas does not refresh.
* The value temporary changes during the rendering process.
*/
Q_PROPERTY( bool freeze READ freeze WRITE setFreeze NOTIFY freezeChanged )
/**
* The isRendering property is set to true while a rendering job is pending for this map canvas map.
* It can be used to show a notification icon about an ongoing rendering job.
* This is a readonly property.
*/
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 )
/**
* When the incrementalRendering property is set to true, the automatic refresh of map canvas during rendering is allowed.
*/
Q_PROPERTY( bool incrementalRendering READ incrementalRendering WRITE setIncrementalRendering NOTIFY incrementalRenderingChanged )
public:
//! Create map canvas map
QgsQuickMapCanvasMap( QQuickItem *parent = nullptr );
~QgsQuickMapCanvasMap() = default;
virtual QSGNode *updatePaintNode( QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * ) override;
//! \copydoc QgsQuickMapCanvasMap::mapSettings
QgsQuickMapSettings *mapSettings() const;
//! \copydoc QgsQuickMapCanvasMap::freeze
bool freeze() const;
//! \copydoc QgsQuickMapCanvasMap::freeze
void setFreeze( bool freeze );
//! \copydoc QgsQuickMapCanvasMap::isRendering
bool isRendering() const;
//! \copydoc QgsQuickMapCanvasMap::mapUpdateInterval
int mapUpdateInterval() const;
//! \copydoc QgsQuickMapCanvasMap::mapUpdateInterval
void setMapUpdateInterval( int mapUpdateInterval );
//! \copydoc QgsQuickMapCanvasMap::incrementalRendering
bool incrementalRendering() const;
//! \copydoc QgsQuickMapCanvasMap::incrementalRendering
void setIncrementalRendering( bool incrementalRendering );
signals:
/**
* Signal is emitted when a rendering is starting
*/
void renderStarting();
/**
* Signal is emitted when a canvas is refreshed
*/
void mapCanvasRefreshed();
//! \copydoc QgsQuickMapCanvasMap::freeze
void freezeChanged();
//! \copydoc QgsQuickMapCanvasMap::isRendering
void isRenderingChanged();
//!\copydoc QgsQuickMapCanvasMap::mapUpdateInterval
void mapUpdateIntervalChanged();
//!\copydoc QgsQuickMapCanvasMap::incrementalRendering
void incrementalRenderingChanged();
protected:
virtual void geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry ) override;
public slots:
//! Stop map rendering
void stopRendering();
/**
* Set map setting's extent (zoom the map) on the center by given scale
*/
void zoom( QPointF center, qreal scale );
/**
* Set map setting's extent (pan the map) based on the difference of positions
*/
void pan( QPointF oldPos, QPointF newPos );
/**
* Refresh the map canvas.
* Does nothing when output size of map settings is not set
*/
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 by stopRendering()!
*/
void destroyJob( QgsMapRendererJob *job );
QgsMapSettings prepareMapSettings() const;
void updateTransform();
void zoomToFullExtent();
QgsQuickMapSettings *mMapSettings;
bool mPinching = false;
QPoint mPinchStartPoint;
QgsMapRendererParallelJob *mJob = nullptr;
QgsMapRendererCache *mCache = nullptr;
QgsLabelingResults *mLabelingResults = nullptr;
QImage mImage;
QgsMapSettings mImageMapSettings;
QTimer mRefreshTimer;
bool mDirty = false;
bool mFreeze = false;
QList<QMetaObject::Connection> mLayerConnections;
QTimer mMapUpdateTimer;
bool mIncrementalRendering = false;
};
#endif // QGSQUICKMAPCANVASMAP_H

View File

@ -0,0 +1,214 @@
/***************************************************************************
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 "qgis.h"
#include "qgsquickmapsettings.h"
QgsQuickMapSettings::QgsQuickMapSettings( QObject *parent )
: QObject( parent )
{
// 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 );
}
void QgsQuickMapSettings::setProject( QgsProject *project )
{
if ( project == mProject )
return;
// If we have already something connected, disconnect it!
if ( mProject )
{
mProject->disconnect( this );
}
mProject = project;
// Connect all signals
if ( mProject )
{
connect( mProject, &QgsProject::readProject, this, &QgsQuickMapSettings::onReadProject );
setDestinationCrs( mProject->crs() );
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 &center )
{
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 ( qgsDoubleNear( 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<QgsMapLayer *> QgsQuickMapSettings::layers() const
{
return mMapSettings.layers();
}
void QgsQuickMapSettings::setLayers( const QList<QgsMapLayer *> &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 ( !qgsDoubleNear( 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 ( !qgsDoubleNear( rotation, 0 ) )
QgsMessageLog::logMessage( tr( "Map Canvas rotation is not supported. Resetting from %1 to 0." ).arg( rotation ) );
}

View File

@ -0,0 +1,237 @@
/***************************************************************************
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 <QObject>
#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.
*
* On top of QgsMapSettings functionality, when QgsProject is attached,
* it automatically loads its default settings from the project.
* QgsProject should be attached before it is read.
*
* \note QML Type: MapSettings
*
* \sa QgsMapCanvas
*
* \since QGIS 3.2
*/
class QUICK_EXPORT QgsQuickMapSettings : public QObject
{
Q_OBJECT
/**
* A project property should be used as a primary source of project all other components
* in the application. QgsProject should be attached to QgsQuickMapSettings before
* it is read (QgsProject::read)
*
* When project is read, map settings (CRS, extent, ...) are automatically set from its DOM.
*/
Q_PROPERTY( QgsProject *project READ project WRITE setProject NOTIFY projectChanged )
/**
* Geographical coordinates of the rectangle that should be rendered.
* The actual visible extent used for rendering could be slightly different
* since the given extent may be expanded in order to fit the aspect ratio
* of output size. Use QgsQuickMapSettings::visibleExtent to get the resulting extent.
*
* Automatically loaded from project on QgsProject::readProject
*/
Q_PROPERTY( QgsRectangle extent READ extent WRITE setExtent NOTIFY extentChanged )
//! \copydoc QgsMapSettings::visibleExtent()
Q_PROPERTY( QgsRectangle visibleExtent READ visibleExtent NOTIFY visibleExtentChanged )
//! \copydoc QgsMapSettings::mapUnitsPerPixel()
Q_PROPERTY( double mapUnitsPerPixel READ mapUnitsPerPixel NOTIFY mapUnitsPerPixelChanged )
/**
* The rotation of the resulting map image, in degrees clockwise.
* Map canvas rotation support is not implemented, 0 is used
*/
Q_PROPERTY( double rotation READ rotation WRITE setRotation NOTIFY rotationChanged )
/**
* The size of the resulting map image
*
* Automatically loaded from project on QgsProject::readProject
*/
Q_PROPERTY( QSize outputSize READ outputSize WRITE setOutputSize NOTIFY outputSizeChanged )
/**
* Output DPI used for conversion between real world units (e.g. mm) and pixels
*
* Automatically loaded from project on QgsProject::readProject
*/
Q_PROPERTY( double outputDpi READ outputDpi WRITE setOutputDpi NOTIFY outputDpiChanged )
/**
* CRS of destination coordinate reference system.
*
* Automatically loaded from project on QgsProject::readProject
*/
Q_PROPERTY( QgsCoordinateReferenceSystem destinationCrs READ destinationCrs WRITE setDestinationCrs NOTIFY destinationCrsChanged )
/**
* Set list of layers for map rendering. The layers must be registered in QgsProject.
* The layers are stored in the reverse order of how they are rendered (layer with index 0 will be on top)
*
* \note Any non-spatial layers will be automatically stripped from the list (since they cannot be rendered!).
*
* Not loaded automatically from the associated project
*/
Q_PROPERTY( QList<QgsMapLayer *> layers READ layers WRITE setLayers NOTIFY layersChanged )
public:
//! Create new map settings
QgsQuickMapSettings( QObject *parent = nullptr );
~QgsQuickMapSettings() = default;
//! Clone map settings
QgsMapSettings mapSettings() const;
//! \copydoc QgsMapSettings::extent()
QgsRectangle extent() const;
//! \copydoc QgsMapSettings::setExtent()
void setExtent( const QgsRectangle &extent );
//! \copydoc QgsQuickMapSettings::project
void setProject( QgsProject *project );
//! \copydoc QgsQuickMapSettings::project
QgsProject *project() const;
//! Move current map extent to have center point defined by \a center
Q_INVOKABLE void setCenter( const QgsPoint &center );
//! \copydoc QgsMapSettings::mapUnitsPerPixel()
double mapUnitsPerPixel() const;
//! \copydoc QgsMapSettings::visibleExtent()
QgsRectangle visibleExtent() const;
//! \copydoc QgsMapSettings::transformContext()
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;
//! \copydoc QgsMapSettings::setTransformContext()
void setTransformContext( const QgsCoordinateTransformContext &context );
//! \copydoc QgsQuickMapSettings::rotation
double rotation() const;
//! \copydoc QgsQuickMapSettings::rotation
void setRotation( double rotation );
//! \copydoc QgsMapSettings::outputSize()
QSize outputSize() const;
//! \copydoc QgsMapSettings::setOutputSize()
void setOutputSize( const QSize &outputSize );
//! \copydoc QgsMapSettings::outputDpi()
double outputDpi() const;
//! \copydoc QgsMapSettings::setOutputDpi()
void setOutputDpi( double outputDpi );
//! \copydoc QgsMapSettings::destinationCrs()
QgsCoordinateReferenceSystem destinationCrs() const;
//! \copydoc QgsMapSettings::setDestinationCrs()
void setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs );
//! \copydoc QgsMapSettings::layers()
QList<QgsMapLayer *> layers() const;
//! \copydoc QgsMapSettings::setLayers()
void setLayers( const QList<QgsMapLayer *> &layers );
signals:
//! \copydoc QgsQuickMapSettings::project
void projectChanged();
//! \copydoc QgsQuickMapSettings::extent
void extentChanged();
//! \copydoc QgsQuickMapSettings::destinationCrs
void destinationCrsChanged();
//! \copydoc QgsQuickMapSettings::mapUnitsPerPixel
void mapUnitsPerPixelChanged();
//! \copydoc QgsQuickMapSettings::rotation
void rotationChanged();
//! \copydoc QgsQuickMapSettings::visibleExtent
void visibleExtentChanged();
//! \copydoc QgsQuickMapSettings::outputSize
void outputSizeChanged();
//! \copydoc QgsQuickMapSettings::outputDpi
void outputDpiChanged();
//! \copydoc QgsQuickMapSettings::layers
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:
QgsProject *mProject = nullptr;
QgsMapSettings mMapSettings;
};
#endif // QGSQUICKMAPSETTINGS_H

View File

@ -0,0 +1,71 @@
/***************************************************************************
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 <QDesktopWidget>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QString>
#include <QThread>
#include "qgis.h"
#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransform.h"
#include "qgsdistancearea.h"
#include "qgslogger.h"
#include "qgsvectorlayer.h"
#include "qgsquickmapsettings.h"
#include "qgsquickutils.h"
#include "qgsunittypes.h"
QgsQuickUtils::QgsQuickUtils( QObject *parent )
: QObject( parent )
, mScreenDensity( calculateScreenDensity() )
{
}
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 = static_cast<double>( width ) / dpiX * 25.4;
double sizeY = static_cast<double>( height ) / dpiY * 25.4;
QString msg;
msg += tr( "screen resolution: %1x%2 px\n" ).arg( width ).arg( height );
msg += tr( "screen DPI: %1x%2\n" ).arg( dpiX ).arg( dpiY );
msg += tr( "screen size: %1x%2 mm\n" ).arg( QString::number( sizeX, 'f', 0 ), QString::number( sizeY, 'f', 0 ) );
msg += tr( "screen density: %1" ).arg( mScreenDensity );
return msg;
}
qreal QgsQuickUtils::screenDensity() const
{
return mScreenDensity;
}
qreal QgsQuickUtils::calculateScreenDensity()
{
// 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 asymmetrical DPI. Improbable
return dpi / 160.; // 160 DPI is baseline for density-independent pixels in Android
}

View File

@ -0,0 +1,74 @@
/***************************************************************************
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 <QObject>
#include <QString>
#include "qgis.h"
#include "qgis_quick.h"
/**
* \ingroup quick
*
* Encapsulating the common utilies for QgsQuick library.
*
* \note QML Type: Utils (Singleton)
*
* \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.
*
* This is a readonly property.
*/
Q_PROPERTY( qreal dp READ screenDensity CONSTANT )
public:
//! Create new utilities
QgsQuickUtils( QObject *parent = nullptr );
//! dtor
~QgsQuickUtils() = default;
//! \copydoc QgsQuickUtils::dp
qreal screenDensity() const;
/**
* Returns a string with information about screen size and resolution
*
* Useful to log for debugging of graphical problems on various display sizes
*/
QString dumpScreenInfo() const;
private:
static qreal calculateScreenDensity();
qreal mScreenDensity;
};
#endif // QGSQUICKUTILS_H

View File

@ -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)

View File

@ -0,0 +1,86 @@
# 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}
${Qt5Xml_LIBRARIES}
${Qt5Core_LIBRARIES}
${Qt5Svg_LIBRARIES}
${Qt5Test_LIBRARIES}
Qt5::Gui
Qt5::Qml
Qt5::Quick
Qt5::Xml
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)

View File

@ -0,0 +1,80 @@
SET(QGIS_QUICK_APP_MOC_HDRS
)
SET(QGIS_QUICK_APP_SRCS
main.cpp
)
SET(QGIS_QUICK_APP_QMLS
main.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 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 "-DQT_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})

View File

@ -0,0 +1,92 @@
/***************************************************************************
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 <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlError>
#include "qgis.h"
#include "qgsapplication.h"
#include "qgsproject.h"
#include "qgslayertree.h"
#include "qgsmessagelog.h"
#include "qgsquickutils.h"
#include "qgslogger.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";
QgsDebugMsg( QStringLiteral( "project file: %1" ).arg( 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() ) );
QQmlComponent component( &engine, QUrl( QStringLiteral( "qrc:/main.qml" ) ) );
QObject *object = component.create();
if ( !component.errors().isEmpty() )
{
QgsDebugMsg( QStringLiteral( "%s" ).arg( QgsApplication::showSettings().toLocal8Bit().data() ) );
QgsDebugMsg( QStringLiteral( "****************************************" ) );
QgsDebugMsg( QStringLiteral( "***** QML errors: *****" ) );
QgsDebugMsg( QStringLiteral( "****************************************" ) );
const QList<QQmlError> errors = component.errors();
for ( const QQmlError &error : errors )
{
QgsDebugMsg( error.toString() );
}
QgsDebugMsg( QStringLiteral( "****************************************" ) );
QgsDebugMsg( QStringLiteral( "****************************************" ) );
}
if ( object == nullptr )
{
QgsDebugMsg( QStringLiteral( "FATAL ERROR: unable to create main.qml" ) );
return EXIT_FAILURE;
}
// Add some data for debugging if needed
QgsApplication::messageLog()->logMessage( QgsQuickUtils().dumpScreenInfo() );
QgsDebugMsg( QStringLiteral( "data directory: %1" ).arg( dataDir ) );
return app.exec();
}

View File

@ -0,0 +1,40 @@
/***************************************************************************
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")
QgsQuick.MapCanvas {
id: mapCanvas
height: parent.height
width: parent.width
mapSettings.project: __project
mapSettings.layers: __layers
onClicked: {
var screenPoint = Qt.point(mouse.x, mouse.y)
console.log("clicked:" + screenPoint)
}
}
}

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>

View File

@ -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

View File

@ -0,0 +1,53 @@
/***************************************************************************
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 <QObject>
#include <QApplication>
#include <QDesktopWidget>
#include "qgsapplication.h"
#include "qgstest.h"
#include "qgis.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 screen_density();
void dump_screen_info();
private:
QgsQuickUtils utils;
};
void TestQgsQuickUtils::screen_density()
{
qreal dp = utils.screenDensity();
QVERIFY( ( dp > 0 ) && ( dp < 1000 ) );
}
void TestQgsQuickUtils::dump_screen_info()
{
qreal dp = utils.screenDensity();
QVERIFY( utils.dumpScreenInfo().contains( QStringLiteral( "%1" ).arg( dp ) ) );
}
QGSTEST_MAIN( TestQgsQuickUtils )
#include "testqgsquickutils.moc"

1018
tests/testdata/quickapp_project.qgs vendored Normal file

File diff suppressed because it is too large Load Diff