From 3021fc86d26fdea0c820a84adaeb0b2bcb35d417 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 21 Jul 2017 13:49:39 +1000 Subject: [PATCH] QgsLayoutUnitComboBox can be linked to spin boxes so that their values are automatically updated when the combo box unit changes This means that you can flip between units and things like the existing width and height are converted immediately to the new unit --- .../qgslayoutnewitempropertiesdialog.sip | 7 ++++ python/gui/layout/qgslayoutunitscombobox.sip | 28 +++++++++++++ .../qgslayoutnewitempropertiesdialog.cpp | 13 ++++++ .../layout/qgslayoutnewitempropertiesdialog.h | 7 ++++ src/gui/layout/qgslayoutunitscombobox.cpp | 36 ++++++++++++++-- src/gui/layout/qgslayoutunitscombobox.h | 41 +++++++++++++++++++ src/gui/layout/qgslayoutviewtooladditem.cpp | 1 + .../src/python/test_qgslayoutunitscombobox.py | 35 +++++++++++++++- 8 files changed, 163 insertions(+), 5 deletions(-) diff --git a/python/gui/layout/qgslayoutnewitempropertiesdialog.sip b/python/gui/layout/qgslayoutnewitempropertiesdialog.sip index a4c5110af04..d31481846d6 100644 --- a/python/gui/layout/qgslayoutnewitempropertiesdialog.sip +++ b/python/gui/layout/qgslayoutnewitempropertiesdialog.sip @@ -69,6 +69,13 @@ class QgsLayoutItemPropertiesDialog : QDialog .. seealso:: referencePoint() %End + void setLayout( QgsLayout *layout ); +%Docstring + Sets the ``layout`` associated with the dialog. This allows the dialog + to retrieve properties from the layout and perform tasks like automatic + conversion of units. +%End + }; /************************************************************************ diff --git a/python/gui/layout/qgslayoutunitscombobox.sip b/python/gui/layout/qgslayoutunitscombobox.sip index 5b7027d8b95..1ee3a111604 100644 --- a/python/gui/layout/qgslayoutunitscombobox.sip +++ b/python/gui/layout/qgslayoutunitscombobox.sip @@ -7,6 +7,7 @@ ************************************************************************/ + class QgsLayoutUnitsComboBox : QComboBox { %Docstring @@ -36,6 +37,33 @@ class QgsLayoutUnitsComboBox : QComboBox %Docstring Sets the ``unit`` currently selected in the combo box. .. seealso:: unit() +%End + + void linkToWidget( QDoubleSpinBox *widget ); +%Docstring + Registers a spin box ``widget`` as linked with the combo box. + + Registered spin boxes will automatically be upodated whenever the unit is changed. I.e. a + spin box with a value of 100 will be set to 1 when the unit is changed from centimeters to meters. + + A measurement converter() must be set in order for the automatic unit conversion to occur. + +.. seealso:: setConverter() +%End + + QgsLayoutMeasurementConverter *converter() const; +%Docstring + Returns the converter used when automatically converting units for linked widgets. +.. seealso:: setConverter() + :rtype: QgsLayoutMeasurementConverter +%End + + void setConverter( QgsLayoutMeasurementConverter *converter ); +%Docstring + Sets a ``converter`` to use when automatically converting units for linked widgets. + The ownership of ``converter`` is not transferred, and converter must exist for the + life of the combo box. +.. seealso:: converter() %End signals: diff --git a/src/gui/layout/qgslayoutnewitempropertiesdialog.cpp b/src/gui/layout/qgslayoutnewitempropertiesdialog.cpp index 0aa0ba4f935..3905208b50e 100644 --- a/src/gui/layout/qgslayoutnewitempropertiesdialog.cpp +++ b/src/gui/layout/qgslayoutnewitempropertiesdialog.cpp @@ -15,6 +15,8 @@ #include "qgslayoutnewitempropertiesdialog.h" #include "qgssettings.h" +#include "qgslayout.h" + QgsLayoutItemPropertiesDialog::QgsLayoutItemPropertiesDialog( QWidget *parent, Qt::WindowFlags flags ) : QDialog( parent, flags ) @@ -39,6 +41,11 @@ QgsLayoutItemPropertiesDialog::QgsLayoutItemPropertiesDialog( QWidget *parent, Q double lastHeight = settings.value( QStringLiteral( "LayoutDesigner/lastItemHeight" ), QStringLiteral( "50" ) ).toDouble(); QgsUnitTypes::LayoutUnit lastSizeUnit = static_cast< QgsUnitTypes::LayoutUnit >( settings.value( QStringLiteral( "LayoutDesigner/lastSizeUnit" ) ).toInt() ); setItemSize( QgsLayoutSize( lastWidth, lastHeight, lastSizeUnit ) ); + + mPosUnitsComboBox->linkToWidget( mXPosSpin ); + mPosUnitsComboBox->linkToWidget( mYPosSpin ); + mSizeUnitsComboBox->linkToWidget( mWidthSpin ); + mSizeUnitsComboBox->linkToWidget( mHeightSpin ); } void QgsLayoutItemPropertiesDialog::setItemPosition( QgsLayoutPoint position ) @@ -147,3 +154,9 @@ void QgsLayoutItemPropertiesDialog::setReferencePoint( QgsLayoutItem::ReferenceP break; } } + +void QgsLayoutItemPropertiesDialog::setLayout( QgsLayout *layout ) +{ + mSizeUnitsComboBox->setConverter( &layout->context().measurementConverter() ); + mPosUnitsComboBox->setConverter( &layout->context().measurementConverter() ); +} diff --git a/src/gui/layout/qgslayoutnewitempropertiesdialog.h b/src/gui/layout/qgslayoutnewitempropertiesdialog.h index 736125bfd21..166e61e86be 100644 --- a/src/gui/layout/qgslayoutnewitempropertiesdialog.h +++ b/src/gui/layout/qgslayoutnewitempropertiesdialog.h @@ -80,6 +80,13 @@ class GUI_EXPORT QgsLayoutItemPropertiesDialog : public QDialog, private Ui::Qgs */ void setReferencePoint( QgsLayoutItem::ReferencePoint point ); + /** + * Sets the \a layout associated with the dialog. This allows the dialog + * to retrieve properties from the layout and perform tasks like automatic + * conversion of units. + */ + void setLayout( QgsLayout *layout ); + }; #endif // QGSLAYOUTNEWITEMPROPERTIESDIALOG_H diff --git a/src/gui/layout/qgslayoutunitscombobox.cpp b/src/gui/layout/qgslayoutunitscombobox.cpp index 9517c256c64..ba6d1081751 100644 --- a/src/gui/layout/qgslayoutunitscombobox.cpp +++ b/src/gui/layout/qgslayoutunitscombobox.cpp @@ -14,6 +14,7 @@ ***************************************************************************/ #include "qgslayoutunitscombobox.h" +#include "qgslayoutmeasurementconverter.h" QgsLayoutUnitsComboBox::QgsLayoutUnitsComboBox( QWidget *parent ) : QComboBox( parent ) @@ -34,10 +35,7 @@ QgsLayoutUnitsComboBox::QgsLayoutUnitsComboBox( QWidget *parent ) setItemData( 6, tr( "Picas" ), Qt::ToolTipRole ); addItem( tr( "px" ), QgsUnitTypes::LayoutPixels ); setItemData( 7, tr( "Pixels" ), Qt::ToolTipRole ); - connect( this, static_cast( &QgsLayoutUnitsComboBox::currentIndexChanged ), this, [ = ]( int ) - { - emit changed( unit() ); - } ); + connect( this, static_cast( &QgsLayoutUnitsComboBox::currentIndexChanged ), this, &QgsLayoutUnitsComboBox::indexChanged ); } QgsUnitTypes::LayoutUnit QgsLayoutUnitsComboBox::unit() const @@ -50,4 +48,34 @@ void QgsLayoutUnitsComboBox::setUnit( QgsUnitTypes::LayoutUnit unit ) setCurrentIndex( findData( unit ) ); } +void QgsLayoutUnitsComboBox::linkToWidget( QDoubleSpinBox *widget ) +{ + mLinkedSpinBoxes << widget; +} + +void QgsLayoutUnitsComboBox::indexChanged( int ) +{ + QgsUnitTypes::LayoutUnit newUnit = unit(); + if ( mConverter ) + { + Q_FOREACH ( const QPointer< QDoubleSpinBox > &widget, mLinkedSpinBoxes ) + { + if ( widget ) + widget->setValue( mConverter->convert( QgsLayoutMeasurement( widget->value(), mOldUnit ), newUnit ).length() ); + } + } + emit changed( newUnit ); + mOldUnit = newUnit; +} + +QgsLayoutMeasurementConverter *QgsLayoutUnitsComboBox::converter() const +{ + return mConverter; +} + +void QgsLayoutUnitsComboBox::setConverter( QgsLayoutMeasurementConverter *converter ) +{ + mConverter = converter; +} + #include "qgslayoutunitscombobox.h" diff --git a/src/gui/layout/qgslayoutunitscombobox.h b/src/gui/layout/qgslayoutunitscombobox.h index 7fbfff11de9..0df9c1a74e8 100644 --- a/src/gui/layout/qgslayoutunitscombobox.h +++ b/src/gui/layout/qgslayoutunitscombobox.h @@ -19,6 +19,10 @@ #include "qgis_gui.h" #include "qgis_sip.h" #include "qgsunittypes.h" +#include +#include + +class QgsLayoutMeasurementConverter; /** * \ingroup gui @@ -50,6 +54,32 @@ class GUI_EXPORT QgsLayoutUnitsComboBox : public QComboBox */ void setUnit( QgsUnitTypes::LayoutUnit unit ); + /** + * Registers a spin box \a widget as linked with the combo box. + * + * Registered spin boxes will automatically be upodated whenever the unit is changed. I.e. a + * spin box with a value of 100 will be set to 1 when the unit is changed from centimeters to meters. + * + * A measurement converter() must be set in order for the automatic unit conversion to occur. + * + * \see setConverter() + */ + void linkToWidget( QDoubleSpinBox *widget ); + + /** + * Returns the converter used when automatically converting units for linked widgets. + * \see setConverter() + */ + QgsLayoutMeasurementConverter *converter() const; + + /** + * Sets a \a converter to use when automatically converting units for linked widgets. + * The ownership of \a converter is not transferred, and converter must exist for the + * life of the combo box. + * \see converter() + */ + void setConverter( QgsLayoutMeasurementConverter *converter ); + signals: /** @@ -57,6 +87,17 @@ class GUI_EXPORT QgsLayoutUnitsComboBox : public QComboBox */ void changed( QgsUnitTypes::LayoutUnit unit ); + private slots: + + void indexChanged( int index ); + + private: + + QgsLayoutMeasurementConverter *mConverter = nullptr; + + QgsUnitTypes::LayoutUnit mOldUnit = QgsUnitTypes::LayoutMillimeters; + + QList< QPointer< QDoubleSpinBox > > mLinkedSpinBoxes; }; #endif // QGSLAYOUTUNITSCOMBOBOX_H diff --git a/src/gui/layout/qgslayoutviewtooladditem.cpp b/src/gui/layout/qgslayoutviewtooladditem.cpp index b172424e3a6..b4d62970354 100644 --- a/src/gui/layout/qgslayoutviewtooladditem.cpp +++ b/src/gui/layout/qgslayoutviewtooladditem.cpp @@ -90,6 +90,7 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even if ( clickOnly ) { QgsLayoutItemPropertiesDialog dlg( view() ); + dlg.setLayout( layout() ); dlg.setItemPosition( QgsLayoutPoint( event->layoutPoint(), layout()->units() ) ); if ( dlg.exec() ) { diff --git a/tests/src/python/test_qgslayoutunitscombobox.py b/tests/src/python/test_qgslayoutunitscombobox.py index f81c42f390e..e7c2444cac0 100644 --- a/tests/src/python/test_qgslayoutunitscombobox.py +++ b/tests/src/python/test_qgslayoutunitscombobox.py @@ -14,9 +14,11 @@ __revision__ = '$Format:%H$' import qgis # NOQA -from qgis.core import QgsUnitTypes +from qgis.core import QgsUnitTypes, QgsLayoutMeasurementConverter from qgis.gui import QgsLayoutUnitsComboBox +from qgis.PyQt.QtWidgets import QDoubleSpinBox + from qgis.PyQt.QtTest import QSignalSpy from qgis.testing import start_app, unittest @@ -42,6 +44,37 @@ class TestQgsLayoutUnitsComboBox(unittest.TestCase): self.assertEqual(len(spy), 1) self.assertEqual(spy[0][0], QgsUnitTypes.LayoutPixels) + def testLinkedWidgets(self): + """ test linking spin boxes to combobox""" + w = qgis.gui.QgsLayoutUnitsComboBox() + self.assertFalse(w.converter()) + c = QgsLayoutMeasurementConverter() + w.setConverter(c) + self.assertEqual(w.converter(), c) + + spin = QDoubleSpinBox() + spin.setMaximum(1000000) + spin.setValue(100) + w.setUnit(QgsUnitTypes.LayoutCentimeters) + w.linkToWidget(spin) + w.setUnit(QgsUnitTypes.LayoutMeters) + self.assertAlmostEqual(spin.value(), 1.0, 2) + w.setUnit(QgsUnitTypes.LayoutMillimeters) + self.assertAlmostEqual(spin.value(), 1000.0, 2) + + spin2 = QDoubleSpinBox() + spin2.setValue(50) + spin2.setMaximum(1000000) + w.linkToWidget(spin2) + w.setUnit(QgsUnitTypes.LayoutCentimeters) + self.assertAlmostEqual(spin.value(), 100.0, 2) + self.assertAlmostEqual(spin2.value(), 5.0, 2) + + # no crash! + del spin + w.setUnit(QgsUnitTypes.LayoutMeters) + self.assertAlmostEqual(spin2.value(), 0.05, 2) + if __name__ == '__main__': unittest.main()