[ui][sensors] Sensors GUI registry implementation

This commit is contained in:
Mathieu Pellerin 2023-03-21 14:03:37 +07:00
parent 97d68a389f
commit 95c57b134c
25 changed files with 1611 additions and 10 deletions

View File

@ -122,6 +122,7 @@ if(WITH_APIDOC)
${CMAKE_SOURCE_DIR}/src/gui/processing
${CMAKE_SOURCE_DIR}/src/gui/processing/models
${CMAKE_SOURCE_DIR}/src/gui/raster
${CMAKE_SOURCE_DIR}/src/gui/sensor
${CMAKE_SOURCE_DIR}/src/gui/settings
${CMAKE_SOURCE_DIR}/src/gui/symbology
${CMAKE_SOURCE_DIR}/src/gui/tableeditor

View File

@ -797,6 +797,7 @@
<file>themes/default/mActionRegularPolygonCenterPoint.svg</file>
<file>themes/default/3d.svg</file>
<file>themes/default/mActionResizeSquare.svg</file>
<file>themes/default/mSensor.svg</file>
<file>themes/default/mSourceFields.svg</file>
<file>themes/default/cursors/mCapturePoint.svg</file>
<file>themes/default/cursors/mCrossHair.svg</file>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16" height="16" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><ellipse cx="8" cy="8" rx="2" ry="2" fill="#6d97c4" stroke="#415a75" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" style="paint-order:stroke fill markers"/><g fill-opacity="0" stroke="#415a75" stroke-width="1px"><path d="m3.4 3.4026c-2.1961 2.13-2.8729 6.329 0 9.2001"/><path d="m4.8184 4.8156c-1.7645 1.8074-1.7713 4.4684-0.018388 6.3802"/><path d="m12.585 12.597c2.1961-2.13 2.8729-6.329 0-9.2001"/><path d="m11.167 11.184c1.7645-1.8074 1.7713-4.4684 0.01839-6.3802"/></g><path d="m8 10c-0.68223-0.21791-1.0082-1.1123-1-2 0.0083867-0.9033 0.36254-1.7999 1-2-1.2889 0.020622-2.0076 1.0366-2 2 0.007328 0.93908 0.65835 1.9488 2 2z" fill="#bacee3"/></svg>

After

Width:  |  Height:  |  Size: 810 B

View File

