Skip to content

Commit

Permalink
[FEATURE] Locked aspect ratio state for Save as image/PDF" (#4880)
Browse files Browse the repository at this point in the history
Sponsored by Andreas Neumann.
  • Loading branch information
nirvn committed Jul 19, 2017
1 parent 08c06de commit 0665072
Show file tree
Hide file tree
Showing 13 changed files with 427 additions and 39 deletions.
1 change: 1 addition & 0 deletions images/images.qrc
Expand Up @@ -467,6 +467,7 @@
<file>themes/default/transformed.svg</file>
<file>themes/default/transp-background_8x8.png</file>
<file>themes/default/unlocked.svg</file>
<file>themes/default/unlockedGray.svg</file>
<file>themes/default/user.svg</file>
<file>flags/eu.png</file>
<file>flags/bn.png</file>
Expand Down
1 change: 1 addition & 0 deletions images/themes/default/unlockedGray.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions python/gui/gui_auto.sip
Expand Up @@ -124,6 +124,7 @@
%Include qgslistwidget.sip
%Include qgslegendfilterbutton.sip
%Include qgslimitedrandomcolorrampdialog.sip
%Include qgsratiolockbutton.sip
%Include qgslonglongvalidator.sip
%Include qgsludialog.sip
%Include qgsmanageconnectionsdialog.sip
Expand Down
18 changes: 18 additions & 0 deletions python/gui/qgsextentgroupbox.sip
Expand Up @@ -163,6 +163,24 @@ class QgsExtentGroupBox : QgsCollapsibleGroupBox
.. versionadded:: 3.0
%End

void setRatio( QSize ratio );
%Docstring
Sets a fixed aspect ratio to be used when dragging extent onto the canvas.
To unset a fixed aspect ratio, set the width and height to zero.
\param ratio aspect ratio's width and height
.. versionadded:: 3.0
*
%End

QSize ratio() const;
%Docstring
Returns the current fixed aspect ratio to be used when dragging extent onto the canvas.
If the aspect ratio isn't fixed, the width and height will be set to zero.
.. versionadded:: 3.0
*
:rtype: QSize
%End

signals:

void extentChanged( const QgsRectangle &r );
Expand Down
69 changes: 69 additions & 0 deletions python/gui/qgsratiolockbutton.sip
@@ -0,0 +1,69 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsratiolockbutton.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsRatioLockButton : QToolButton
{
%Docstring
A cross platform button subclass used to represent a locked / unlocked ratio state.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsratiolockbutton.h"
%End
public:

QgsRatioLockButton( QWidget *parent /TransferThis/ = 0 );
%Docstring
Construct a new ratio lock button.
Use ``parent`` to attach a parent QWidget to the button.
%End

void setLocked( const bool locked );
%Docstring
Sets whether the button state is locked.
\param locked locked state
.. seealso:: locked
%End

bool locked() const;
%Docstring
Returns whether the button state is locked.
:return: true if the button state is locked.
.. seealso:: setLocked
:rtype: bool
%End

signals:

void lockChanged( const bool locked );
%Docstring
Emitted whenever the lock state changes.
%End

protected:

virtual void changeEvent( QEvent *e );

virtual void showEvent( QShowEvent *e );

virtual void resizeEvent( QResizeEvent *event );


};

/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsratiolockbutton.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
52 changes: 48 additions & 4 deletions src/app/qgsmapsavedialog.cpp
Expand Up @@ -81,6 +81,7 @@ QgsMapSaveDialog::QgsMapSaveDialog( QWidget *parent, QgsMapCanvas *mapCanvas, QL
connect( mOutputHeightSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsMapSaveDialog::updateOutputHeight );
connect( mExtentGroupBox, &QgsExtentGroupBox::extentChanged, this, &QgsMapSaveDialog::updateExtent );
connect( mScaleWidget, &QgsScaleWidget::scaleChanged, this, &QgsMapSaveDialog::updateScale );
connect( mLockAspectRatio, &QgsRatioLockButton::lockChanged, this, &QgsMapSaveDialog::lockChanged );

updateOutputSize();

Expand Down Expand Up @@ -126,25 +127,51 @@ void QgsMapSaveDialog::updateOutputWidth( int width )
double scale = ( double )width / mSize.width();
double adjustment = ( ( mExtent.width() * scale ) - mExtent.width() ) / 2;

mSize.setWidth( width );

mExtent.setXMinimum( mExtent.xMinimum() - adjustment );
mExtent.setXMaximum( mExtent.xMaximum() + adjustment );

whileBlocking( mExtentGroupBox )->setOutputExtentFromUser( mExtent, mExtentGroupBox->currentCrs() );
if ( mLockAspectRatio->locked() )
{
int height = width * mExtentGroupBox->ratio().height() / mExtentGroupBox->ratio().width();
double scale = ( double )height / mSize.height();
double adjustment = ( ( mExtent.height() * scale ) - mExtent.height() ) / 2;

mSize.setWidth( width );
whileBlocking( mOutputHeightSpinBox )->setValue( height );
mSize.setHeight( height );

mExtent.setYMinimum( mExtent.yMinimum() - adjustment );
mExtent.setYMaximum( mExtent.yMaximum() + adjustment );
}

whileBlocking( mExtentGroupBox )->setOutputExtentFromUser( mExtent, mExtentGroupBox->currentCrs() );
}

