[processing][FEATURE] Add api for setting model component colors, and expose

the option to set manual colors for individual model comments
This commit is contained in:
Nyall Dawson 2020-04-01 09:48:06 +10:00
parent 25b2ba4158
commit e788be93fa
19 changed files with 397 additions and 43 deletions

View File

@ -69,6 +69,27 @@ Sets the ``size`` of the model component within the graphical modeler.
.. seealso:: :py:func:`size`
.. versionadded:: 3.14
%End
QColor color() const;
%Docstring
Returns the color of the model component within the graphical modeler.
An invalid color indicates that the default color for the component should be used.
.. seealso:: :py:func:`setColor`
.. versionadded:: 3.14
%End
void setColor( const QColor &color );
%Docstring
Sets the ``color`` of the model component within the graphical modeler. An invalid ``color``
indicates that the default color for the component should be used.
.. seealso:: :py:func:`color`
.. versionadded:: 3.14
%End

View File

@ -428,6 +428,7 @@
%Include auto_generated/processing/models/qgsprocessingmodelchildparametersource.sip
%Include auto_generated/processing/models/qgsprocessingmodelcomment.sip
%Include auto_generated/processing/models/qgsprocessingmodelcomponent.sip
%Include auto_generated/processing/models/qgsprocessingmodelgroupbox.sip
%Include auto_generated/processing/models/qgsprocessingmodeloutput.sip
%Include auto_generated/processing/models/qgsprocessingmodelparameter.sip
%Include auto_generated/processing/qgsprocessing.sip

View File

@ -157,6 +157,24 @@ Returns the comments for the parameter.
.. seealso:: :py:func:`setComments`
.. versionadded:: 3.14
%End
void setCommentColor( const QColor &color );
%Docstring
Sets the color for the comments for the parameter.
.. seealso:: :py:func:`commentColor`
.. versionadded:: 3.14
%End
QColor commentColor() const;
%Docstring
Returns the color for the comments for the parameter.
.. seealso:: :py:func:`setCommentColor`
.. versionadded:: 3.14
%End

View File

@ -210,11 +210,12 @@ Sets the string to use for the "no color" option in the button's drop-down menu.
dialog
%End
void setShowNull( bool showNull );
void setShowNull( bool showNull, const QString &nullString = QString() );
%Docstring
Sets whether a set to null (clear) option is shown in the button's drop-down menu.
:param showNull: set to ``True`` to show a null option
:param nullString: translated string to use for the null option. If not set, a default "Clear Color" string will be used.
.. seealso:: :py:func:`showNull`

View File

@ -63,17 +63,20 @@ class ModelerInputGraphicItem(QgsModelParameterGraphicItem):
def edit(self, edit_comment=False):
existing_param = self.model().parameterDefinition(self.component().parameterName())
comment = self.component().comment().description()
comment_color = self.component().comment().color()
new_param = None
if ModelerParameterDefinitionDialog.use_legacy_dialog(param=existing_param):
# boo, old api
dlg = ModelerParameterDefinitionDialog(self.model(),
param=existing_param)
dlg.setComments(comment)
dlg.setCommentColor(comment_color)
if edit_comment:
dlg.switchToCommentTab()
if dlg.exec_():
new_param = dlg.param
comment = dlg.comments()
comment_color = dlg.commentColor()
else:
# yay, use new API!
context = createContext()
@ -84,12 +87,14 @@ class ModelerInputGraphicItem(QgsModelParameterGraphicItem):
definition=existing_param,
algorithm=self.model())
dlg.setComments(comment)
dlg.setCommentColor(comment_color)
if edit_comment:
dlg.switchToCommentTab()
if dlg.exec_():
new_param = dlg.createParameter(existing_param.name())
comment = dlg.comments()
comment_color = dlg.commentColor()
if new_param is not None:
self.aboutToChange.emit(self.tr('Edit {}').format(new_param.description()))
@ -97,6 +102,7 @@ class ModelerInputGraphicItem(QgsModelParameterGraphicItem):
self.component().setParameterName(new_param.name())
self.component().setDescription(new_param.name())
self.component().comment().setDescription(comment)
self.component().comment().setColor(comment_color)
self.model().addModelParameter(new_param, self.component())
self.setLabel(new_param.description())
self.requestModelRepaint.emit()
@ -125,6 +131,7 @@ class ModelerChildAlgorithmGraphicItem(QgsModelChildAlgorithmGraphicItem):
dlg = ModelerParametersDialog(elemAlg, self.model(), self.component().childId(),
self.component().configuration())
dlg.setComments(self.component().comment().description())
dlg.setCommentColor(self.component().comment().color())
if edit_comment:
dlg.switchToCommentTab()
if dlg.exec_():
@ -160,6 +167,7 @@ class ModelerOutputGraphicItem(QgsModelOutputGraphicItem):
dlg = ModelerParameterDefinitionDialog(self.model(),
param=self.model().parameterDefinition(param_name))
dlg.setComments(self.component().comment().description())
dlg.setCommentColor(self.component().comment().color())
if edit_comment:
dlg.switchToCommentTab()
@ -169,6 +177,7 @@ class ModelerOutputGraphicItem(QgsModelOutputGraphicItem):
model_output.setDefaultValue(dlg.param.defaultValue())
model_output.setMandatory(not (dlg.param.flags() & QgsProcessingParameterDefinition.FlagOptional))
model_output.comment().setDescription(dlg.comments())
model_output.comment().setColor(dlg.commentColor())
self.aboutToChange.emit(self.tr('Edit {}').format(model_output.description()))
self.model().updateDestinationParameters()
self.requestModelRepaint.emit()