@ -10,6 +10,7 @@
class QgsGui : QObject
{
%Docstring(signature="appended")
@ -130,6 +131,13 @@ Returns the global GUI-related project storage registry
Returns the registry of GUI-related components of data providers
.. versionadded:: 3.10
%End
static QgsSensorGuiRegistry *sensorGuiRegistry() /KeepReference/;
%Docstring
Returns the registry of GUI-related components for sensors
.. versionadded:: 3.32
%End
static QgsSubsetStringEditorProviderRegistry *subsetStringEditorProviderRegistry() /KeepReference/;

View File

@ -0,0 +1,156 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/sensor/qgssensorguiregistry.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsSensorAbstractGuiMetadata
{
%Docstring(signature="appended")
Stores GUI metadata about one sensor class.
This is a companion to :py:class:`QgsSensorAbstractMetadata`, storing only
the components related to the GUI behavior of sensor.
.. note::
In C++ you can use :py:class:`QgsSensorGuiMetadata` convenience class.
.. versionadded:: 3.32
%End
%TypeHeaderCode
#include "qgssensorguiregistry.h"
%End
public:
QgsSensorAbstractGuiMetadata( const QString &type, const QString &visibleName );
%Docstring
Constructor for QgsSensorAbstractGuiMetadata with the specified class ``type``.
``visibleName`` should be set to a translated, user visible name identifying the corresponding sensor type.
%End
virtual ~QgsSensorAbstractGuiMetadata();
QString type() const;
%Docstring
Returns the unique type code for the sensor class.
%End
QString visibleName() const;
%Docstring
Returns a translated, user visible name identifying the corresponding sensor.
%End
virtual QIcon creationIcon() const;
%Docstring
Returns an icon representing creation of the sensor type.
%End
virtual QgsAbstractSensorWidget *createSensorWidget( QgsAbstractSensor *sensor ) /TransferBack/;
%Docstring
Creates a configuration widget for an ``sensor`` of this type. Can return ``None`` if no configuration GUI is required.
%End
virtual QgsAbstractSensor *createSensor( QObject *parent ) /TransferBack/;
%Docstring
Creates an instance of the corresponding sensor type.
%End
};
class QgsSensorGuiRegistry : QObject
{
%Docstring(signature="appended")
Registry of available sensor GUI behavior.
:py:class:`QgsSensorGuiRegistry` is not usually directly created, but rather accessed through
:py:func:`QgsGui.sensorGuiRegistry()`.
This acts as a companion to :py:class:`QgsSensorRegistry`, handling only
the components related to the GUI behavior of sensors.
.. versionadded:: 3.32
%End
%TypeHeaderCode
#include "qgssensorguiregistry.h"
%End
public:
QgsSensorGuiRegistry( QObject *parent = 0 );
%Docstring
Creates a new empty sensor GUI registry.
QgsSensorGuiRegistry is not usually directly created, but rather accessed through
:py:func:`QgsGui.sensorGuiRegistry()`.
%End
~QgsSensorGuiRegistry();
bool populate();
%Docstring
Populates the registry with standard sensor types. If called on a non-empty registry
then this will have no effect and will return ``False``.
%End
QgsSensorAbstractGuiMetadata *sensorMetadata( const QString &type ) const;
%Docstring
Returns the metadata for the specified sensor ``type``. Returns ``None`` if
a corresponding sensor type was not found in the registry.
%End
bool addSensorGuiMetadata( QgsSensorAbstractGuiMetadata *metadata /Transfer/ );
%Docstring
Registers the GUI metadata for a new sensor type. Takes ownership of the metadata instance.
%End
QgsAbstractSensor *createSensor( const QString &type, QObject *parent = 0 ) const /TransferBack/;
%Docstring
Creates a new instance of a sensor given the ``type``.
%End
QgsAbstractSensorWidget *createSensorWidget( QgsAbstractSensor *sensor ) const /TransferBack/;
%Docstring
Creates a new instance of a sensor configuration widget for the specified ``sensor``. The
``sensor`` doesn't need to live for the duration of the widget, it is only used when creating
the configuration widget to match a sensor type and initiate the widget to match the
``sensor`` settings.
%End
QMap<QString, QString> sensorTypes() const;
%Docstring
Returns a list of sensor types handled by the registry.
%End
signals:
void sensorAdded( const QString &type, const QString &name );
%Docstring
Emitted whenever a new sensor type is added to the registry, with the specified
``type``.
%End
private:
QgsSensorGuiRegistry( const QgsSensorGuiRegistry &rh );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/sensor/qgssensorguiregistry.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -0,0 +1,72 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/sensor/qgssensorwidget.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsAbstractSensorWidget : QWidget
{
%Docstring(signature="appended")
Base class for widgets which allow control over the properties of sensors.
.. versionadded:: 3.32
%End
%TypeHeaderCode
#include "qgssensorwidget.h"
%End
public:
QgsAbstractSensorWidget( QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsAbstractSensorWidget.
:param parent: parent widget
%End
virtual QgsAbstractSensor *createSensor() = 0 /Factory/;
%Docstring
Creates a new sensor matching the settings defined in the widget.
%End
virtual bool updateSensor( QgsAbstractSensor *sensor ) = 0;
%Docstring
Updates an existing ``sensor`` to match the settings defined in the widget. If
``True`` is returned, the ``sensor`` was successfully updated.
If ``False`` is returned, then the widget could not successfully update
the ``sensor``.
%End
virtual bool setSensor( QgsAbstractSensor *sensor ) = 0;
%Docstring
Sets the widget settings to match a given ``sensor``. If ``True`` is returned, ``sensor``
was an acceptable type and the widget has been updated to match
the ``sensor``'s properties.
If ``False`` is returned, then the widget could not be successfully updated
to show the properties of ``sensor``.
%End
signals:
void changed();
%Docstring
Emitted whenever configuration changes happened on this sensor configuration.
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/sensor/qgssensorwidget.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -476,6 +476,8 @@
%Include auto_generated/symbology/qgssymbolslistwidget.sip
%Include auto_generated/symbology/qgssymbolwidgetcontext.sip
%Include auto_generated/symbology/qgsvectorfieldsymbollayerwidget.sip
%Include auto_generated/sensor/qgssensorguiregistry.sip
%Include auto_generated/sensor/qgssensorwidget.sip
%Include auto_generated/settings/qgssettingsregistrygui.sip
%Include auto_generated/tableeditor/qgstableeditordialog.sip
%Include auto_generated/tableeditor/qgstableeditorwidget.sip

View File

@ -20,12 +20,16 @@
#include "qgsapplication.h"
#include "qgsproject.h"
#include "qgssensormanager.h"
#include "qgssensortablewidget.h"
QgsProjectSensorSettingsWidget::QgsProjectSensorSettingsWidget( QWidget *parent )
: QgsOptionsPageWidget( parent )
{
setupUi( this );
QgsSensorTableWidget *widget = new QgsSensorTableWidget( this );
mPanelStack->setMainPanel( widget );
QDomElement sensorElem = QgsProject::instance()->sensorManager()->writeXml( mPreviousSensors );
mPreviousSensors.appendChild( sensorElem );
@ -67,6 +71,20 @@ void QgsProjectSensorSettingsWidget::cancel()
void QgsProjectSensorSettingsWidget::apply()
{
mPreviousSensors = QDomDocument();
QDomElement sensorElem = QgsProject::instance()->sensorManager()->writeXml( mPreviousSensors );
mPreviousSensors.appendChild( sensorElem );
mConnectedSensors.clear();
const QList<QgsAbstractSensor *> sensors = QgsProject::instance()->sensorManager()->sensors();
for ( QgsAbstractSensor *sensor : sensors )
{
if ( sensor->status() == Qgis::DeviceConnectionStatus::Connected )
{
mConnectedSensors << sensor->id();
}
}
return;
}

View File

@ -17,12 +17,86 @@
#include "qgisapp.h"
#include "qgsapplication.h"
#include "qgsgui.h"
#include "qgsiodevicesensor.h"
#include "qgssensorguiregistry.h"
#include "qgssensormanager.h"
#include "qgssensormodel.h"
#include "qgssensorwidget.h"
#include "qgsproject.h"
#include <QDialogButtonBox>
#include <QTableWidget>
QgsSensorSettingsWidget::QgsSensorSettingsWidget( QgsAbstractSensor *sensor, QWidget *parent )
: QgsPanelWidget( parent )
, mSensor( sensor )
{
setupUi( this );
setPanelTitle( tr( "Sensor Settings" ) );
setObjectName( QStringLiteral( "SensorSettings" ) );
mNameLineEdit->setText( sensor->name() );
connect( mNameLineEdit, &QLineEdit::textChanged, this, [ = ]() { mButtonBox->button( QDialogButtonBox::Apply )->setEnabled( true ); } );
const QMap<QString, QString> sensorTypes = QgsGui::sensorGuiRegistry()->sensorTypes();
for ( auto sensorIt = sensorTypes.begin(); sensorIt != sensorTypes.end(); ++sensorIt )
{
mTypeComboBox->addItem( QgsGui::sensorGuiRegistry()->sensorMetadata( sensorIt.key() )->creationIcon(), sensorIt.value(), sensorIt.key() );
}
mTypeComboBox->setCurrentIndex( mTypeComboBox->findData( sensor->type() ) );
connect( mTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]()
{
mButtonBox->button( QDialogButtonBox::Apply )->setEnabled( true );
setSensorWidget();
} );
mButtonBox->button( QDialogButtonBox::Apply )->setEnabled( false );
connect( mButtonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, [ = ]()
{
apply();
} );
setSensorWidget();
}
void QgsSensorSettingsWidget::setSensorWidget()
{
if ( mSensorWidget )
{
mSensorWidget->deleteLater();
mSensorWidget = nullptr;
}
mSensorWidget = QgsGui::sensorGuiRegistry()->sensorMetadata( mTypeComboBox->currentData().toString() )->createSensorWidget( mSensor );
if ( mSensorWidget )
{
mSensorWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
mSensorWidget->setSensor( mSensor );
mTypeLayout->addWidget( mSensorWidget );
connect( mSensorWidget, &QgsAbstractSensorWidget::changed, this, [ = ]() { mButtonBox->button( QDialogButtonBox::Apply )->setEnabled( true ); } );
}
}
void QgsSensorSettingsWidget::apply()
{
if ( mSensorWidget && mSensor )
{
mSensor->disconnectSensor();
if ( !mSensorWidget->updateSensor( mSensor ) )
{
// The sensor type has changed, remove sensor and add a fresh one
QgsProject::instance()->sensorManager()->removeSensor( mSensor->id() );
mSensor = mSensorWidget->createSensor();
QgsProject::instance()->sensorManager()->addSensor( mSensor );
}
mSensor->setName( mNameLineEdit->text() );
}
}
//----------------
QgsSensorTableWidget::QgsSensorTableWidget( QWidget *parent )
: QgsPanelWidget( parent )
{
@ -31,6 +105,8 @@ QgsSensorTableWidget::QgsSensorTableWidget( QWidget *parent )
setObjectName( QStringLiteral( "SensorsList" ) );
mActionConnection->setEnabled( false );
mActionRemoveSensor->setEnabled( false );
mActionEditSensor->setEnabled( false );
mSensorModel = new QgsSensorModel( QgsProject::instance()->sensorManager(), this );
@ -39,6 +115,15 @@ QgsSensorTableWidget::QgsSensorTableWidget( QWidget *parent )
mSensorTable->setSelectionBehavior( QAbstractItemView::SelectRows );
mSensorTable->setSelectionMode( QAbstractItemView::SingleSelection );
connect( mSensorTable, &QAbstractItemView::doubleClicked, this, [ = ]( const QModelIndex & index )
{
if ( index.isValid() )
{
QgsSensorSettingsWidget *settingsWidget = new QgsSensorSettingsWidget( mSensorModel->data( index, QgsSensorModel::Sensor ).value<QgsAbstractSensor *>(), this );
showPanel( settingsWidget );
}
} );
connect( QgsProject::instance()->sensorManager(), &QgsSensorManager::sensorStatusChanged, this, [ = ]( const QString & id )
{
const QModelIndex index = mSensorTable->currentIndex();
@ -65,6 +150,8 @@ QgsSensorTableWidget::QgsSensorTableWidget( QWidget *parent )
connect( mSensorTable->selectionModel(), &QItemSelectionModel::currentChanged, this, [ = ]( const QModelIndex & current, const QModelIndex & )
{
mActionConnection->setEnabled( current.isValid() );
mActionRemoveSensor->setEnabled( current.isValid() );
mActionEditSensor->setEnabled( current.isValid() );
if ( current.isValid() && mSensorModel->data( current, QgsSensorModel::SensorStatus ).value<Qgis::DeviceConnectionStatus>() == Qgis::DeviceConnectionStatus::Connected )
{
mActionConnection->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionStop.svg" ) ) );
@ -94,4 +181,33 @@ QgsSensorTableWidget::QgsSensorTableWidget( QWidget *parent )
}
}
} );
connect( mActionAddSensor, &QToolButton::clicked, this, [ = ]()
{
QgsTcpSocketSensor *sensor = new QgsTcpSocketSensor();
sensor->setName( tr( "New sensor" ) );
QgsProject::instance()->sensorManager()->addSensor( sensor );
QgsSensorSettingsWidget *settingsWidget = new QgsSensorSettingsWidget( sensor, this );
showPanel( settingsWidget );
} );
connect( mActionRemoveSensor, &QToolButton::clicked, this, [ = ]()
{
const QModelIndex index = mSensorTable->currentIndex();
if ( index.isValid() )
{
QgsProject::instance()->sensorManager()->removeSensor( mSensorModel->data( index, QgsSensorModel::SensorId ).toString() );
}
} );
connect( mActionEditSensor, &QToolButton::clicked, this, [ = ]()
{
const QModelIndex index = mSensorTable->currentIndex();
if ( index.isValid() )
{
QgsSensorSettingsWidget *settingsWidget = new QgsSensorSettingsWidget( mSensorModel->data( index, QgsSensorModel::Sensor ).value<QgsAbstractSensor *>(), this );
showPanel( settingsWidget );
}
} );
}

View File

@ -17,13 +17,33 @@
#define QGSSENSORTABLEWIDGET_H
#include "ui_qgssensortablewidgetbase.h"
#include "ui_qgssensorsettingswidgetbase.h"
#include "qgis_app.h"
#include "qgsdockwidget.h"
#include "qgsabstractsensor.h"
#include "qgspanelwidget.h"
#include "qgspanelwidgetstack.h"
class QgsSensorModel;
class QgsAbstractSensorWidget;
class APP_EXPORT QgsSensorSettingsWidget : public QgsPanelWidget, private Ui::QgsSensorSettingsWidgetBase
{
Q_OBJECT
public:
QgsSensorSettingsWidget( QgsAbstractSensor *sensor = nullptr, QWidget *parent SIP_TRANSFERTHIS = nullptr );
~QgsSensorSettingsWidget() override = default;
void apply();
private:
void setSensorWidget();
QgsAbstractSensor *mSensor = nullptr;
QgsAbstractSensorWidget *mSensorWidget = nullptr;
};
class APP_EXPORT QgsSensorTableWidget : public QgsPanelWidget, private Ui::QgsSensorTableWidgetBase
{

View File

@ -424,6 +424,9 @@ set(QGIS_GUI_SRCS
providers/ogr/qgsogritemguiprovider.cpp
providers/ogr/qgsgeopackageprojectstorageguiprovider.cpp
sensor/qgssensorguiregistry.cpp
sensor/qgssensorwidget.cpp
settings/qgssettingsregistrygui.cpp
tableeditor/qgstableeditordialog.cpp
@ -1400,6 +1403,9 @@ set(QGIS_GUI_HDRS
symbology/qgssymbolwidgetcontext.h
symbology/qgsvectorfieldsymbollayerwidget.h
sensor/qgssensorguiregistry.h
sensor/qgssensorwidget.h
settings/qgssettingsregistrygui.h
tableeditor/qgstableeditordialog.h
@ -1547,6 +1553,7 @@ target_include_directories(qgis_gui PUBLIC
${CMAKE_SOURCE_DIR}/src/gui/providers/ogr
${CMAKE_SOURCE_DIR}/src/gui/pointcloud
${CMAKE_SOURCE_DIR}/src/gui/raster
${CMAKE_SOURCE_DIR}/src/gui/sensor
${CMAKE_SOURCE_DIR}/src/gui/settings
${CMAKE_SOURCE_DIR}/src/gui/tableeditor
${CMAKE_SOURCE_DIR}/src/gui/vector

View File

@ -60,6 +60,7 @@
#include "qgssettingsregistrygui.h"
#include "qgshistoryproviderregistry.h"
#include "qgslayermetadatasourceselectprovider.h"
#include "qgssensorguiregistry.h"
#include <QPushButton>
#include <QToolButton>
@ -170,6 +171,11 @@ QgsProviderGuiRegistry *QgsGui::providerGuiRegistry()
return instance()->mProviderGuiRegistry;
}
QgsSensorGuiRegistry *QgsGui::sensorGuiRegistry()
{
return instance()->mSensorGuiRegistry;
}
QgsHistoryProviderRegistry *QgsGui::historyProviderRegistry()
{
return instance()->mHistoryProviderRegistry;
@ -230,6 +236,7 @@ QgsGui::~QgsGui()
delete mShapeMapToolRegistry;
delete mRelationEditorRegistry;
delete mSettingsRegistryGui;
delete mSensorGuiRegistry;
}
QColor QgsGui::sampleColor( QPoint point )
@ -283,6 +290,9 @@ QgsGui::QgsGui()
mCodeEditorColorSchemeRegistry = new QgsCodeEditorColorSchemeRegistry();
// provider gui registry initialize QgsProviderRegistry too
mSensorGuiRegistry = new QgsSensorGuiRegistry();
mSensorGuiRegistry->populate();
mHistoryProviderRegistry = new QgsHistoryProviderRegistry();
mHistoryProviderRegistry->addDefaultProviders();

View File

@ -47,6 +47,8 @@ class QgsProviderSourceWidgetProviderRegistry;
class QgsRelationWidgetRegistry;
class QgsMapToolShapeRegistry;
class QgsHistoryProviderRegistry;
class QgsSensorGuiRegistry;
/**
* \ingroup gui
@ -174,6 +176,12 @@ class GUI_EXPORT QgsGui : public QObject
*/
static QgsProviderGuiRegistry *providerGuiRegistry() SIP_KEEPREFERENCE;
/**
* Returns the registry of GUI-related components for sensors
* \since QGIS 3.32
*/
static QgsSensorGuiRegistry *sensorGuiRegistry() SIP_KEEPREFERENCE;
/**
* Returns the registry of subset string editors of data providers
* \since QGIS 3.18
@ -313,6 +321,7 @@ class GUI_EXPORT QgsGui : public QObject
QgsRelationWidgetRegistry *mRelationEditorRegistry = nullptr;
QgsMapToolShapeRegistry *mShapeMapToolRegistry = nullptr;
QgsHistoryProviderRegistry *mHistoryProviderRegistry = nullptr;
QgsSensorGuiRegistry *mSensorGuiRegistry = nullptr;
std::unique_ptr< QgsWindowManagerInterface > mWindowManager;
#ifdef SIP_RUN

View File

@ -0,0 +1,113 @@
/***************************************************************************
qgssensorguiregistry.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 "qgsconfig.h"
#include "qgssensorguiregistry.h"
#include "qgssensorwidget.h"
QgsSensorGuiRegistry::QgsSensorGuiRegistry( QObject *parent )
: QObject( parent )
{
}
QgsSensorGuiRegistry::~QgsSensorGuiRegistry()
{
qDeleteAll( mMetadata );
}
bool QgsSensorGuiRegistry::populate()
{
if ( !mMetadata.isEmpty() )
return false;
addSensorGuiMetadata( new QgsSensorGuiMetadata( QStringLiteral( "tcp_socket" ),
QObject::tr( "TCP socket sensor" ),
QgsApplication::getThemeIcon( QStringLiteral( "/mSensor.svg" ) ),
[ = ]( QgsAbstractSensor * sensor )->QgsAbstractSensorWidget *
{
QgsTcpSocketSensorWidget *widget = new QgsTcpSocketSensorWidget( nullptr );
widget->setSensor( sensor );
return widget;
}, nullptr ) );
addSensorGuiMetadata( new QgsSensorGuiMetadata( QStringLiteral( "udp_socket" ),
QObject::tr( "UDP socket sensor" ),
QgsApplication::getThemeIcon( QStringLiteral( "/mSensor.svg" ) ),
[ = ]( QgsAbstractSensor * sensor )->QgsAbstractSensorWidget *
{
QgsUdpSocketSensorWidget *widget = new QgsUdpSocketSensorWidget( nullptr );
widget->setSensor( sensor );
return widget;
}, nullptr ) );
#if defined( HAVE_QTSERIALPORT )
addSensorGuiMetadata( new QgsSensorGuiMetadata( QStringLiteral( "serial_port" ),
QObject::tr( "Serial port sensor" ),
QgsApplication::getThemeIcon( QStringLiteral( "/mSensor.svg" ) ),
[ = ]( QgsAbstractSensor * sensor )->QgsAbstractSensorWidget *
{
QgsSerialPortSensorWidget *widget = new QgsSerialPortSensorWidget( nullptr );
widget->setSensor( sensor );
return widget;
}, nullptr ) );
#endif
return true;
}
QgsSensorAbstractGuiMetadata *QgsSensorGuiRegistry::sensorMetadata( const QString &type ) const
{
return mMetadata.value( type );
}
bool QgsSensorGuiRegistry::addSensorGuiMetadata( QgsSensorAbstractGuiMetadata *metadata )
{
if ( !metadata || mMetadata.contains( metadata->type() ) )
return false;
mMetadata[metadata->type()] = metadata;
emit sensorAdded( metadata->type(), metadata->visibleName() );
return true;
}
QgsAbstractSensor *QgsSensorGuiRegistry::createSensor( const QString &type, QObject *parent ) const
{
if ( !mMetadata.contains( type ) )
return nullptr;
std::unique_ptr< QgsAbstractSensor > sensor( mMetadata.value( type )->createSensor( parent ) );
if ( sensor )
return sensor.release();
return QgsApplication::sensorRegistry()->createSensor( type, parent );
}
QgsAbstractSensorWidget *QgsSensorGuiRegistry::createSensorWidget( QgsAbstractSensor *sensor ) const
{
if ( !sensor || !mMetadata.contains( sensor->type() ) )
return nullptr;
return mMetadata[sensor->type()]->createSensorWidget( sensor );
}
QMap<QString, QString> QgsSensorGuiRegistry::sensorTypes() const
{
QMap<QString, QString> types;
for ( auto it = mMetadata.constBegin(); it != mMetadata.constEnd(); ++it )
{
types.insert( it.key(), it.value()->visibleName() );
}
return types;
}

View File

@ -0,0 +1,272 @@
/***************************************************************************
qgssensorguiregistry.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 QGSSENSORGUIREGISTRY_H
#define QGSSENSORGUIREGISTRY_H
#include "qgis_gui.h"
#include "qgis_sip.h"
#include "qgsabstractsensor.h"
#include "qgssensorregistry.h"
#include "qgssensorwidget.h"
#include <QIcon>
/**
* \ingroup gui
* \brief Stores GUI metadata about one sensor class.
*
* This is a companion to QgsSensorAbstractMetadata, storing only
* the components related to the GUI behavior of sensor.
*
* \note In C++ you can use QgsSensorGuiMetadata convenience class.
* \since QGIS 3.32
*/
class GUI_EXPORT QgsSensorAbstractGuiMetadata
{
public:
/**
* Constructor for QgsSensorAbstractGuiMetadata with the specified class \a type.
*
* \a visibleName should be set to a translated, user visible name identifying the corresponding sensor type.
*/
QgsSensorAbstractGuiMetadata( const QString &type, const QString &visibleName )
: mType( type )
, mVisibleName( visibleName )
{}
virtual ~QgsSensorAbstractGuiMetadata() = default;
/**
* Returns the unique type code for the sensor class.
*/
QString type() const { return mType; }
/**
* Returns a translated, user visible name identifying the corresponding sensor.
*/
QString visibleName() const { return mVisibleName; }
/**
* Returns an icon representing creation of the sensor type.
*/
virtual QIcon creationIcon() const { return QgsApplication::getThemeIcon( QStringLiteral( "/mSensor.svg" ) ); }
/*
* IMPORTANT: While it seems like /Factory/ would be the correct annotations here, that's not
* the case.
* As per Phil Thomson's advice on https://www.riverbankcomputing.com/pipermail/pyqt/2017-July/039450.html:
*
* "
* /Factory/ is used when the instance returned is guaranteed to be new to Python.
* In this case it isn't because it has already been seen when being returned by QgsProcessingAlgorithm::createInstance()
* (However for a different sub-class implemented in C++ then it would be the first time it was seen
* by Python so the /Factory/ on create() would be correct.)
*
* You might try using /TransferBack/ on create() instead - that might be the best compromise.
* "
*/
/**
* Creates a configuration widget for an \a sensor of this type. Can return NULLPTR if no configuration GUI is required.
*/
virtual QgsAbstractSensorWidget *createSensorWidget( QgsAbstractSensor *sensor ) SIP_TRANSFERBACK { Q_UNUSED( sensor ) return nullptr; }
/**
* Creates an instance of the corresponding sensor type.
*/
virtual QgsAbstractSensor *createSensor( QObject *parent ) SIP_TRANSFERBACK { Q_UNUSED( parent ) return nullptr; }
private:
QString mType;
QString mVisibleName;
};
//! Sensor configuration widget creation function
typedef std::function<QgsAbstractSensorWidget *( QgsAbstractSensor *sensor )> QgsSensorWidgetFunc SIP_SKIP;
#ifndef SIP_RUN
/**
* \ingroup gui
* \brief Convenience metadata class that uses static functions to handle sensor GUI behavior.
* \note not available in Python bindings
* \since QGIS 3.0
*/
class GUI_EXPORT QgsSensorGuiMetadata : public QgsSensorAbstractGuiMetadata
{
public:
/**
* Constructor for QgsSensorGuiMetadata with the specified class \a type
* and \a creationIcon, and function pointers for the configuration widget creation function.
*
* \a visibleName should be set to a translated, user visible name identifying the corresponding sensor.
*/
QgsSensorGuiMetadata( const QString &type, const QString &visibleName, const QIcon &creationIcon,
const QgsSensorWidgetFunc &pfWidget = nullptr,
const QgsSensorCreateFunc &pfCreateFunc = nullptr )
: QgsSensorAbstractGuiMetadata( type, visibleName )
, mIcon( creationIcon )
, mWidgetFunc( pfWidget )
, mCreateFunc( pfCreateFunc )
{}
/**
* Returns the classes' configuration widget creation function.
* \see setWidgetFunction()
*/
QgsSensorWidgetFunc widgetFunction() const { return mWidgetFunc; }
/**
* Sets the classes' sensor configuration widget creation \a function.
* \see widgetFunction()
*/
void setWidgetFunction( const QgsSensorWidgetFunc &function ) { mWidgetFunc = function; }
/**
* Returns the classes' sensor creation function.
* \see setSensorCreationFunction()
*/
QgsSensorCreateFunc sensorCreationFunction() const { return mCreateFunc; }
/**
* Sets the classes' sensor creation \a function.
* \see sensorCreationFunction()
*/
void setSensorCreationFunction( const QgsSensorCreateFunc &function ) { mCreateFunc = function; }
QIcon creationIcon() const override { return mIcon.isNull() ? QgsSensorAbstractGuiMetadata::creationIcon() : mIcon; }
QgsAbstractSensorWidget *createSensorWidget( QgsAbstractSensor *sensor ) override { return mWidgetFunc ? mWidgetFunc( sensor ) : nullptr; }
QgsAbstractSensor *createSensor( QObject *parent ) override { return mCreateFunc ? mCreateFunc( parent ) : nullptr; }
protected:
QIcon mIcon;
QgsSensorWidgetFunc mWidgetFunc = nullptr;
QgsSensorCreateFunc mCreateFunc = nullptr;
};
#endif
/**
* \ingroup gui
* \class QgsSensorGuiRegistry
* \brief Registry of available sensor GUI behavior.
*
* QgsSensorGuiRegistry is not usually directly created, but rather accessed through
* QgsGui::sensorGuiRegistry().
*
* This acts as a companion to QgsSensorRegistry, handling only
* the components related to the GUI behavior of sensors.
*
* \since QGIS 3.32
*/
class GUI_EXPORT QgsSensorGuiRegistry : public QObject
{
Q_OBJECT
public:
/**
* Creates a new empty sensor GUI registry.
*
* QgsSensorGuiRegistry is not usually directly created, but rather accessed through
* QgsGui::sensorGuiRegistry().
*/
QgsSensorGuiRegistry( QObject *parent = nullptr );
~QgsSensorGuiRegistry() override;
//! QgsSensorGuiRegistry cannot be copied.
QgsSensorGuiRegistry( const QgsSensorGuiRegistry &rh ) = delete;
//! QgsSensorGuiRegistry cannot be copied.
QgsSensorGuiRegistry &operator=( const QgsSensorGuiRegistry &rh ) = delete;
/**
* Populates the registry with standard sensor types. If called on a non-empty registry
* then this will have no effect and will return FALSE.
*/
bool populate();
/**
* Returns the metadata for the specified sensor \a type. Returns NULLPTR if
* a corresponding sensor type was not found in the registry.
*/
QgsSensorAbstractGuiMetadata *sensorMetadata( const QString &type ) const;
/**
* Registers the GUI metadata for a new sensor type. Takes ownership of the metadata instance.
*/
bool addSensorGuiMetadata( QgsSensorAbstractGuiMetadata *metadata SIP_TRANSFER );
/*
* IMPORTANT: While it seems like /Factory/ would be the correct annotations here, that's not
* the case.
* As per Phil Thomson's advice on https://www.riverbankcomputing.com/pipermail/pyqt/2017-July/039450.html:
*
* "
* /Factory/ is used when the instance returned is guaranteed to be new to Python.
* In this case it isn't because it has already been seen when being returned by QgsProcessingAlgorithm::createInstance()
* (However for a different sub-class implemented in C++ then it would be the first time it was seen
* by Python so the /Factory/ on create() would be correct.)
*
* You might try using /TransferBack/ on create() instead - that might be the best compromise.
* "
*/
/**
* Creates a new instance of a sensor given the \a type.
*/
QgsAbstractSensor *createSensor( const QString &type, QObject *parent = nullptr ) const SIP_TRANSFERBACK;
/**
* Creates a new instance of a sensor configuration widget for the specified \a sensor. The
* \a sensor doesn't need to live for the duration of the widget, it is only used when creating
* the configuration widget to match a sensor type and initiate the widget to match the
* \a sensor settings.
*/
QgsAbstractSensorWidget *createSensorWidget( QgsAbstractSensor *sensor ) const SIP_TRANSFERBACK;
/**
* Returns a list of sensor types handled by the registry.
*/
QMap<QString, QString> sensorTypes() const;
signals:
/**
* Emitted whenever a new sensor type is added to the registry, with the specified
* \a type.
*/
void sensorAdded( const QString &type, const QString &name );
private:
#ifdef SIP_RUN
QgsSensorGuiRegistry( const QgsSensorGuiRegistry &rh );
#endif
QMap<QString, QgsSensorAbstractGuiMetadata *> mMetadata;
};
#endif //QGSSENSORGUIREGISTRY_H

View File

@ -0,0 +1,228 @@
/***************************************************************************
qgssensorwidget.cpp
---------------------
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 "qgsconfig.h"
#include "qgssensorwidget.h"
#include "qgsiodevicesensor.h"
#if defined( HAVE_QTSERIALPORT )
#include <QSerialPort>
#include <QSerialPortInfo>
#endif
QgsAbstractSensorWidget::QgsAbstractSensorWidget( QWidget *parent SIP_TRANSFERTHIS )
: QWidget( parent )
{
}
// ------------------------
///@cond PRIVATE
QgsTcpSocketSensorWidget::QgsTcpSocketSensorWidget( QWidget *parent )
: QgsAbstractSensorWidget( parent )
{
setupUi( this );
connect( mHostNameLineEdit, &QLineEdit::textChanged, this, &QgsAbstractSensorWidget::changed );
connect( mPortSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsAbstractSensorWidget::changed );
}
QgsAbstractSensor *QgsTcpSocketSensorWidget::createSensor()
{
QgsTcpSocketSensor *s = new QgsTcpSocketSensor();
s->setHostName( mHostNameLineEdit->text() );
s->setPort( mPortSpinBox->value() );
return s;
}
bool QgsTcpSocketSensorWidget::updateSensor( QgsAbstractSensor *sensor )
{
QgsTcpSocketSensor *s = dynamic_cast<QgsTcpSocketSensor *>( sensor );
if ( !s )
return false;
s->setHostName( mHostNameLineEdit->text() );
s->setPort( mPortSpinBox->value() );
return true;
}
bool QgsTcpSocketSensorWidget::setSensor( QgsAbstractSensor *sensor )
{
QgsTcpSocketSensor *ts = dynamic_cast<QgsTcpSocketSensor *>( sensor );
if ( ts )
{
mHostNameLineEdit->setText( ts->hostName() );
mPortSpinBox->setValue( ts->port() );
return true;
}
QgsUdpSocketSensor *us = dynamic_cast<QgsUdpSocketSensor *>( sensor );
if ( us )
{
mHostNameLineEdit->setText( us->hostName() );
mPortSpinBox->setValue( us->port() );
return true;
}
return false;
}
// ------------------------
QgsUdpSocketSensorWidget::QgsUdpSocketSensorWidget( QWidget *parent )
: QgsAbstractSensorWidget( parent )
{
setupUi( this );
connect( mHostNameLineEdit, &QLineEdit::textChanged, this, &QgsAbstractSensorWidget::changed );
connect( mPortSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsAbstractSensorWidget::changed );
}
QgsAbstractSensor *QgsUdpSocketSensorWidget::createSensor()
{
QgsUdpSocketSensor *s = new QgsUdpSocketSensor();
s->setHostName( mHostNameLineEdit->text() );
s->setPort( mPortSpinBox->value() );
return s;
}
bool QgsUdpSocketSensorWidget::updateSensor( QgsAbstractSensor *sensor )
{
QgsUdpSocketSensor *s = dynamic_cast<QgsUdpSocketSensor *>( sensor );
if ( !s )
return false;
s->setHostName( mHostNameLineEdit->text() );
s->setPort( mPortSpinBox->value() );
return true;
}
bool QgsUdpSocketSensorWidget::setSensor( QgsAbstractSensor *sensor )
{
QgsTcpSocketSensor *ts = dynamic_cast<QgsTcpSocketSensor *>( sensor );
if ( ts )
{
mHostNameLineEdit->setText( ts->hostName() );
mPortSpinBox->setValue( ts->port() );
return true;
}
QgsUdpSocketSensor *us = dynamic_cast<QgsUdpSocketSensor *>( sensor );
if ( us )
{
mHostNameLineEdit->setText( us->hostName() );
mPortSpinBox->setValue( us->port() );
return true;
}
return false;
}
// ------------------------
#if defined( HAVE_QTSERIALPORT )
QgsSerialPortSensorWidget::QgsSerialPortSensorWidget( QWidget *parent )
: QgsAbstractSensorWidget( parent )
{
setupUi( this );
for ( const QSerialPortInfo &info : QSerialPortInfo::availablePorts() )
{
mSerialPortComboBox->addItem( QStringLiteral( "%1: %2" ).arg( info.portName(), info.description() ), info.portName() );
}
updateSerialPortDetails();
connect( mSerialPortComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]()
{
updateSerialPortDetails();
emit changed();
} );
}
QgsAbstractSensor *QgsSerialPortSensorWidget::createSensor()
{
QgsSerialPortSensor *s = new QgsSerialPortSensor();
s->setPortName( mSerialPortComboBox->currentData().toString() );
return s;
}
bool QgsSerialPortSensorWidget::updateSensor( QgsAbstractSensor *sensor )
{
QgsSerialPortSensor *s = dynamic_cast<QgsSerialPortSensor *>( sensor );
if ( !s )
return false;
s->setPortName( mSerialPortComboBox->currentData().toString() );
return true;
}
bool QgsSerialPortSensorWidget::setSensor( QgsAbstractSensor *sensor )
{
QgsSerialPortSensor *s = dynamic_cast<QgsSerialPortSensor *>( sensor );
if ( !s )
return false;
const int index = mSerialPortComboBox->findData( s->portName() );
if ( index >= 0 )
{
mSerialPortComboBox->setCurrentIndex( index );
}
else
{
mSerialPortComboBox->addItem( s->portName(), s->portName() );
mSerialPortComboBox->setCurrentIndex( mSerialPortComboBox->count() - 1 );
}
return true;
}
void QgsSerialPortSensorWidget::updateSerialPortDetails()
{
if ( mSerialPortComboBox->currentIndex() < 0 )
{
return;
}
const QString &currentPortName = mSerialPortComboBox->currentData().toString();
bool serialPortFound = false;
for ( const QSerialPortInfo &info : QSerialPortInfo::availablePorts() )
{
serialPortFound = info.portName() == currentPortName;
if ( serialPortFound )
{
mSerialPortDetails->setText( QStringLiteral( "%1:\n- %2: %3\n- %4: %5\n- %6: %7\n- %8: %9\n- %10: %11" ).arg( tr( "Serial port details" ),
tr( "Port name" ), info.portName(),
tr( "Description" ), info.description(),
tr( "Manufacturer" ), info.manufacturer(),
tr( "Product identifier" ), QString::number( info.productIdentifier() ),
tr( "Serial number" ), info.serialNumber() ) );
break;
}
}
if ( !serialPortFound )
{
mSerialPortDetails->setText( QStringLiteral( "%1:\n- %2: %3" ).arg( tr( "Serial port details" ),
tr( "Port name" ), currentPortName ) );
}
}
#endif
///@endcond

View File

@ -0,0 +1,166 @@
/***************************************************************************
qgssensorwidget.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 QGSSENSORWIDGET_H
#define QGSSENSORWIDGET_H
#include "ui_widget_tcpsocketsensor.h"
#include "ui_widget_udpsocketsensor.h"
#include "ui_widget_serialportsensor.h"
#include "qgsconfig.h"
#include "qgis_sip.h"
#include "qgis_gui.h"
#include "qgsabstractsensor.h"
#include <QWidget>
/**
* \ingroup gui
* \class QgsAbstractSensorWidget
* \brief Base class for widgets which allow control over the properties of sensors.
* \since QGIS 3.32
*/
class GUI_EXPORT QgsAbstractSensorWidget : public QWidget
{
Q_OBJECT
public:
/**
* Constructor for QgsAbstractSensorWidget.
* \param parent parent widget
*/
QgsAbstractSensorWidget( QWidget *parent SIP_TRANSFERTHIS = nullptr );
/**
* Creates a new sensor matching the settings defined in the widget.
*/
virtual QgsAbstractSensor *createSensor() = 0 SIP_FACTORY;
/**
* Updates an existing \a sensor to match the settings defined in the widget. If
* TRUE is returned, the \a sensor was successfully updated.
*
* If FALSE is returned, then the widget could not successfully update
* the \a sensor.
*/
virtual bool updateSensor( QgsAbstractSensor *sensor ) = 0;
/**
* Sets the widget settings to match a given \a sensor. If TRUE is returned, \a sensor
* was an acceptable type and the widget has been updated to match
* the \a sensor's properties.
*
* If FALSE is returned, then the widget could not be successfully updated
* to show the properties of \a sensor.
*/
virtual bool setSensor( QgsAbstractSensor *sensor ) = 0;
signals:
/**
* Emitted whenever configuration changes happened on this sensor configuration.
*/
void changed();
};
#ifndef SIP_RUN
///@cond PRIVATE
/**
* \ingroup gui
* \class QgsTcpSocketSensorWidget
* \brief A configuration widget which allow control over QgsTcpSocketSensor properties.
* \since QGIS 3.32
*/
class GUI_EXPORT QgsTcpSocketSensorWidget : public QgsAbstractSensorWidget, private Ui::WidgetTcpSocketSensor
{
Q_OBJECT
public:
/**
* Constructor for QgsTcpSocketSensorWidget.
* \param parent parent widget
*/
QgsTcpSocketSensorWidget( QWidget *parent );
QgsAbstractSensor *createSensor() override;
bool updateSensor( QgsAbstractSensor *sensor ) override;
bool setSensor( QgsAbstractSensor *sensor ) override;
};
/**
* \ingroup gui
* \class QgsUdpSocketSensorWidget
* \brief A configuration widget which allow control over QgsUdpSocketSensorWidget properties.
* \since QGIS 3.32
*/
class GUI_EXPORT QgsUdpSocketSensorWidget : public QgsAbstractSensorWidget, private Ui::WidgetUdpSocketSensor
{
Q_OBJECT
public:
/**
* Constructor for QgsUdpSocketSensorWidget.
* \param parent parent widget
*/
QgsUdpSocketSensorWidget( QWidget *parent );
QgsAbstractSensor *createSensor() override;
bool updateSensor( QgsAbstractSensor *sensor ) override;
bool setSensor( QgsAbstractSensor *sensor ) override;
};
#if defined( HAVE_QTSERIALPORT )
/**
* \ingroup gui
* \class QgsSerialPortSensorWidget
* \brief A configuration widget which allow control over QgsSerialPortSensor properties.
* \since QGIS 3.32
*/
class GUI_EXPORT QgsSerialPortSensorWidget : public QgsAbstractSensorWidget, private Ui::WidgetSerialPortSensor
{
Q_OBJECT
public:
/**
* Constructor for QgsSerialPortSensorWidget.
* \param parent parent widget
*/
QgsSerialPortSensorWidget( QWidget *parent );
QgsAbstractSensor *createSensor() override;
bool updateSensor( QgsAbstractSensor *sensor ) override;
bool setSensor( QgsAbstractSensor *sensor ) override;
private:
void updateSerialPortDetails();
};
#endif
#endif
///@endcond
#endif // QGSSENSORWIDGET_H

View File

@ -2854,6 +2854,21 @@ Shift+click to snap rotation to 45 degree steps.</string>
<string>Show Statistical Summary</string>
</property>
</action>
<action name="mActionSensorDock">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mSensorDock.svg</normaloff>:/images/themes/default/mSensorDock.svg</iconset>
</property>
<property name="text">
<string>Sensors</string>
</property>
<property name="toolTip">
<string>Show Sensors</string>
</property>
</action>
<action name="mActionShowAlignRasterTool">
<property name="text">
<string>Align Rasters…</string>

View File

@ -3373,7 +3373,7 @@
</spacer>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>

View File

@ -29,11 +29,11 @@
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Sensor Settings</string>
<string>Sensors</string>
</property>
<layout class="QVBoxLayout" name="mainLayout">
<item>
<widget class="QgsSensorTableWidget" name="mSensorTableWidget"/>
<widget class="QgsPanelWidgetStack" name="mPanelStack" native="true"/>
</item>
</layout>
</widget>
@ -42,9 +42,9 @@
</widget>
<customwidgets>
<customwidget>
<class>QgsSensorTableWidget</class>
<class>QgsPanelWidgetStack</class>
<extends>QWidget</extends>
<header>qgssensortablewidget.h</header>
<header>qgspanelwidgetstack.h</header>
<container>1</container>
</customwidget>
</customwidgets>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsSensorSettingsWidgetBase</class>
<widget class="QgsPanelWidget" name="QgsSensorSettingsWidgetBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>291</width>
<height>376</height>
</rect>
</property>
<layout class="QVBoxLayout" name="mMainLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="mNameLabel">
<property name="text">
<string>Sensor name</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mNameLineEdit"/>
</item>
<item>
<widget class="QLabel" name="mTypeLabel">
<property name="text">
<string>Sensor type</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mTypeComboBox"/>
</item>
<item>
<widget class="QGroupBox" name="mTypeGroupBox">
<layout class="QVBoxLayout" name="mTypeLayout"/>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="mButtonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Apply</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsPanelWidget</class>
<extends>QWidget</extends>
<header>qgspanelwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../images/images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -10,9 +10,6 @@
<height>376</height>
</rect>
</property>
<property name="windowTitle">
<string>Sensors Table</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
@ -58,6 +55,48 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mActionAddSensor">
<property name="toolTip">
<string>Create new sensor</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyAdd.svg</normaloff>:/images/themes/default/symbologyAdd.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mActionRemoveSensor">
<property name="toolTip">
<string>Create new sensor</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyRemove.svg</normaloff>:/images/themes/default/symbologyRemove.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mActionEditSensor">
<property name="toolTip">
<string>Edit sensor</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyEdit.svg</normaloff>:/images/themes/default/symbologyEdit.svg</iconset>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@ -78,7 +117,7 @@
<customwidgets>
<customwidget>
<class>QgsPanelWidget</class>
<extends>QgsPanelWidget</extends>
<extends>QWidget</extends>
<header>qgspanelwidget.h</header>
<container>1</container>
</customwidget>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WidgetSerialPortSensor</class>
<widget class="QWidget" name="WidgetSerialPortSensor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>371</width>
<height>409</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="mSerialPortLabel">
<property name="text">
<string>Serial port name</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mSerialPortComboBox"/>
</item>
<item>
<widget class="QLabel" name="mSerialPortDetails">
<property name="enabled">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WidgetTcpSocketSensor</class>
<widget class="QWidget" name="WidgetTcpSocketSensor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>371</width>
<height>409</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="mHostNameLabel">
<property name="text">
<string>Host name (a domain string or IP address)</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mHostNameLineEdit"/>
</item>
<item>
<widget class="QLabel" name="mPortLabel">
<property name="text">
<string>Port</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QgsSpinBox" name="mPortSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="showClearButton" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsSpinBox</class>
<extends>QSpinBox</extends>
<header>qgsspinbox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WidgetUdpSocketSensor</class>
<widget class="QWidget" name="WidgetUdpSocketSensor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>371</width>
<height>409</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="mHostNameLabel">
<property name="text">
<string>Host name (a domain string or IP address)</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mHostNameLineEdit"/>
</item>
<item>
<widget class="QLabel" name="mPortLabel">
<property name="text">
<string>Port</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QgsSpinBox" name="mPortSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="showClearButton" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsSpinBox</class>
<extends>QSpinBox</extends>
<header>qgsspinbox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>