void QgsMapSaveDialog::updateOutputHeight( int height )
{
double scale = ( double )height / mSize.height();
double adjustment = ( ( mExtent.height() * scale ) - mExtent.height() ) / 2;

mSize.setHeight( height );

mExtent.setYMinimum( mExtent.yMinimum() - adjustment );
mExtent.setYMaximum( mExtent.yMaximum() + adjustment );

whileBlocking( mExtentGroupBox )->setOutputExtentFromUser( mExtent, mExtentGroupBox->currentCrs() );
if ( mLockAspectRatio->locked() )
{
int width = height * mExtentGroupBox->ratio().width() / mExtentGroupBox->ratio().height();
double scale = ( double )width / mSize.width();
double adjustment = ( ( mExtent.width() * scale ) - mExtent.width() ) / 2;

mSize.setHeight( height );
whileBlocking( mOutputWidthSpinBox )->setValue( height );
mSize.setWidth( width );

mExtent.setXMinimum( mExtent.xMinimum() - adjustment );
mExtent.setXMaximum( mExtent.xMaximum() + adjustment );
}

whileBlocking( mExtentGroupBox )->setOutputExtentFromUser( mExtent, mExtentGroupBox->currentCrs() );
}

void QgsMapSaveDialog::updateExtent( const QgsRectangle &extent )
Expand All @@ -153,6 +180,11 @@ void QgsMapSaveDialog::updateExtent( const QgsRectangle &extent )
mSize.setHeight( mSize.height() * extent.height() / mExtent.height() );
mExtent = extent;

if ( mLockAspectRatio->locked() )
{
mExtentGroupBox->setRatio( QSize( mSize.width(), mSize.height() ) );
}

updateOutputSize();
}

Expand Down Expand Up @@ -242,6 +274,18 @@ void QgsMapSaveDialog::applyMapSettings( QgsMapSettings &mapSettings )
mapSettings.setExpressionContext( expressionContext );
}

void QgsMapSaveDialog::lockChanged( const bool locked )
{
if ( locked )
{
mExtentGroupBox->setRatio( QSize( mOutputWidthSpinBox->value(), mOutputHeightSpinBox->value() ) );
}
else
{
mExtentGroupBox->setRatio( QSize( 0, 0 ) );
}
}

void QgsMapSaveDialog::accepted()
{
if ( mDialogType == Image )
Expand Down
1 change: 1 addition & 0 deletions src/app/qgsmapsavedialog.h
Expand Up @@ -77,6 +77,7 @@ class APP_EXPORT QgsMapSaveDialog: public QDialog, private Ui::QgsMapSaveDialog

private:

void lockChanged( const bool locked );
void accepted();

void updateDpi( int dpi );
Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -263,6 +263,7 @@ SET(QGIS_GUI_SRCS
qgslistwidget.cpp
qgslegendfilterbutton.cpp
qgslimitedrandomcolorrampdialog.cpp
qgsratiolockbutton.cpp
qgsludialog.cpp
qgsmanageconnectionsdialog.cpp
qgsmapcanvas.cpp
Expand Down Expand Up @@ -421,6 +422,7 @@ SET(QGIS_GUI_MOC_HDRS
qgslistwidget.h
qgslegendfilterbutton.h
qgslimitedrandomcolorrampdialog.h
qgsratiolockbutton.h
qgslonglongvalidator.h
qgsludialog.h
qgsmanageconnectionsdialog.h
Expand Down
1 change: 1 addition & 0 deletions src/gui/qgsextentgroupbox.cpp
Expand Up @@ -277,6 +277,7 @@ void QgsExtentGroupBox::setOutputExtentFromDrawOnCanvas()
mMapToolPrevious = nullptr;
} );
}
mMapToolExtent->setRatio( mRatio );
mCanvas->setMapTool( mMapToolExtent.get() );
window()->setVisible( false );
}
Expand Down
14 changes: 14 additions & 0 deletions src/gui/qgsextentgroupbox.h
Expand Up @@ -177,6 +177,19 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
*/
void setOutputExtentFromDrawOnCanvas();

