27 #include <QDoubleValidator> 28 #include <QFileDialog> 30 #include <QImageReader> 31 #include <QMessageBox> 33 #include <QProgressDialog> 34 #include <QSvgRenderer> 58 connect( mPictureBrowseButton, &QPushButton::clicked,
this, &QgsLayoutPictureWidget::mPictureBrowseButton_clicked );
59 connect( mPictureLineEdit, &QLineEdit::editingFinished,
this, &QgsLayoutPictureWidget::mPictureLineEdit_editingFinished );
60 connect( mPictureRotationSpinBox, static_cast <
void ( QDoubleSpinBox::* )(
double ) > ( &QDoubleSpinBox::valueChanged ),
this, &QgsLayoutPictureWidget::mPictureRotationSpinBox_valueChanged );
61 connect( mPreviewListWidget, &QListWidget::currentItemChanged,
this, &QgsLayoutPictureWidget::mPreviewListWidget_currentItemChanged );
62 connect( mAddDirectoryButton, &QPushButton::clicked,
this, &QgsLayoutPictureWidget::mAddDirectoryButton_clicked );
63 connect( mRemoveDirectoryButton, &QPushButton::clicked,
this, &QgsLayoutPictureWidget::mRemoveDirectoryButton_clicked );
64 connect( mRotationFromComposerMapCheckBox, &QCheckBox::stateChanged,
this, &QgsLayoutPictureWidget::mRotationFromComposerMapCheckBox_stateChanged );
65 connect( mResizeModeComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsLayoutPictureWidget::mResizeModeComboBox_currentIndexChanged );
66 connect( mAnchorPointComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsLayoutPictureWidget::mAnchorPointComboBox_currentIndexChanged );
69 connect( mStrokeWidthSpinBox, static_cast <
void ( QDoubleSpinBox::* )(
double ) > ( &QDoubleSpinBox::valueChanged ),
this, &QgsLayoutPictureWidget::mStrokeWidthSpinBox_valueChanged );
70 connect( mPictureRotationOffsetSpinBox, static_cast <
void ( QDoubleSpinBox::* )(
double ) > ( &QDoubleSpinBox::valueChanged ),
this, &QgsLayoutPictureWidget::mPictureRotationOffsetSpinBox_valueChanged );
71 connect( mNorthTypeComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsLayoutPictureWidget::mNorthTypeComboBox_currentIndexChanged );
74 mFillColorButton->setAllowOpacity(
true );
75 mFillColorButton->setColorDialogTitle( tr(
"Select Fill Color" ) );
76 mFillColorButton->setContext( QStringLiteral(
"composer" ) );
77 mStrokeColorButton->setAllowOpacity(
true );
78 mStrokeColorButton->setColorDialogTitle( tr(
"Select Stroke Color" ) );
79 mStrokeColorButton->setContext( QStringLiteral(
"composer" ) );
81 mFillColorDDBtn->registerLinkedWidget( mFillColorButton );
82 mStrokeColorDDBtn->registerLinkedWidget( mStrokeColorButton );
84 mNorthTypeComboBox->blockSignals(
true );
87 mNorthTypeComboBox->blockSignals(
false );
88 mPictureRotationOffsetSpinBox->setClearValue( 0.0 );
89 mPictureRotationSpinBox->setClearValue( 0.0 );
93 mainLayout->addWidget( mItemPropertiesWidget );
95 if ( mPicture->layout() )
97 mComposerMapComboBox->setCurrentLayout( mPicture->layout() );
102 setGuiElementValues();
103 mPreviewsLoadingLabel->hide();
105 mPreviewListWidget->setIconSize( QSize( 30, 30 ) );
108 mSearchDirectoriesGroupBox->setCollapsed(
true );
125 if ( mItemPropertiesWidget )
129 void QgsLayoutPictureWidget::mPictureBrowseButton_clicked()
133 QString lineEditText = mPictureLineEdit->text();
134 if ( !lineEditText.isEmpty() )
136 QFileInfo openDirFileInfo( lineEditText );
137 openDir = openDirFileInfo.path();
140 if ( openDir.isEmpty() )
142 openDir = s.
value( QStringLiteral(
"/UI/lastComposerPictureDir" ), QDir::homePath() ).toString();
146 QString filePath = QFileDialog::getOpenFileName(
this, tr(
"Select SVG or Image File" ), openDir );
147 if ( filePath.isEmpty() )
153 QFileInfo fileInfo( filePath );
154 if ( !fileInfo.exists() || !fileInfo.isReadable() )
156 QMessageBox::critical(
nullptr, tr(
"Select File" ), tr(
"Error, file does not exist or is not readable." ) );
160 s.
setValue( QStringLiteral(
"/UI/lastComposerPictureDir" ), fileInfo.absolutePath() );
162 mPictureLineEdit->blockSignals(
true );
163 mPictureLineEdit->setText( filePath );
164 mPictureLineEdit->blockSignals(
false );
170 mPicture->beginCommand( tr(
"Change Picture" ) );
171 mPicture->setPicturePath( filePath );
173 mPicture->endCommand();
177 void QgsLayoutPictureWidget::mPictureLineEdit_editingFinished()
181 QString filePath = mPictureLineEdit->text();
183 mPicture->beginCommand( tr(
"Change Picture" ) );
184 mPicture->setPicturePath( filePath );
186 mPicture->endCommand();
191 void QgsLayoutPictureWidget::mPictureRotationSpinBox_valueChanged(
double d )
196 mPicture->setPictureRotation( d );
197 mPicture->endCommand();
201 void QgsLayoutPictureWidget::mPreviewListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous )
204 if ( !mPicture || !current )
209 QString absoluteFilePath = current->data( Qt::UserRole ).toString();
210 mPicture->beginCommand( tr(
"Change Picture" ) );
211 mPicture->setPicturePath( absoluteFilePath );
212 mPictureLineEdit->setText( absoluteFilePath );
214 mPicture->endCommand();
218 void QgsLayoutPictureWidget::mAddDirectoryButton_clicked()
221 QString directory = QFileDialog::getExistingDirectory(
this, tr(
"Select New Preview Directory" ) );
222 if ( directory.isNull() )
228 mSearchDirectoriesComboBox->addItem( directory );
231 addDirectoryToPreview( directory );
235 QStringList userDirList = s.
value( QStringLiteral(
"/Composer/PictureWidgetDirectories" ) ).toStringList();
236 if ( !userDirList.contains( directory ) )
238 userDirList.append( directory );
240 s.
setValue( QStringLiteral(
"/Composer/PictureWidgetDirectories" ), userDirList );
243 void QgsLayoutPictureWidget::mRemoveDirectoryButton_clicked()
245 QString directoryToRemove = mSearchDirectoriesComboBox->currentText();
246 if ( directoryToRemove.isEmpty() )
250 mSearchDirectoriesComboBox->removeItem( mSearchDirectoriesComboBox->currentIndex() );
253 for (
int i = ( mPreviewListWidget->count() - 1 ); i >= 0; --i )
255 QListWidgetItem *currentItem = mPreviewListWidget->item( i );
256 if ( currentItem && currentItem->data( Qt::UserRole ).toString().startsWith( directoryToRemove ) )
258 delete ( mPreviewListWidget->takeItem( i ) );
264 QStringList userDirList = s.
value( QStringLiteral(
"/Composer/PictureWidgetDirectories" ) ).toStringList();
265 userDirList.removeOne( directoryToRemove );
266 s.
setValue( QStringLiteral(
"/Composer/PictureWidgetDirectories" ), userDirList );
269 void QgsLayoutPictureWidget::mResizeModeComboBox_currentIndexChanged(
int )
276 mPicture->beginCommand( tr(
"Change Resize Mode" ) );
277 mPicture->setResizeMode( static_cast< QgsLayoutItemPicture::ResizeMode >( mResizeModeComboBox->currentData().toInt() ) );
278 mPicture->endCommand();
288 mAnchorPointComboBox->setEnabled(
true );
292 mAnchorPointComboBox->setEnabled(
false );
296 void QgsLayoutPictureWidget::mAnchorPointComboBox_currentIndexChanged(
int )
303 mPicture->beginCommand( tr(
"Change Placement" ) );
304 mPicture->setPictureAnchor( static_cast< QgsLayoutItem::ReferencePoint >( mAnchorPointComboBox->currentData().toInt() ) );
305 mPicture->endCommand();
320 mItemPropertiesWidget->
setItem( mPicture );
328 setGuiElementValues();
333 void QgsLayoutPictureWidget::mRotationFromComposerMapCheckBox_stateChanged(
int state )
340 mPicture->beginCommand( tr(
"Toggle Rotation Sync" ) );
341 if ( state == Qt::Unchecked )
343 mPicture->setLinkedMap(
nullptr );
344 mPictureRotationSpinBox->setEnabled(
true );
345 mComposerMapComboBox->setEnabled(
false );
346 mNorthTypeComboBox->setEnabled(
false );
347 mPictureRotationOffsetSpinBox->setEnabled(
false );
348 mPicture->setPictureRotation( mPictureRotationSpinBox->value() );
353 mPicture->setLinkedMap( map );
354 mPictureRotationSpinBox->setEnabled(
false );
355 mNorthTypeComboBox->setEnabled(
true );
356 mPictureRotationOffsetSpinBox->setEnabled(
true );
357 mComposerMapComboBox->setEnabled(
true );
359 mPicture->endCommand();
362 void QgsLayoutPictureWidget::mapChanged(
QgsLayoutItem *item )
370 const QgsLayout *layout = mPicture->layout();
382 mPicture->beginCommand( tr(
"Change Rotation Map" ) );
383 mPicture->setLinkedMap( map );
385 mPicture->endCommand();
388 void QgsLayoutPictureWidget::setPicRotationSpinValue(
double r )
390 mPictureRotationSpinBox->blockSignals(
true );
391 mPictureRotationSpinBox->setValue( r );
392 mPictureRotationSpinBox->blockSignals(
false );
395 void QgsLayoutPictureWidget::setGuiElementValues()
400 mPictureRotationSpinBox->blockSignals(
true );
401 mPictureLineEdit->blockSignals(
true );
402 mComposerMapComboBox->blockSignals(
true );
403 mRotationFromComposerMapCheckBox->blockSignals(
true );
404 mNorthTypeComboBox->blockSignals(
true );
405 mPictureRotationOffsetSpinBox->blockSignals(
true );
406 mResizeModeComboBox->blockSignals(
true );
407 mAnchorPointComboBox->blockSignals(
true );
408 mFillColorButton->blockSignals(
true );
409 mStrokeColorButton->blockSignals(
true );
410 mStrokeWidthSpinBox->blockSignals(
true );
412 mPictureLineEdit->setText( mPicture->picturePath() );
413 mPictureRotationSpinBox->setValue( mPicture->pictureRotation() );
415 mComposerMapComboBox->setItem( mPicture->linkedMap() );
417 if ( mPicture->linkedMap() )
419 mRotationFromComposerMapCheckBox->setCheckState( Qt::Checked );
420 mPictureRotationSpinBox->setEnabled(
false );
421 mComposerMapComboBox->setEnabled(
true );
422 mNorthTypeComboBox->setEnabled(
true );
423 mPictureRotationOffsetSpinBox->setEnabled(
true );
427 mRotationFromComposerMapCheckBox->setCheckState( Qt::Unchecked );
428 mPictureRotationSpinBox->setEnabled(
true );
429 mComposerMapComboBox->setEnabled(
false );
430 mNorthTypeComboBox->setEnabled(
false );
431 mPictureRotationOffsetSpinBox->setEnabled(
false );
433 mNorthTypeComboBox->setCurrentIndex( mNorthTypeComboBox->findData( mPicture->northMode() ) );
434 mPictureRotationOffsetSpinBox->setValue( mPicture->northOffset() );
436 mResizeModeComboBox->setCurrentIndex( mResizeModeComboBox->findData( mPicture->resizeMode() ) );
441 mAnchorPointComboBox->setCurrentIndex( mAnchorPointComboBox->findData( mPicture->pictureAnchor() ) );
446 mAnchorPointComboBox->setEnabled(
true );
450 mAnchorPointComboBox->setEnabled(
false );
453 updateSvgParamGui(
false );
454 mFillColorButton->setColor( mPicture->svgFillColor() );
455 mStrokeColorButton->setColor( mPicture->svgStrokeColor() );
456 mStrokeWidthSpinBox->setValue( mPicture->svgStrokeWidth() );
458 mRotationFromComposerMapCheckBox->blockSignals(
false );
459 mPictureRotationSpinBox->blockSignals(
false );
460 mPictureLineEdit->blockSignals(
false );
461 mComposerMapComboBox->blockSignals(
false );
462 mNorthTypeComboBox->blockSignals(
false );
463 mPictureRotationOffsetSpinBox->blockSignals(
false );
464 mResizeModeComboBox->blockSignals(
false );
465 mAnchorPointComboBox->blockSignals(
false );
466 mFillColorButton->blockSignals(
false );
467 mStrokeColorButton->blockSignals(
false );
468 mStrokeWidthSpinBox->blockSignals(
false );
474 QIcon QgsLayoutPictureWidget::svgToIcon(
const QString &filePath )
const 477 double strokeWidth, fillOpacity, strokeOpacity;
478 bool fillParam, fillOpacityParam, strokeParam, strokeWidthParam, strokeOpacityParam;
479 bool hasDefaultFillColor =
false, hasDefaultFillOpacity =
false, hasDefaultStrokeColor =
false,
480 hasDefaultStrokeWidth =
false, hasDefaultStrokeOpacity =
false;
482 fillOpacityParam, hasDefaultFillOpacity, fillOpacity,
483 strokeParam, hasDefaultStrokeColor, stroke,
484 strokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
485 strokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
488 if ( !hasDefaultFillColor )
489 fill = QColor( 200, 200, 200 );
490 fill.setAlphaF( hasDefaultFillOpacity ? fillOpacity : 1.0 );
491 if ( !hasDefaultStrokeColor )
493 stroke.setAlphaF( hasDefaultStrokeOpacity ? strokeOpacity : 1.0 );
494 if ( !hasDefaultStrokeWidth )
500 return QIcon( QPixmap::fromImage( img ) );
503 void QgsLayoutPictureWidget::updateSvgParamGui(
bool resetValues )
508 QString picturePath = mPicture->picturePath();
509 if ( !picturePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
511 mFillColorButton->setEnabled(
false );
512 mStrokeColorButton->setEnabled(
false );
513 mStrokeWidthSpinBox->setEnabled(
false );
518 bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
519 QColor defaultFill, defaultStroke;
520 double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
521 bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
523 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
524 hasStrokeParam, hasDefaultStrokeColor, defaultStroke,
525 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
526 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
530 QColor fill = mFillColorButton->color();
531 double newOpacity = hasFillOpacityParam ? fill.alphaF() : 1.0;
532 if ( hasDefaultFillColor )
536 fill.setAlphaF( hasDefaultFillOpacity ? defaultFillOpacity : newOpacity );
537 mFillColorButton->setColor( fill );
539 mFillColorButton->setEnabled( hasFillParam );
540 mFillColorButton->setAllowOpacity( hasFillOpacityParam );
543 QColor stroke = mStrokeColorButton->color();
544 double newOpacity = hasStrokeOpacityParam ? stroke.alphaF() : 1.0;
545 if ( hasDefaultStrokeColor )
547 stroke = defaultStroke;
549 stroke.setAlphaF( hasDefaultStrokeOpacity ? defaultStrokeOpacity : newOpacity );
550 mStrokeColorButton->setColor( stroke );
552 mStrokeColorButton->setEnabled( hasStrokeParam );
553 mStrokeColorButton->setAllowOpacity( hasStrokeOpacityParam );
554 if ( hasDefaultStrokeWidth && resetValues )
556 mStrokeWidthSpinBox->setValue( defaultStrokeWidth );
558 mStrokeWidthSpinBox->setEnabled( hasStrokeWidthParam );
561 int QgsLayoutPictureWidget::addDirectoryToPreview(
const QString &path )
564 QDir directory( path );
565 if ( !directory.exists() || !directory.isReadable() )
570 QFileInfoList fileList = directory.entryInfoList( QDir::Files );
571 QFileInfoList::const_iterator fileIt = fileList.constBegin();
573 QProgressDialog progress( tr(
"Adding Icons…" ), tr(
"Abort" ), 0, fileList.size() - 1, this );
578 for ( ; fileIt != fileList.constEnd(); ++fileIt )
581 progress.setLabelText( tr(
"Creating icon for file %1" ).arg( fileIt->fileName() ) );
582 progress.setValue( counter );
583 QCoreApplication::processEvents();
584 if ( progress.wasCanceled() )
588 QString filePath = fileIt->absoluteFilePath();
591 bool fileIsPixel =
false;
592 bool fileIsSvg = testSvgFile( filePath );
595 fileIsPixel = testImageFile( filePath );
599 if ( !fileIsSvg && !fileIsPixel )
605 QListWidgetItem *listItem =
new QListWidgetItem( mPreviewListWidget );
606 listItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
611 QIcon icon = svgToIcon( filePath );
612 listItem->setIcon( icon );
616 QPixmap iconPixmap( filePath );
617 if ( iconPixmap.isNull() )
623 QPixmap scaledPixmap( iconPixmap.scaled( QSize( 30, 30 ), Qt::KeepAspectRatio ) );
624 QIcon icon( scaledPixmap );
625 listItem->setIcon( icon );
628 listItem->setText( QString() );
630 listItem->setData( Qt::UserRole, fileIt->absoluteFilePath() );
639 mPreviewListWidget->clear();
643 for (
int i = 0; i < svgPaths.size(); i++ )
645 QDir svgDirectory( svgPaths[i] );
646 if ( !svgDirectory.exists() || !svgDirectory.isReadable() )
652 mSearchDirectoriesComboBox->addItem( svgDirectory.absolutePath() );
653 addDirectoryToPreview( svgDirectory.absolutePath() );
656 QFileInfoList directoryList = svgDirectory.entryInfoList( QDir::Dirs | QDir::NoDotAndDotDot );
657 QFileInfoList::const_iterator dirIt = directoryList.constBegin();
658 for ( ; dirIt != directoryList.constEnd(); ++dirIt )
660 if ( addDirectoryToPreview( dirIt->absoluteFilePath() ) == 0 )
662 mSearchDirectoriesComboBox->addItem( dirIt->absoluteFilePath() );
669 QStringList userDirList = s.
value( QStringLiteral(
"/Composer/PictureWidgetDirectories" ) ).toStringList();
670 QStringList::const_iterator userDirIt = userDirList.constBegin();
671 for ( ; userDirIt != userDirList.constEnd(); ++userDirIt )
673 addDirectoryToPreview( *userDirIt );
674 mSearchDirectoriesComboBox->addItem( *userDirIt );
677 mPreviewsLoaded =
true;
680 bool QgsLayoutPictureWidget::testSvgFile(
const QString &filename )
const 684 if ( !filename.endsWith( QLatin1String(
".svg" ) ) )
689 QSvgRenderer svgRenderer( filename );
690 return svgRenderer.isValid();
693 bool QgsLayoutPictureWidget::testImageFile(
const QString &filename )
const 695 QString formatName = QString( QImageReader::imageFormat( filename ) );
696 return !formatName.isEmpty();
699 void QgsLayoutPictureWidget::loadPicturePreviews(
bool collapsed )
701 if ( mPreviewsLoaded )
708 mPreviewListWidget->hide();
709 mPreviewsLoadingLabel->show();
711 mPreviewsLoadingLabel->hide();
712 mPreviewListWidget->show();
716 void QgsLayoutPictureWidget::mFillColorButton_colorChanged(
const QColor &color )
719 mPicture->setSvgFillColor( color );
720 mPicture->endCommand();
724 void QgsLayoutPictureWidget::mStrokeColorButton_colorChanged(
const QColor &color )
727 mPicture->setSvgStrokeColor( color );
728 mPicture->endCommand();
732 void QgsLayoutPictureWidget::mStrokeWidthSpinBox_valueChanged(
double d )
735 mPicture->setSvgStrokeWidth( d );
736 mPicture->endCommand();
740 void QgsLayoutPictureWidget::mPictureRotationOffsetSpinBox_valueChanged(
double d )
743 mPicture->setNorthOffset( d );
744 mPicture->endCommand();
748 void QgsLayoutPictureWidget::mNorthTypeComboBox_currentIndexChanged(
int index )
750 mPicture->beginCommand( tr(
"Change Picture North Mode" ) );
751 mPicture->setNorthMode( static_cast< QgsLayoutItemPicture::NorthMode >( mNorthTypeComboBox->itemData( index ).toInt() ) );
752 mPicture->endCommand();
759 mSearchDirectoriesComboBox->setMinimumWidth( mPreviewListWidget->sizeHint().width() );
770 mPictureLineEdit->setEnabled( !mSourceDDBtn->isActive() );
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
Base class for graphical items within a QgsLayout.
int type() const override
Returns a unique graphics item type identifier.
Lower left corner of item.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false)
Gets SVG as QImage.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an svg file contains parameters for fill, stroke color, stroke width.
Stretches image to fit frame, ignores aspect ratio.
void collapsedStateChanged(bool collapsed)
Signal emitted when groupbox collapsed/expanded state is changed, and when first shown.
A layout item subclass that displays SVG files or raster format images (jpg, png, ...
Lower right corner of item.
Layout graphical items for displaying a map.
Upper right corner of item.
void pictureRotationChanged(double newRotation)
Emitted on picture rotation change.
Upper left corner of item.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
Enlarges image to fit frame, then resizes frame to fit resultant image.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Sets size of frame to match original size of image without scaling.
Draws image at original size and clips any portion which falls outside frame.
static QStringList svgPaths()
Returns the paths to svg directories.
Enlarges image to fit frame while maintaining aspect ratio of picture.
Interface for master layout type objects, such as print layouts and reports.
void changed()
Emitted when the object's properties change.