Skip to content

Commit

Permalink
[ui][sensors] Add a sensors panel in the project properties dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Mar 28, 2023
1 parent 99f8005 commit 00b53e7
Show file tree
Hide file tree
Showing 16 changed files with 481 additions and 1 deletion.
1 change: 1 addition & 0 deletions images/images.qrc
Expand Up @@ -639,6 +639,7 @@
<file>themes/default/propertyicons/colors.svg</file>
<file>themes/default/propertyicons/system.svg</file>
<file>themes/default/propertyicons/transparency.svg</file>
<file>themes/default/propertyicons/sensor.svg</file>
<file>themes/default/propertyicons/spacer.svg</file>
<file>themes/default/propertyicons/relations.svg</file>
<file>themes/default/rendererCategorizedSymbol.svg</file>
Expand Down
6 changes: 5 additions & 1 deletion images/themes/default/mActionStop.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions images/themes/default/propertyicons/sensor.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions python/gui/auto_generated/qgsoptionswidgetfactory.sip.in
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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

Expand Down
9 changes: 9 additions & 0 deletions src/app/qgsprojectproperties.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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 )
Expand Down
5 changes: 5 additions & 0 deletions src/app/qgsprojectproperties.h
Expand Up @@ -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.
Expand Down
93 changes: 93 additions & 0 deletions 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<QgsAbstractSensor *> 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<QgsAbstractSensor *> 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 );
}
55 changes: 55 additions & 0 deletions 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 <QDomDocument>

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
97 changes: 97 additions & 0 deletions 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 <QTableWidget>

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<int>( 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<QgsAbstractSensor *>();
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>() == 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<QgsAbstractSensor *>();
if ( sensor )
{
if ( sensor->status() == Qgis::DeviceConnectionStatus::Disconnected )
{
sensor->connectSensor();
}
else
{
sensor->disconnectSensor();
}
}
}
} );
}

0 comments on commit 00b53e7

Please sign in to comment.