/** Sets a fixed aspect ratio to be used when dragging extent onto the canvas.
* To unset a fixed aspect ratio, set the width and height to zero.
* \param ratio aspect ratio's width and height
* \since QGIS 3.0
* */
void setRatio( QSize ratio ) { mRatio = ratio; }

/** Returns the current fixed aspect ratio to be used when dragging extent onto the canvas.
* If the aspect ratio isn't fixed, the width and height will be set to zero.
* \since QGIS 3.0
* */
QSize ratio() const { return mRatio; }

signals:

/**
Expand Down Expand Up @@ -223,6 +236,7 @@ class GUI_EXPORT QgsExtentGroupBox : public QgsCollapsibleGroupBox, private Ui::
std::unique_ptr< QgsMapToolExtent > mMapToolExtent;
QPointer< QgsMapTool > mMapToolPrevious = nullptr;
QgsMapCanvas *mCanvas = nullptr;
QSize mRatio;

void setExtentToLayerExtent( const QString &layerId );

Expand Down
110 changes: 110 additions & 0 deletions src/gui/qgsratiolockbutton.cpp
@@ -0,0 +1,110 @@
/***************************************************************************
qgsratiolockbutton.cpp - Lock button
--------------------------------------
Date : July, 2017
Copyright : (C) 2017 by Mathieu Pellerin
Email : nirvn dot asia 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 "qgsratiolockbutton.h"

#include <QApplication>
#include <QMouseEvent>
#include <QPainter>
#include <QPushButton>
#include <QWidget>

QgsRatioLockButton::QgsRatioLockButton( QWidget *parent )
: QToolButton( parent )
, mLocked( false )

{
setMinimumSize( QSize( 24, 24 ) );
setCheckable( true );
setAutoRaise( true );
connect( this, &QPushButton::clicked, this, &QgsRatioLockButton::buttonClicked );
}

void QgsRatioLockButton::setLocked( const bool locked )
{
if ( mLocked != locked )
buttonClicked();
}

void QgsRatioLockButton::buttonClicked()
{
mLocked = !mLocked;
setDown( mLocked );

emit lockChanged( mLocked );

drawButton();
}

void QgsRatioLockButton::changeEvent( QEvent *e )
{
if ( e->type() == QEvent::EnabledChange )
{
drawButton();
}
QToolButton::changeEvent( e );
}

void QgsRatioLockButton::showEvent( QShowEvent *e )
{
drawButton();
QToolButton::showEvent( e );
}

void QgsRatioLockButton::resizeEvent( QResizeEvent *event )
{
QToolButton::resizeEvent( event );
drawButton();
}

void QgsRatioLockButton::drawButton()
{
QSize currentIconSize;

#ifdef Q_OS_WIN
currentIconSize = QSize( width() - 10, height() - 6 );
#else
currentIconSize = QSize( width() - 10, height() - 12 );
#endif

if ( !currentIconSize.isValid() || currentIconSize.width() <= 0 || currentIconSize.height() <= 0 )
{
return;
}

QPixmap pm;
pm = QPixmap( currentIconSize );
pm.fill( Qt::transparent );

QPainter painter;
QPen pen = ( QColor( 136, 136, 136 ) );
pen.setWidth( 2 );

painter.begin( &pm );
painter.setPen( pen );

painter.drawLine( 1, 1, currentIconSize.width() / 2, 1 );
painter.drawLine( currentIconSize.width() / 2, 1, currentIconSize.width() / 2, currentIconSize.height() / 2 - 13 );
painter.drawLine( currentIconSize.width() / 2, currentIconSize.height() / 2 + 13, currentIconSize.width() / 2, currentIconSize.height() - 2 );
painter.drawLine( currentIconSize.width() / 2, currentIconSize.height() - 2, 1, currentIconSize.height() - 2 );

QImage image( mLocked ? QStringLiteral( ":/images/themes/default/lockedGray.svg" ) : QStringLiteral( ":/images/themes/default/unlockedGray.svg" ) );
painter.drawImage( QRectF( currentIconSize.width() / 2 - 8, currentIconSize.height() / 2 - 8, 16, 16 ), image, QRectF( 0, 0, 16, 16 ) );

painter.end();

setIconSize( currentIconSize );
setIcon( pm );
}

0 comments on commit 0665072

Please sign in to comment.