diff --git a/images/images.qrc b/images/images.qrc index c6e005f8515..63dcfdfd7c8 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -639,6 +639,7 @@ themes/default/propertyicons/colors.svg themes/default/propertyicons/system.svg themes/default/propertyicons/transparency.svg + themes/default/propertyicons/sensor.svg themes/default/propertyicons/spacer.svg themes/default/propertyicons/relations.svg themes/default/rendererCategorizedSymbol.svg diff --git a/images/themes/default/mActionStop.svg b/images/themes/default/mActionStop.svg index cfc08ec760e..867c8fe3f5d 100644 --- a/images/themes/default/mActionStop.svg +++ b/images/themes/default/mActionStop.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + + + diff --git a/images/themes/default/propertyicons/sensor.svg b/images/themes/default/propertyicons/sensor.svg new file mode 100644 index 00000000000..e4a9daf94c2 --- /dev/null +++ b/images/themes/default/propertyicons/sensor.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/python/gui/auto_generated/qgsoptionswidgetfactory.sip.in b/python/gui/auto_generated/qgsoptionswidgetfactory.sip.in index b20389b3877..0bfae9e838f 100644 --- a/python/gui/auto_generated/qgsoptionswidgetfactory.sip.in +++ b/python/gui/auto_generated/qgsoptionswidgetfactory.sip.in @@ -56,6 +56,12 @@ The default implementation returns ``True``. %Docstring Called to permanently apply the settings shown in the options page (e.g. save them to :py:class:`QgsSettings` objects). This is usually called when the options dialog is accepted. +%End + + virtual void cancel(); +%Docstring +Called to cancel settings changed in the options page (e.g. save them to +:py:class:`QgsSettings` objects). This is usually called when the options dialog is canceled. %End protected: diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 46fafc67353..e02afc39fea 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -272,6 +272,9 @@ set(QGIS_APP_SRCS gps/qgsgpsmarker.cpp gps/qgsgpstoolbar.cpp + sensor/qgssensortablewidget.cpp + sensor/qgsprojectsensorsettingswidget.cpp + project/qgsprojectelevationsettingswidget.cpp pluginmanager/qgspluginmanager.cpp @@ -525,6 +528,7 @@ target_include_directories(qgis_app PUBLIC ${CMAKE_SOURCE_DIR}/src/app/layout ${CMAKE_SOURCE_DIR}/src/app/pluginmanager ${CMAKE_SOURCE_DIR}/src/app/gps + ${CMAKE_SOURCE_DIR}/src/app/sensor ${CMAKE_SOURCE_DIR}/src/app/dwg ${CMAKE_SOURCE_DIR}/src/app/maptools ${CMAKE_SOURCE_DIR}/src/app/mesh diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 65707f0080f..79e9d6e05aa 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -442,7 +442,9 @@ Q_GUI_EXPORT extern int qt_defaultDpiX(); #include "pointcloud/qgspointcloudlayerstylewidget.h" #include "pointcloud/qgspointcloudlayersaveasdialog.h" #include "pointcloud/qgspointcloudlayerexporter.h" + #include "project/qgsprojectelevationsettingswidget.h" +#include "sensor/qgsprojectsensorsettingswidget.h" #include "qgsmaptoolsdigitizingtechniquemanager.h" #include "qgsmaptoolshaperegistry.h" @@ -1507,6 +1509,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipBadLayers registerMapLayerPropertiesFactory( new QgsElevationShadingRendererSettingsWidgetFactory( this ) ); registerProjectPropertiesWidgetFactory( new QgsProjectElevationSettingsWidgetFactory( this ) ); + registerProjectPropertiesWidgetFactory( new QgsProjectSensorSettingsWidgetFactory( this ) ); activateDeactivateLayerRelatedActions( nullptr ); // after members were created diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index dc9a2e10731..4b2fbdd0348 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -182,6 +182,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa projectionSelector->setShowNoProjection( true ); connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsProjectProperties::apply ); + connect( this, &QDialog::finished, this, [ = ]( int result ) { if ( result == QDialog::Rejected ) cancel(); } ); // disconnect default connection setup by initOptionsBase for accepting dialog, and insert logic // to validate widgets before allowing dialog to be closed @@ -1738,6 +1739,14 @@ void QgsProjectProperties::apply() } } +void QgsProjectProperties::cancel() +{ + for ( QgsOptionsPageWidget *widget : std::as_const( mAdditionalProjectPropertiesWidgets ) ) + { + widget->cancel(); + } +} + void QgsProjectProperties::lwWmsRowsInserted( const QModelIndex &parent, int first, int last ) { Q_UNUSED( parent ) diff --git a/src/app/qgsprojectproperties.h b/src/app/qgsprojectproperties.h index f92171a21e8..5c092c74b2f 100644 --- a/src/app/qgsprojectproperties.h +++ b/src/app/qgsprojectproperties.h @@ -80,6 +80,11 @@ class APP_EXPORT QgsProjectProperties : public QgsOptionsDialogBase, private Ui: */ void apply(); + /** + * Slot called when cancel button is pressed or dialog is not accepted + */ + void cancel(); + /** * Let the user add a scale to the list of project scales * used in scale combobox instead of global ones. diff --git a/src/app/sensor/qgsprojectsensorsettingswidget.cpp b/src/app/sensor/qgsprojectsensorsettingswidget.cpp new file mode 100644 index 00000000000..936a57d0557 --- /dev/null +++ b/src/app/sensor/qgsprojectsensorsettingswidget.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + qgsprojectsensorsettingswidget.cpp + --------------------- + begin : March 2022 + copyright : (C) 2022 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsprojectsensorsettingswidget.h" + +#include "qgis.h" +#include "qgsabstractsensor.h" +#include "qgsapplication.h" +#include "qgsproject.h" +#include "qgssensormanager.h" + +QgsProjectSensorSettingsWidget::QgsProjectSensorSettingsWidget( QWidget *parent ) + : QgsOptionsPageWidget( parent ) +{ + setupUi( this ); + + QDomElement sensorElem = QgsProject::instance()->sensorManager()->writeXml( mPreviousSensors ); + mPreviousSensors.appendChild( sensorElem ); + + const QList sensors = QgsProject::instance()->sensorManager()->sensors(); + for ( QgsAbstractSensor *sensor : sensors ) + { + if ( sensor->status() == Qgis::DeviceConnectionStatus::Connected ) + { + mConnectedSensors << sensor->id(); + } + } +} + +void QgsProjectSensorSettingsWidget::cancel() +{ + // Capture connected state of current sensors even if we're about to revert as someone might have + // activated a sensor then closed the dialog using the window bar's close button + QList sensors = QgsProject::instance()->sensorManager()->sensors(); + for ( QgsAbstractSensor *sensor : sensors ) + { + if ( sensor->status() == Qgis::DeviceConnectionStatus::Connected ) + { + mConnectedSensors << sensor->id(); + } + } + + QgsProject::instance()->sensorManager()->clear(); + QgsProject::instance()->sensorManager()->readXml( mPreviousSensors.documentElement(), mPreviousSensors ); + + sensors = QgsProject::instance()->sensorManager()->sensors(); + for ( QgsAbstractSensor *sensor : sensors ) + { + if ( mConnectedSensors.contains( sensor->id() ) ) + { + sensor->connectSensor(); + } + } +} + +void QgsProjectSensorSettingsWidget::apply() +{ + return; +} + +bool QgsProjectSensorSettingsWidget::isValid() +{ + return true; +} + + +// +// QgsProjectSensorSettingsWidgetFactory +// + +QgsProjectSensorSettingsWidgetFactory::QgsProjectSensorSettingsWidgetFactory( QObject *parent ) + : QgsOptionsWidgetFactory( tr( "Sensors" ), QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/sensor.svg" ) ), QStringLiteral( "sensor" ) ) +{ + setParent( parent ); +} + + +QgsOptionsPageWidget *QgsProjectSensorSettingsWidgetFactory::createWidget( QWidget *parent ) const +{ + return new QgsProjectSensorSettingsWidget( parent ); +} diff --git a/src/app/sensor/qgsprojectsensorsettingswidget.h b/src/app/sensor/qgsprojectsensorsettingswidget.h new file mode 100644 index 00000000000..83d547d8c74 --- /dev/null +++ b/src/app/sensor/qgsprojectsensorsettingswidget.h @@ -0,0 +1,55 @@ +/*************************************************************************** + qgsprojectsensorsettingswidget.h + --------------------- + begin : March 2023 + copyright : (C) 2023 by Mathieu Pellerin + email : mathieu at opengis dot 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 QGSPROJECTSENSORSETTINGSWIDGET_H +#define QGSPROJECTSENSORSETTINGSWIDGET_H + +#include "ui_qgsprojectsensorettingswidgetbase.h" +#include "qgsoptionswidgetfactory.h" + +#include + +class QgsProjectSensorSettingsWidget : public QgsOptionsPageWidget, private Ui::QgsProjectSensorSettingsWidgetBase +{ + Q_OBJECT + public: + + QgsProjectSensorSettingsWidget( QWidget *parent = nullptr ); + + public slots: + + bool isValid() override; + void apply() override; + void cancel() override; + + private: + + QDomDocument mPreviousSensors; + QStringList mConnectedSensors; +}; + + +class QgsProjectSensorSettingsWidgetFactory : public QgsOptionsWidgetFactory +{ + Q_OBJECT + public: + explicit QgsProjectSensorSettingsWidgetFactory( QObject *parent = nullptr ); + + QgsOptionsPageWidget *createWidget( QWidget *parent = nullptr ) const override; +}; + + + +#endif // QGSPROJECTSENSORSETTINGSWIDGET_H diff --git a/src/app/sensor/qgssensortablewidget.cpp b/src/app/sensor/qgssensortablewidget.cpp new file mode 100644 index 00000000000..cc81dca72b2 --- /dev/null +++ b/src/app/sensor/qgssensortablewidget.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + qgssensortablewidget.h + --------------------------------- + begin : March 2023 + copyright : (C) 2023 by Mathieu Pellerin + email : mathieu at opengis dot 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 "qgssensortablewidget.h" + +#include "qgisapp.h" +#include "qgsapplication.h" +#include "qgssensormanager.h" +#include "qgssensormodel.h" +#include "qgsproject.h" + +#include + +QgsSensorTableWidget::QgsSensorTableWidget( QWidget *parent ) + : QgsPanelWidget( parent ) +{ + setupUi( this ); + setPanelTitle( tr( "Sensors List" ) ); + setObjectName( QStringLiteral( "SensorsList" ) ); + + mActionConnection->setEnabled( false ); + + mSensorModel = new QgsSensorModel( QgsProject::instance()->sensorManager(), this ); + + mSensorTable->setModel( mSensorModel ); + mSensorTable->horizontalHeader()->setSectionResizeMode( static_cast( QgsSensorModel::Column::Name ), QHeaderView::Stretch ); + mSensorTable->setSelectionBehavior( QAbstractItemView::SelectRows ); + mSensorTable->setSelectionMode( QAbstractItemView::SingleSelection ); + + connect( QgsProject::instance()->sensorManager(), &QgsSensorManager::sensorStatusChanged, this, [ = ]( const QString & id ) + { + const QModelIndex index = mSensorTable->currentIndex(); + if ( index.isValid() ) + { + if ( id == mSensorModel->data( index, QgsSensorModel::SensorId ).toString() ) + { + QgsAbstractSensor *sensor = mSensorModel->data( index, QgsSensorModel::Sensor ).value(); + if ( sensor ) + { + if ( sensor->status() == Qgis::DeviceConnectionStatus::Disconnected ) + { + mActionConnection->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionStart.svg" ) ) ); + } + else + { + mActionConnection->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionStop.svg" ) ) ); + } + } + } + } + } ); + + connect( mSensorTable->selectionModel(), &QItemSelectionModel::currentChanged, this, [ = ]( const QModelIndex & current, const QModelIndex & ) + { + mActionConnection->setEnabled( current.isValid() ); + if ( current.isValid() && mSensorModel->data( current, QgsSensorModel::SensorStatus ).value() == Qgis::DeviceConnectionStatus::Connected ) + { + mActionConnection->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionStop.svg" ) ) ); + } + else + { + mActionConnection->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionStart.svg" ) ) ); + } + } ); + + connect( mActionConnection, &QToolButton::clicked, this, [ = ]() + { + const QModelIndex index = mSensorTable->currentIndex(); + if ( index.isValid() ) + { + QgsAbstractSensor *sensor = mSensorModel->data( index, QgsSensorModel::Sensor ).value(); + if ( sensor ) + { + if ( sensor->status() == Qgis::DeviceConnectionStatus::Disconnected ) + { + sensor->connectSensor(); + } + else + { + sensor->disconnectSensor(); + } + } + } + } ); +} diff --git a/src/app/sensor/qgssensortablewidget.h b/src/app/sensor/qgssensortablewidget.h new file mode 100644 index 00000000000..abff5484572 --- /dev/null +++ b/src/app/sensor/qgssensortablewidget.h @@ -0,0 +1,40 @@ +/*************************************************************************** + qgssensortablewidget.h + --------------------------------- + begin : March 2023 + copyright : (C) 2023 by Mathieu Pellerin + email : mathieu at opengis dot 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 QGSSENSORTABLEWIDGET_H +#define QGSSENSORTABLEWIDGET_H + +#include "ui_qgssensortablewidgetbase.h" + +#include "qgis_app.h" +#include "qgsdockwidget.h" +#include "qgspanelwidget.h" +#include "qgspanelwidgetstack.h" + +class QgsSensorModel; + +class APP_EXPORT QgsSensorTableWidget : public QgsPanelWidget, private Ui::QgsSensorTableWidgetBase +{ + Q_OBJECT + + public: + QgsSensorTableWidget( QWidget *parent = nullptr ); + ~QgsSensorTableWidget() override = default; + + private: + QgsSensorModel *mSensorModel = nullptr; +}; + +#endif // QGSSENSORTABLEWIDGET_H diff --git a/src/gui/qgsoptionswidgetfactory.h b/src/gui/qgsoptionswidgetfactory.h index 1aaf4048b2c..001e3606ca9 100644 --- a/src/gui/qgsoptionswidgetfactory.h +++ b/src/gui/qgsoptionswidgetfactory.h @@ -77,6 +77,12 @@ class GUI_EXPORT QgsOptionsPageWidget : public QWidget */ virtual void apply() = 0; + /** + * Called to cancel settings changed in the options page (e.g. save them to + * QgsSettings objects). This is usually called when the options dialog is canceled. + */ + virtual void cancel() { return; } + protected: /** diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 96a41bad782..da8025925fa 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -17,6 +17,7 @@ file(GLOB NUMERICFORMAT_UIS "${CMAKE_CURRENT_SOURCE_DIR}/numericformats/*.ui") file(GLOB ATTRIBUTEFORMCONFIG_UIS "${CMAKE_CURRENT_SOURCE_DIR}/attributeformconfig/*.ui") file(GLOB GEOREFERENCER_UIS "${CMAKE_CURRENT_SOURCE_DIR}/georeferencer/*.ui") file(GLOB ANNOTATION_UIS "${CMAKE_CURRENT_SOURCE_DIR}/annotations/*.ui") +file(GLOB SENSOR_UIS "${CMAKE_CURRENT_SOURCE_DIR}/sensor/*.ui") if (BUILD_WITH_QT6) QT6_WRAP_UI(QGIS_UIS_H @@ -38,6 +39,7 @@ if (BUILD_WITH_QT6) ${ATTRIBUTEFORMCONFIG_UIS} ${GEOREFERENCER_UIS} ${ANNOTATION_UIS} + ${SENSOR_UIS} ) else() QT5_WRAP_UI(QGIS_UIS_H @@ -59,6 +61,7 @@ else() ${ATTRIBUTEFORMCONFIG_UIS} ${GEOREFERENCER_UIS} ${ANNOTATION_UIS} + ${SENSOR_UIS} ) endif() diff --git a/src/ui/sensor/qgsprojectsensorettingswidgetbase.ui b/src/ui/sensor/qgsprojectsensorettingswidgetbase.ui new file mode 100644 index 00000000000..b709803b3ef --- /dev/null +++ b/src/ui/sensor/qgsprojectsensorettingswidgetbase.ui @@ -0,0 +1,53 @@ + + + QgsProjectSensorSettingsWidgetBase + + + + 0 + 0 + 504 + 486 + + + + Sensor Settings + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Sensor Settings + + + + + + + + + + + + + QgsSensorTableWidget + QWidget +
qgssensortablewidget.h
+ 1 +
+
+ + +
diff --git a/src/ui/sensor/qgssensortablewidgetbase.ui b/src/ui/sensor/qgssensortablewidgetbase.ui new file mode 100644 index 00000000000..75a825c41c0 --- /dev/null +++ b/src/ui/sensor/qgssensortablewidgetbase.ui @@ -0,0 +1,90 @@ + + + QgsSensorTableWidgetBase + + + + 0 + 0 + 291 + 376 + + + + Sensors Table + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + true + + + false + + + + + + + + + Toggle sensor connection status + + + + + + + :/images/themes/default/mActionStart.svg:/images/themes/default/mActionStart.svg + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + QgsPanelWidget + QgsPanelWidget +
qgspanelwidget.h
+ 1 +
+
+ + + + +