View File

@ -36,9 +36,12 @@ from qgis.PyQt.QtWidgets import (QDialog,
QMessageBox,
QTabWidget,
QWidget,
QTextEdit)
QTextEdit,
QHBoxLayout)
from qgis.PyQt.QtGui import QColor
from qgis.gui import (QgsProcessingLayerOutputDestinationWidget)
from qgis.gui import (QgsProcessingLayerOutputDestinationWidget,
QgsColorButton)
from qgis.core import (QgsApplication,
QgsSettings,
QgsProcessing,
@ -210,8 +213,8 @@ class ModelerParameterDefinitionDialog(QDialog):
if self.param is not None:
self.shapetypeCombo.setCurrentIndex(self.shapetypeCombo.findData(self.param.dataTypes()[0]))
self.verticalLayout.addWidget(self.shapetypeCombo)
elif (self.paramType == parameters.PARAMETER_MULTIPLE
or isinstance(self.param, QgsProcessingParameterMultipleLayers)):
elif (self.paramType == parameters.PARAMETER_MULTIPLE or
isinstance(self.param, QgsProcessingParameterMultipleLayers)):
self.verticalLayout.addWidget(QLabel(self.tr('Data type')))
self.datatypeCombo = QComboBox()
self.datatypeCombo.addItem(self.tr('Any Map Layer'), QgsProcessing.TypeMapLayer)
@ -225,8 +228,8 @@ class ModelerParameterDefinitionDialog(QDialog):
if self.param is not None:
self.datatypeCombo.setCurrentIndex(self.datatypeCombo.findData(self.param.layerType()))
self.verticalLayout.addWidget(self.datatypeCombo)
elif (self.paramType == parameters.PARAMETER_MAP_LAYER or
isinstance(self.param, QgsProcessingParameterMapLayer)):
elif (self.paramType == parameters.PARAMETER_MAP_LAYER
or isinstance(self.param, QgsProcessingParameterMapLayer)):
self.verticalLayout.addWidget(QLabel(self.tr('Data type')))
self.datatypeCombo = QComboBox()
self.datatypeCombo.addItem(self.tr('Any Map Layer'), QgsProcessing.TypeMapLayer)
@ -239,11 +242,11 @@ class ModelerParameterDefinitionDialog(QDialog):
if self.param is not None:
self.datatypeCombo.setCurrentIndex(self.datatypeCombo.findData(self.param.dataTypes()[0]))
self.verticalLayout.addWidget(self.datatypeCombo)
elif (self.paramType in (parameters.PARAMETER_NUMBER, parameters.PARAMETER_DISTANCE, parameters.PARAMETER_SCALE)
or isinstance(self.param, (QgsProcessingParameterNumber, QgsProcessingParameterDistance, QgsProcessingParameterScale))):
elif (self.paramType in (parameters.PARAMETER_NUMBER, parameters.PARAMETER_DISTANCE, parameters.PARAMETER_SCALE) or
isinstance(self.param, (QgsProcessingParameterNumber, QgsProcessingParameterDistance, QgsProcessingParameterScale))):
if (self.paramType == parameters.PARAMETER_DISTANCE
or isinstance(self.param, QgsProcessingParameterDistance)):
if (self.paramType == parameters.PARAMETER_DISTANCE or
isinstance(self.param, QgsProcessingParameterDistance)):
self.verticalLayout.addWidget(QLabel(self.tr('Linked input')))
self.parentCombo = QComboBox()
self.parentCombo.addItem('', '')
@ -334,15 +337,26 @@ class ModelerParameterDefinitionDialog(QDialog):
self.commentLayout = QVBoxLayout()
self.commentEdit = QTextEdit()
self.commentEdit.setAcceptRichText(False)
self.commentLayout.addWidget(self.commentEdit)
self.commentLayout.addWidget(self.commentEdit, 1)
hl = QHBoxLayout()
hl.setContentsMargins(0, 0, 0, 0)
hl.addWidget(QLabel(self.tr('Color')))
self.comment_color_button = QgsColorButton()
self.comment_color_button.setAllowOpacity(True)
self.comment_color_button.setWindowTitle(self.tr('Comment Color'))
self.comment_color_button.setShowNull(True, self.tr('Default'))
hl.addWidget(self.comment_color_button)
self.commentLayout.addLayout(hl)
w2 = QWidget()
w2.setLayout(self.commentLayout)
self.tab.addTab(w2, self.tr('Comments'))
self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
| QDialogButtonBox.Ok)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel |
QDialogButtonBox.Ok)
self.buttonBox.setObjectName('buttonBox')
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
@ -357,6 +371,15 @@ class ModelerParameterDefinitionDialog(QDialog):
def comments(self):
return self.commentEdit.toPlainText()
def setCommentColor(self, color):
if color.isValid():
self.comment_color_button.setColor(color)
else:
self.comment_color_button.setToNull()
def commentColor(self):
return self.comment_color_button.color() if not self.comment_color_button.isNull() else QColor()
def accept(self):
description = self.nameTextBox.text()
if description.strip() == '':
@ -374,8 +397,8 @@ class ModelerParameterDefinitionDialog(QDialog):
i += 1
else:
name = self.param.name()
if (self.paramType == parameters.PARAMETER_TABLE_FIELD
or isinstance(self.param, QgsProcessingParameterField)):
if (self.paramType == parameters.PARAMETER_TABLE_FIELD or
isinstance(self.param, QgsProcessingParameterField)):
if self.parentCombo.currentIndex() < 0:
QMessageBox.warning(self, self.tr('Unable to define parameter'),
self.tr('Wrong or missing parameter values'))
@ -388,39 +411,39 @@ class ModelerParameterDefinitionDialog(QDialog):
self.param = QgsProcessingParameterField(name, description, defaultValue=default,
parentLayerParameterName=parent, type=datatype,
allowMultiple=self.multipleCheck.isChecked())
elif (self.paramType == parameters.PARAMETER_BAND
or isinstance(self.param, QgsProcessingParameterBand)):
elif (self.paramType == parameters.PARAMETER_BAND or
isinstance(self.param, QgsProcessingParameterBand)):
if self.parentCombo.currentIndex() < 0:
QMessageBox.warning(self, self.tr('Unable to define parameter'),
self.tr('Wrong or missing parameter values'))
return
parent = self.parentCombo.currentData()
self.param = QgsProcessingParameterBand(name, description, None, parent)
elif (self.paramType == parameters.PARAMETER_MAP_LAYER
or isinstance(self.param, QgsProcessingParameterMapLayer)):
elif (self.paramType == parameters.PARAMETER_MAP_LAYER or
isinstance(self.param, QgsProcessingParameterMapLayer)):
self.param = QgsProcessingParameterMapLayer(
name, description, types=[self.datatypeCombo.currentData()])
elif (self.paramType == parameters.PARAMETER_RASTER
or isinstance(self.param, QgsProcessingParameterRasterLayer)):
elif (self.paramType == parameters.PARAMETER_RASTER or
isinstance(self.param, QgsProcessingParameterRasterLayer)):
self.param = QgsProcessingParameterRasterLayer(
name, description)
elif (self.paramType == parameters.PARAMETER_TABLE
or isinstance(self.param, QgsProcessingParameterVectorLayer)):
elif (self.paramType == parameters.PARAMETER_TABLE or
isinstance(self.param, QgsProcessingParameterVectorLayer)):
self.param = QgsProcessingParameterVectorLayer(
name, description,
[self.shapetypeCombo.currentData()])
elif (self.paramType == parameters.PARAMETER_VECTOR
or isinstance(self.param, QgsProcessingParameterFeatureSource)):
elif (self.paramType == parameters.PARAMETER_VECTOR or
isinstance(self.param, QgsProcessingParameterFeatureSource)):
self.param = QgsProcessingParameterFeatureSource(
name, description,
[self.shapetypeCombo.currentData()])
elif (self.paramType == parameters.PARAMETER_MULTIPLE
or isinstance(self.param, QgsProcessingParameterMultipleLayers)):
elif (self.paramType == parameters.PARAMETER_MULTIPLE or
isinstance(self.param, QgsProcessingParameterMultipleLayers)):
self.param = QgsProcessingParameterMultipleLayers(
name, description,
self.datatypeCombo.currentData())
elif (self.paramType == parameters.PARAMETER_DISTANCE
or isinstance(self.param, QgsProcessingParameterDistance)):
elif (self.paramType == parameters.PARAMETER_DISTANCE or
isinstance(self.param, QgsProcessingParameterDistance)):
self.param = QgsProcessingParameterDistance(name, description,
self.defaultTextBox.text())
try:
@ -442,12 +465,12 @@ class ModelerParameterDefinitionDialog(QDialog):
parent = self.parentCombo.currentData()
if parent:
self.param.setParentParameterName(parent)
elif (self.paramType == parameters.PARAMETER_SCALE
or isinstance(self.param, QgsProcessingParameterScale)):
elif (self.paramType == parameters.PARAMETER_SCALE or
isinstance(self.param, QgsProcessingParameterScale)):
self.param = QgsProcessingParameterScale(name, description,
self.defaultTextBox.text())
elif (self.paramType == parameters.PARAMETER_NUMBER
or isinstance(self.param, QgsProcessingParameterNumber)):
elif (self.paramType == parameters.PARAMETER_NUMBER or
isinstance(self.param, QgsProcessingParameterNumber)):
type = self.type_combo.currentData()
self.param = QgsProcessingParameterNumber(name, description, type,

View File

@ -27,6 +27,7 @@ from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import (QDialog, QDialogButtonBox, QLabel, QLineEdit,
QFrame, QPushButton, QSizePolicy, QVBoxLayout,
QHBoxLayout, QWidget, QTabWidget, QTextEdit)
from qgis.PyQt.QtGui import QColor
from qgis.core import (Qgis,
QgsProject,
@ -50,7 +51,8 @@ from qgis.gui import (QgsGui,
QgsProcessingModelerParameterWidget,
QgsProcessingParameterWidgetContext,
QgsPanelWidget,
QgsPanelWidgetStack)
QgsPanelWidgetStack,
QgsColorButton)
from qgis.utils import iface
from processing.gui.wrappers import WidgetWrapperFactory
@ -101,6 +103,12 @@ class ModelerParametersDialog(QDialog):
def comments(self):
return self.widget.comments()
def setCommentColor(self, color):
self.widget.setCommentColor(color)
def commentColor(self):
return self.widget.commentColor()
def switchToCommentTab(self):
self.widget.switchToCommentTab()
@ -534,7 +542,18 @@ class ModelerParametersWidget(QWidget):
self.commentLayout = QVBoxLayout()
self.commentEdit = QTextEdit()
self.commentEdit.setAcceptRichText(False)
self.commentLayout.addWidget(self.commentEdit)
self.commentLayout.addWidget(self.commentEdit, 1)
hl = QHBoxLayout()
hl.setContentsMargins(0, 0, 0, 0)
hl.addWidget(QLabel(self.tr('Color')))
self.comment_color_button = QgsColorButton()
self.comment_color_button.setAllowOpacity(True)
self.comment_color_button.setWindowTitle(self.tr('Comment Color'))
self.comment_color_button.setShowNull(True, self.tr('Default'))
hl.addWidget(self.comment_color_button)
self.commentLayout.addLayout(hl)
w2 = QWidget()
w2.setLayout(self.commentLayout)
self.tab.addTab(w2, self.tr('Comments'))
@ -547,6 +566,15 @@ class ModelerParametersWidget(QWidget):
def comments(self):
return self.commentEdit.toPlainText()
def setCommentColor(self, color):
if color.isValid():
self.comment_color_button.setColor(color)
else:
self.comment_color_button.setToNull()
def commentColor(self):
return self.comment_color_button.color() if not self.comment_color_button.isNull() else QColor()
def getAvailableDependencies(self):
return self.widget.getAvailableDependencies()
@ -566,4 +594,5 @@ class ModelerParametersWidget(QWidget):
alg = self.widget.createAlgorithm()
if alg:
alg.comment().setDescription(self.comments())
alg.comment().setColor(self.commentColor())
return alg

View File

@ -155,6 +155,7 @@ SET(QGIS_CORE_SRCS
processing/models/qgsprocessingmodelchildparametersource.cpp
processing/models/qgsprocessingmodelcomment.cpp
processing/models/qgsprocessingmodelcomponent.cpp
processing/models/qgsprocessingmodelgroupbox.cpp
processing/models/qgsprocessingmodelparameter.cpp
processing/models/qgsprocessingmodeloutput.cpp
@ -1197,6 +1198,7 @@ SET(QGIS_CORE_HDRS
processing/models/qgsprocessingmodelchildparametersource.h
processing/models/qgsprocessingmodelcomment.h
processing/models/qgsprocessingmodelcomponent.h
processing/models/qgsprocessingmodelgroupbox.h
processing/models/qgsprocessingmodeloutput.h
processing/models/qgsprocessingmodelparameter.h
processing/qgsprocessing.h

View File

@ -68,7 +68,10 @@ void QgsProcessingModelChildAlgorithm::copyNonDefinitionPropertiesFromModel( Qgs
for ( auto it = mModelOutputs.begin(); it != mModelOutputs.end(); ++it )
{
if ( !existingChild.modelOutputs().value( it.key() ).position().isNull() )
{
it.value().setPosition( existingChild.modelOutputs().value( it.key() ).position() );
it.value().setSize( existingChild.modelOutputs().value( it.key() ).size() );
}
else
it.value().setPosition( position() + QPointF( size().width(), ( i + 1.5 ) * size().height() ) );
@ -79,6 +82,7 @@ void QgsProcessingModelChildAlgorithm::copyNonDefinitionPropertiesFromModel( Qgs
comment->setDescription( existingComment->description() );
comment->setSize( existingComment->size() );
comment->setPosition( existingComment->position() );
comment->setColor( existingComment->color() );
}
}
i++;

View File

@ -17,6 +17,7 @@
#include "qgsprocessingmodelcomponent.h"
#include "qgsprocessingmodelcomment.h"
#include "qgssymbollayerutils.h"
///@cond NOT_STABLE
@ -54,6 +55,16 @@ void QgsProcessingModelComponent::setSize( QSizeF size )
mSize = size;
}
QColor QgsProcessingModelComponent::color() const
{
return mColor;
}
void QgsProcessingModelComponent::setColor( const QColor &color )
{
mColor = color;
}
bool QgsProcessingModelComponent::linksCollapsed( Qt::Edge edge ) const
{
switch ( edge )
@ -103,6 +114,7 @@ void QgsProcessingModelComponent::saveCommonProperties( QVariantMap &map ) const
map.insert( QStringLiteral( "component_height" ), mSize.height() );
map.insert( QStringLiteral( "parameters_collapsed" ), mTopEdgeLinksCollapsed );
map.insert( QStringLiteral( "outputs_collapsed" ), mBottomEdgeLinksCollapsed );
map.insert( QStringLiteral( "color" ), mColor.isValid() ? QgsSymbolLayerUtils::encodeColor( mColor ) : QString() );
if ( comment() )
map.insert( QStringLiteral( "comment" ), comment()->toVariant() );
}
@ -116,6 +128,7 @@ void QgsProcessingModelComponent::restoreCommonProperties( const QVariantMap &ma
mDescription = map.value( QStringLiteral( "component_description" ) ).toString();
mSize.setWidth( map.value( QStringLiteral( "component_width" ), QString::number( DEFAULT_COMPONENT_WIDTH ) ).toDouble() );
mSize.setHeight( map.value( QStringLiteral( "component_height" ), QString::number( DEFAULT_COMPONENT_HEIGHT ) ).toDouble() );
mColor = map.value( QStringLiteral( "color" ) ).toString().isEmpty() ? QColor() : QgsSymbolLayerUtils::decodeColor( map.value( QStringLiteral( "color" ) ).toString() );
mTopEdgeLinksCollapsed = map.value( QStringLiteral( "parameters_collapsed" ) ).toBool();
mBottomEdgeLinksCollapsed = map.value( QStringLiteral( "outputs_collapsed" ) ).toBool();
if ( comment() )

View File

@ -22,6 +22,7 @@
#include "qgis.h"
#include <QPointF>
#include <QSizeF>
#include <QColor>
class QgsProcessingModelComment;
@ -76,6 +77,25 @@ class CORE_EXPORT QgsProcessingModelComponent
*/
void setSize( QSizeF size );
/**
* Returns the color of the model component within the graphical modeler.
*
* An invalid color indicates that the default color for the component should be used.
*
* \see setColor()
* \since QGIS 3.14
*/
QColor color() const;
/**
* Sets the \a color of the model component within the graphical modeler. An invalid \a color
* indicates that the default color for the component should be used.
*
* \see color()
* \since QGIS 3.14
*/
void setColor( const QColor &color );
/**
* Returns TRUE if the link points for the specified \a edge should be shown collapsed or not.
* \see setLinksCollapsed()
@ -158,6 +178,7 @@ class CORE_EXPORT QgsProcessingModelComponent
QString mDescription;
QSizeF mSize = QSizeF( DEFAULT_COMPONENT_WIDTH, DEFAULT_COMPONENT_HEIGHT );
QColor mColor;
bool mTopEdgeLinksCollapsed = true;
bool mBottomEdgeLinksCollapsed = true;

View File

@ -0,0 +1,47 @@
/***************************************************************************
qgsprocessingmodelgroupbox.cpp
--------------------------
begin : March 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsprocessingmodelgroupbox.h"
///@cond NOT_STABLE
QgsProcessingModelGroupBox::QgsProcessingModelGroupBox( const QString &description )
: QgsProcessingModelComponent( description )
{
setSize( QSizeF( 100, 60 ) );
}
QgsProcessingModelGroupBox *QgsProcessingModelGroupBox::clone() const
{
return new QgsProcessingModelGroupBox( *this );
}
QVariant QgsProcessingModelGroupBox::toVariant() const
{
QVariantMap map;
saveCommonProperties( map );
return map;
}
bool QgsProcessingModelGroupBox::loadVariant( const QVariantMap &map )
{
restoreCommonProperties( map );
return true;
}
///@endcond

View File

@ -0,0 +1,59 @@
/***************************************************************************
qgsprocessingmodelgroupbox.h
--------------------------
begin : March 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSPROCESSINGMODELGROUPBOX_H
#define QGSPROCESSINGMODELGROUPBOX_H
#include "qgis_core.h"
#include "qgis.h"
#include "qgsprocessingmodelcomponent.h"
#include "qgsprocessingparameters.h"
///@cond NOT_STABLE
/**
* Represents a group box in a model.
* \ingroup core
* \since QGIS 3.14
*/
class CORE_EXPORT QgsProcessingModelGroupBox : public QgsProcessingModelComponent
{
public:
/**
* Constructor for QgsProcessingModelGroupBox with the specified \a description.
*/
QgsProcessingModelGroupBox( const QString &description = QString() );
QgsProcessingModelGroupBox *clone() const override SIP_FACTORY;
/**
* Saves this group box to a QVariant.
* \see loadVariant()
*/
QVariant toVariant() const;
/**
* Loads this group box from a QVariantMap.
* \see toVariant()
*/
bool loadVariant( const QVariantMap &map );
};
///@endcond
#endif // QGSPROCESSINGMODELGROUPBOX_H

View File

@ -318,8 +318,33 @@ bool QgsModelComponentGraphicItem::contains( const QPointF &point ) const
void QgsModelComponentGraphicItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *, QWidget * )
{
const QRectF rect = itemRect();
QColor color = fillColor( state() );
QColor stroke = strokeColor( state() );
QColor color;
QColor stroke;
QColor foreColor;
if ( mComponent->color().isValid() )
{
color = mComponent->color();
switch ( state() )
{
case Selected:
color = color.darker( 110 );
break;
case Hover:
color = color.darker( 105 );
break;
case Normal:
break;
}
stroke = color.darker( 110 );
foreColor = color.lightness() > 150 ? QColor( 0, 0, 0 ) : QColor( 255, 255, 255 );
}
else
{
color = fillColor( state() );
stroke = strokeColor( state() );
foreColor = textColor( state() );
}
QPen strokePen = QPen( stroke, 0 ) ; // 0 width "cosmetic" pen
strokePen.setStyle( strokeStyle( state() ) );
@ -327,7 +352,7 @@ void QgsModelComponentGraphicItem::paint( QPainter *painter, const QStyleOptionG
painter->setBrush( QBrush( color, Qt::SolidPattern ) );
painter->drawRect( rect );
painter->setFont( font() );
painter->setPen( QPen( textColor( state() ) ) );
painter->setPen( QPen( foreColor ) );
QString text;

View File

@ -22,6 +22,7 @@
#include "qgsapplication.h"
#include "qgsprocessingregistry.h"
#include "qgsprocessingparametertype.h"
#include "qgscolorbutton.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
@ -145,7 +146,18 @@ QgsProcessingParameterDefinitionDialog::QgsProcessingParameterDefinitionDialog(
QVBoxLayout *commentLayout = new QVBoxLayout();
mCommentEdit = new QTextEdit();
mCommentEdit->setAcceptRichText( false );
commentLayout->addWidget( mCommentEdit );
commentLayout->addWidget( mCommentEdit, 1 );
QHBoxLayout *hl = new QHBoxLayout();
hl->setContentsMargins( 0, 0, 0, 0 );
hl->addWidget( new QLabel( tr( "Color" ) ) );
mCommentColorButton = new QgsColorButton();
mCommentColorButton->setAllowOpacity( true );
mCommentColorButton->setWindowTitle( tr( "Comment Color" ) );
mCommentColorButton->setShowNull( true, tr( "Default" ) );
hl->addWidget( mCommentColorButton );
commentLayout->addLayout( hl );
QWidget *w2 = new QWidget();
w2->setLayout( commentLayout );
mTabWidget->addTab( w2, tr( "Comments" ) );
@ -178,6 +190,19 @@ QString QgsProcessingParameterDefinitionDialog::comments() const
return mCommentEdit->toPlainText();
}
void QgsProcessingParameterDefinitionDialog::setCommentColor( const QColor &color )
{
if ( color.isValid() )
mCommentColorButton->setColor( color );
else
mCommentColorButton->setToNull();
}
QColor QgsProcessingParameterDefinitionDialog::commentColor() const
{
return !mCommentColorButton->isNull() ? mCommentColorButton->color() : QColor();
}
void QgsProcessingParameterDefinitionDialog::switchToCommentTab()
{
mTabWidget->setCurrentIndex( 1 );

View File

@ -31,6 +31,7 @@ class QLineEdit;
class QCheckBox;
class QTabWidget;
class QTextEdit;
class QgsColorButton;
/**
* Abstract base class for widgets which allow users to specify the properties of a
@ -183,6 +184,20 @@ class GUI_EXPORT QgsProcessingParameterDefinitionDialog: public QDialog
*/
QString comments() const;
/**
* Sets the color for the comments for the parameter.
* \see commentColor()
* \since QGIS 3.14
*/
void setCommentColor( const QColor &color );
/**
* Returns the color for the comments for the parameter.
* \see setCommentColor()
* \since QGIS 3.14
*/
QColor commentColor() const;
/**
* Switches the dialog to the comments tab.
*/
@ -195,6 +210,7 @@ class GUI_EXPORT QgsProcessingParameterDefinitionDialog: public QDialog
QTabWidget *mTabWidget = nullptr;
QTextEdit *mCommentEdit = nullptr;
QgsColorButton *mCommentColorButton = nullptr;
QgsProcessingParameterDefinitionWidget *mWidget = nullptr;
};

View File

@ -508,7 +508,7 @@ void QgsColorButton::prepareMenu()
{
if ( mShowNull )
{
QAction *nullAction = new QAction( tr( "Clear Color" ), this );
QAction *nullAction = new QAction( mNullColorString.isEmpty() ? tr( "Clear Color" ) : mNullColorString, this );
nullAction->setIcon( createMenuIcon( Qt::transparent, false ) );
mMenu->addAction( nullAction );
connect( nullAction, &QAction::triggered, this, &QgsColorButton::setToNull );
@ -825,9 +825,10 @@ void QgsColorButton::setDefaultColor( const QColor &color )
mDefaultColor = color;
}
void QgsColorButton::setShowNull( bool showNull )
void QgsColorButton::setShowNull( bool showNull, const QString &nullString )
{
mShowNull = showNull;
mNullColorString = nullString;
}
bool QgsColorButton::showNull() const

View File

@ -202,11 +202,12 @@ class GUI_EXPORT QgsColorButton : public QToolButton
/**
* Sets whether a set to null (clear) option is shown in the button's drop-down menu.
* \param showNull set to TRUE to show a null option
* \param nullString translated string to use for the null option. If not set, a default "Clear Color" string will be used.
* \see showNull()
* \see isNull()
* \since QGIS 2.16
*/
void setShowNull( bool showNull );
void setShowNull( bool showNull, const QString &nullString = QString() );
/**
* Returns whether the set to null (clear) option is shown in the button's drop-down menu.
@ -481,6 +482,7 @@ class GUI_EXPORT QgsColorButton : public QToolButton
bool mShowNoColorOption = false;
QString mNoColorString;
bool mShowNull = false;
QString mNullColorString;
QPoint mDragStartPosition;
bool mPickingColor = false;

View File

@ -8266,16 +8266,20 @@ void TestQgsProcessing::modelerAlgorithm()
QCOMPARE( comment.position(), QPointF( 11, 14 ) );
comment.setDescription( QStringLiteral( "a comment" ) );
QCOMPARE( comment.description(), QStringLiteral( "a comment" ) );
comment.setColor( QColor( 123, 45, 67 ) );
QCOMPARE( comment.color(), QColor( 123, 45, 67 ) );
std::unique_ptr< QgsProcessingModelComment > commentClone( comment.clone() );
QCOMPARE( commentClone->toVariant(), comment.toVariant() );
QCOMPARE( commentClone->size(), QSizeF( 9, 8 ) );
QCOMPARE( commentClone->position(), QPointF( 11, 14 ) );
QCOMPARE( commentClone->description(), QStringLiteral( "a comment" ) );
QCOMPARE( commentClone->color(), QColor( 123, 45, 67 ) );
QgsProcessingModelComment comment2;
comment2.loadVariant( comment.toVariant().toMap() );
QCOMPARE( comment2.size(), QSizeF( 9, 8 ) );
QCOMPARE( comment2.position(), QPointF( 11, 14 ) );
QCOMPARE( comment2.description(), QStringLiteral( "a comment" ) );
QCOMPARE( comment2.color(), QColor( 123, 45, 67 ) );
QMap< QString, QString > friendlyOutputNames;
QgsProcessingModelChildAlgorithm child( QStringLiteral( "some_id" ) );
@ -8463,6 +8467,21 @@ void TestQgsProcessing::modelerAlgorithm()
a2.setDescription( QStringLiteral( "alg2" ) );
a2.setPosition( QPointF( 112, 131 ) );
a2.setSize( QSizeF( 44, 55 ) );
a2.comment()->setSize( QSizeF( 111, 222 ) );
a2.comment()->setPosition( QPointF( 113, 114 ) );
a2.comment()->setDescription( QStringLiteral( "c" ) );
a2.comment()->setColor( QColor( 255, 254, 253 ) );
QgsProcessingModelOutput oo;
oo.setPosition( QPointF( 312, 331 ) );
oo.setSize( QSizeF( 344, 355 ) );
oo.comment()->setSize( QSizeF( 311, 322 ) );
oo.comment()->setPosition( QPointF( 313, 314 ) );
oo.comment()->setDescription( QStringLiteral( "c3" ) );
oo.comment()->setColor( QColor( 155, 14, 353 ) );
QMap< QString, QgsProcessingModelOutput > a2Outs;
a2Outs.insert( QStringLiteral( "out1" ), oo );
a2.setModelOutputs( a2Outs );
algs.insert( QStringLiteral( "a" ), a1 );
algs.insert( QStringLiteral( "b" ), a2 );
alg.setChildAlgorithms( algs );
@ -8473,10 +8492,28 @@ void TestQgsProcessing::modelerAlgorithm()
QgsProcessingModelChildAlgorithm a2other;
a2other.setChildId( QStringLiteral( "b" ) );
a2other.setDescription( QStringLiteral( "alg2 other" ) );
QgsProcessingModelOutput oo2;
QMap< QString, QgsProcessingModelOutput > a2Outs2;
a2Outs2.insert( QStringLiteral( "out1" ), oo2 );
a2other.setModelOutputs( a2Outs2 );
a2other.copyNonDefinitionPropertiesFromModel( &alg );
QCOMPARE( a2other.description(), QStringLiteral( "alg2 other" ) );
QCOMPARE( a2other.position(), QPointF( 112, 131 ) );
QCOMPARE( a2other.size(), QSizeF( 44, 55 ) );
QCOMPARE( a2other.comment()->size(), QSizeF( 111, 222 ) );
QCOMPARE( a2other.comment()->position(), QPointF( 113, 114 ) );
// should not be copied
QCOMPARE( a2other.comment()->description(), QString() );
QVERIFY( !a2other.comment()->color().isValid() );
QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).position(), QPointF( 312, 331 ) );
QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).size(), QSizeF( 344, 355 ) );
QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).comment()->size(), QSizeF( 311, 322 ) );
QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).comment()->position(), QPointF( 313, 314 ) );
// should be copied for outputs
QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).comment()->description(), QStringLiteral( "c3" ) );
QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).comment()->color(), QColor( 155, 14, 353 ) );
QgsProcessingModelChildAlgorithm a3;
a3.setChildId( QStringLiteral( "c" ) );