[FEATURE] Redesigned interactive gradient editor

Uses new gradient editor widget and color controls for easier
manipulation of gradients. Also adds an interactive plot
for modifying gradient stops via the color HSV or RGB values.

Fix #8383

Sponsored by North Road
This commit is contained in:
Nyall Dawson 2016-05-12 20:57:31 +10:00
parent 881be0f365
commit 6886ea0d0c
4 changed files with 777 additions and 372 deletions

View File

@ -11,20 +11,9 @@ class QgsVectorGradientColorRampV2Dialog : QDialog
void setColor1( const QColor& color );
void setColor2( const QColor& color );
void toggledStops( bool on );
void addStop();
void removeStop();
void stopDoubleClicked( QTreeWidgetItem* item, int column );
void setItemStopColor( const QColor& newColor );
protected slots:
void on_cboType_currentIndexChanged( int index );
void on_btnInformation_pressed();
protected:
void updateStops();
void updatePreview();
void setStopColor( QTreeWidgetItem* item, const QColor& color );
};

View File

@ -27,16 +27,31 @@
#include <QTableWidget>
#include <QTextEdit>
// QWT Charting widget
#include <qwt_global.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_picker.h>
#include <qwt_picker_machine.h>
#include <qwt_plot_layout.h>
#include <qwt_symbol.h>
#include <qwt_legend.h>
QgsVectorGradientColorRampV2Dialog::QgsVectorGradientColorRampV2Dialog( QgsVectorGradientColorRampV2* ramp, QWidget* parent )
: QDialog( parent )
, mRamp( ramp )
, mCurrentItem( nullptr )
, mCurrentPlotColorComponent( -1 )
, mCurrentPlotMarkerIndex( 0 )
{
setupUi( this );
#ifdef Q_OS_MAC
setWindowModality( Qt::WindowModal );
#endif
mPositionSpinBox->setShowClearButton( false );
btnColor1->setAllowAlpha( true );
btnColor1->setColorDialogTitle( tr( "Select ramp color" ) );
btnColor1->setContext( "symbology" );
@ -47,16 +62,10 @@ QgsVectorGradientColorRampV2Dialog::QgsVectorGradientColorRampV2Dialog( QgsVecto
btnColor2->setContext( "symbology" );
btnColor2->setShowNoColor( true );
btnColor2->setNoColorString( tr( "Transparent" ) );
updateColorButtons();
connect( btnColor1, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( setColor1( const QColor& ) ) );
connect( btnColor2, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( setColor2( const QColor& ) ) );
// handle stops
updateStops();
connect( groupStops, SIGNAL( toggled( bool ) ), this, SLOT( toggledStops( bool ) ) );
connect( btnAddStop, SIGNAL( clicked() ), this, SLOT( addStop() ) );
connect( btnRemoveStop, SIGNAL( clicked() ), this, SLOT( removeStop() ) );
connect( treeStops, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( stopDoubleClicked( QTreeWidgetItem*, int ) ) );
// fill type combobox
cboType->blockSignals( true );
cboType->addItem( tr( "Discrete" ) );
@ -67,14 +76,83 @@ QgsVectorGradientColorRampV2Dialog::QgsVectorGradientColorRampV2Dialog( QgsVecto
cboType->setCurrentIndex( 1 );
cboType->blockSignals( false );
// information button if needed
// QPushButton* button = buttonBox->addButton( tr( "Information" ), QDialogButtonBox::ActionRole );
if ( mRamp->info().isEmpty() )
btnInformation->setEnabled( false );
// else
// connect( button, SIGNAL( pressed() ), this, SLOT( showInformation() ) );
updatePreview();
mStopEditor->setGradientRamp( *mRamp );
connect( mStopEditor, SIGNAL( changed() ), this, SLOT( updateRampFromStopEditor() ) );
connect( mColorWidget, SIGNAL( currentColorChanged( QColor ) ), this, SLOT( colorWidgetChanged( QColor ) ) );
connect( mDeleteStopButton, SIGNAL( clicked() ), mStopEditor, SLOT( deleteSelectedStop() ) );
QSettings settings;
restoreGeometry( settings.value( "/Windows/GradientEditor/geometry" ).toByteArray() );
// hide the ugly canvas frame
mPlot->setFrameStyle( QFrame::NoFrame );
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QFrame* plotCanvasFrame = dynamic_cast<QFrame*>( mPlot->canvas() );
if ( plotCanvasFrame )
plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
#else
mPlot->canvas()->setFrameStyle( QFrame::NoFrame );
#endif
mPlot->setAxisScale( QwtPlot::yLeft, 0.0, 1.0 );
mPlot->enableAxis( QwtPlot::yLeft, false );
mLightnessCurve = new QwtPlotCurve();
mLightnessCurve->setTitle( "Lightness" );
mLightnessCurve->setPen( QPen( QColor( 70, 150, 255 ), 0.0 ) ),
mLightnessCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true );
mLightnessCurve->attach( mPlot );
mHueCurve = new QwtPlotCurve();
mHueCurve->setTitle( "Hue" );
mHueCurve->setPen( QPen( QColor( 255, 215, 70 ), 0.0 ) ),
mHueCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true );
mHueCurve->attach( mPlot );
mSaturationCurve = new QwtPlotCurve();
mSaturationCurve->setTitle( "Saturation" );
mSaturationCurve->setPen( QPen( QColor( 255, 70, 150 ), 0.0 ) ),
mSaturationCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true );
mSaturationCurve->attach( mPlot );
mAlphaCurve = new QwtPlotCurve();
mAlphaCurve->setTitle( "Alpha" );
mAlphaCurve->setPen( QPen( QColor( 50, 50, 50 ), 0.0 ) ),
mAlphaCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true );
mAlphaCurve->attach( mPlot );
mPlotFilter = new QgsGradientPlotEventFilter( mPlot );
connect( mPlotFilter, SIGNAL( mousePress( QPointF ) ), this, SLOT( plotMousePress( QPointF ) ) );
connect( mPlotFilter, SIGNAL( mouseRelease( QPointF ) ), this, SLOT( plotMouseRelease( QPointF ) ) );
connect( mPlotFilter, SIGNAL( mouseMove( QPointF ) ), this, SLOT( plotMouseMove( QPointF ) ) );
mPlotHueCheckbox->setChecked( settings.value( "/GradientEditor/plotHue", false ).toBool() );
mPlotLightnessCheckbox->setChecked( settings.value( "/GradientEditor/plotLightness", true ).toBool() );
mPlotSaturationCheckbox->setChecked( settings.value( "/GradientEditor/plotSaturation", false ).toBool() );
mPlotAlphaCheckbox->setChecked( settings.value( "/GradientEditor/plotAlpha", false ).toBool() );
mHueCurve->setVisible( mPlotHueCheckbox->isChecked() );
mLightnessCurve->setVisible( mPlotLightnessCheckbox->isChecked() );
mSaturationCurve->setVisible( mPlotSaturationCheckbox->isChecked() );
mAlphaCurve->setVisible( mPlotAlphaCheckbox->isChecked() );
connect( mStopEditor, SIGNAL( selectedStopChanged( QgsGradientStop ) ), this, SLOT( selectedStopChanged( QgsGradientStop ) ) );
mStopEditor->selectStop( 0 );
}
QgsVectorGradientColorRampV2Dialog::~QgsVectorGradientColorRampV2Dialog()
{
QSettings settings;
settings.setValue( "/Windows/GradientEditor/geometry", saveGeometry() );
settings.setValue( "/GradientEditor/plotHue", mPlotHueCheckbox->isChecked() );
settings.setValue( "/GradientEditor/plotLightness", mPlotLightnessCheckbox->isChecked() );
settings.setValue( "/GradientEditor/plotSaturation", mPlotSaturationCheckbox->isChecked() );
settings.setValue( "/GradientEditor/plotAlpha", mPlotAlphaCheckbox->isChecked() );
}
void QgsVectorGradientColorRampV2Dialog::on_cboType_currentIndexChanged( int index )
@ -83,8 +161,9 @@ void QgsVectorGradientColorRampV2Dialog::on_cboType_currentIndexChanged( int ind
( index == 1 && !mRamp->isDiscrete() ) )
return;
mRamp->convertToDiscrete( index == 0 );
updateStops();
updatePreview();
updateColorButtons();
updateStopEditor();
updatePlot();
}
void QgsVectorGradientColorRampV2Dialog::on_btnInformation_pressed()
@ -173,55 +252,8 @@ void QgsVectorGradientColorRampV2Dialog::on_btnInformation_pressed()
dlg->show(); //non modal
}
void QgsVectorGradientColorRampV2Dialog::updateStops()
void QgsVectorGradientColorRampV2Dialog::updateColorButtons()
{
QgsGradientStopsList stops = mRamp->stops();
groupStops->setChecked( !stops.isEmpty() );
QList<QTreeWidgetItem *> items;
for ( QgsGradientStopsList::iterator it = stops.begin();
it != stops.end(); ++it )
{
double val = it->offset * 100.0;
QStringList lst;
lst << "." << QString(( val < 10 ) ? '0' + QString::number( val ) : QString::number( val ) );
QTreeWidgetItem* item = new QTreeWidgetItem( lst );
setStopColor( item, it->color );
item->setData( 0, StopOffsetRole, it->offset );
items.append( item );
}
treeStops->clear();
treeStops->insertTopLevelItems( 0, items );
treeStops->resizeColumnToContents( 0 );
treeStops->setColumnWidth( 0, treeStops->columnWidth( 0 ) + 20 );
treeStops->sortByColumn( 1, Qt::AscendingOrder );
treeStops->setSortingEnabled( true );
}
void QgsVectorGradientColorRampV2Dialog::updatePreview()
{
// update ramp stops from the tree widget
QgsGradientStopsList stops;
if ( groupStops->isChecked() )
{
int count = treeStops->topLevelItemCount();
stops.reserve( count );
for ( int i = 0; i < count; i++ )
{
QTreeWidgetItem* item = treeStops->topLevelItem( i );
double offset = item->data( 0, StopOffsetRole ).toDouble();
QColor color = item->data( 0, StopColorRole ).value<QColor>();
stops.append( QgsGradientStop( offset, color ) );
}
}
mRamp->setStops( stops );
// generate the preview
QSize size( 300, 40 );
lblPreview->setPixmap( QgsSymbolLayerV2Utils::colorRampPreviewPixmap( mRamp, size ) );
btnColor1->blockSignals( true );
btnColor1->setColor( mRamp->color1() );
btnColor1->blockSignals( false );
@ -230,168 +262,358 @@ void QgsVectorGradientColorRampV2Dialog::updatePreview()
btnColor2->blockSignals( false );
}
void QgsVectorGradientColorRampV2Dialog::updateStopEditor()
{
mStopEditor->blockSignals( true );
mStopEditor->setGradientRamp( *mRamp );
mStopEditor->blockSignals( false );
}
void QgsVectorGradientColorRampV2Dialog::selectedStopChanged( const QgsGradientStop& stop )
{
mColorWidget->blockSignals( true );
mColorWidget->setColor( stop.color );
mColorWidget->blockSignals( false );
mPositionSpinBox->blockSignals( true );
mPositionSpinBox->setValue( stop.offset * 100 );
mPositionSpinBox->blockSignals( false );
if (( stop.offset == 0 && stop.color == mRamp->color1() ) || ( stop.offset == 1.0 && stop.color == mRamp->color2() ) )
{
//first/last stop can't be repositioned
mPositionSpinBox->setDisabled( true );
mDeleteStopButton->setDisabled( true );
}
else
{
mPositionSpinBox->setDisabled( false );
mDeleteStopButton->setDisabled( false );
}
updatePlot();
}
void QgsVectorGradientColorRampV2Dialog::colorWidgetChanged( const QColor &color )
{
mStopEditor->setSelectedStopColor( color );
}
void QgsVectorGradientColorRampV2Dialog::on_mPositionSpinBox_valueChanged( double val )
{
mStopEditor->setSelectedStopOffset( val / 100.0 );
}
void QgsVectorGradientColorRampV2Dialog::on_mPlotHueCheckbox_toggled( bool checked )
{
mHueCurve->setVisible( checked );
updatePlot();
}
void QgsVectorGradientColorRampV2Dialog::on_mPlotLightnessCheckbox_toggled( bool checked )
{
mLightnessCurve->setVisible( checked );
updatePlot();
}
void QgsVectorGradientColorRampV2Dialog::on_mPlotSaturationCheckbox_toggled( bool checked )
{
mSaturationCurve->setVisible( checked );
updatePlot();
}
void QgsVectorGradientColorRampV2Dialog::on_mPlotAlphaCheckbox_toggled( bool checked )
{
mAlphaCurve->setVisible( checked );
updatePlot();
}
void QgsVectorGradientColorRampV2Dialog::plotMousePress( QPointF point )
{
//find closest part
double minDist = 1;
mCurrentPlotColorComponent = -1;
mCurrentPlotMarkerIndex = -1;
// first color
for ( int i = 0; i < mRamp->count(); ++i )
{
QColor currentCol;
double currentOff = 0.0;
if ( i == 0 )
{
currentOff = 0.0;
currentCol = mRamp->color1();
}
else if ( i == mRamp->count() - 1 )
{
currentOff = 1.0;
currentCol = mRamp->color2();
}
else
{
currentOff = mRamp->stops().at( i - 1 ).offset;
currentCol = mRamp->stops().at( i - 1 ).color;
}
double currentDist;
if ( mPlotHueCheckbox->isChecked() )
{
currentDist = qPow( point.x() - currentOff, 2.0 ) + qPow( point.y() - currentCol.hslHueF(), 2.0 );
if ( currentDist < minDist )
{
minDist = currentDist;
mCurrentPlotColorComponent = 0;
mCurrentPlotMarkerIndex = i;
}
}
if ( mPlotLightnessCheckbox->isChecked() )
{
currentDist = qPow( point.x() - currentOff, 2.0 ) + qPow( point.y() - currentCol.lightnessF(), 2.0 );
if ( currentDist < minDist )
{
minDist = currentDist;
mCurrentPlotColorComponent = 1;
mCurrentPlotMarkerIndex = i;
}
}
if ( mPlotSaturationCheckbox->isChecked() )
{
currentDist = qPow( point.x() - currentOff, 2.0 ) + qPow( point.y() - currentCol.hslSaturationF(), 2.0 );
if ( currentDist < minDist )
{
minDist = currentDist;
mCurrentPlotColorComponent = 2;
mCurrentPlotMarkerIndex = i;
}
}
if ( mPlotAlphaCheckbox->isChecked() )
{
currentDist = qPow( point.x() - currentOff, 2.0 ) + qPow( point.y() - currentCol.alphaF(), 2.0 );
if ( currentDist < minDist )
{
minDist = currentDist;;
mCurrentPlotColorComponent = 3;
mCurrentPlotMarkerIndex = i;
}
}
}
// watch out - selected stop index may differ if stops in editor are out of order!!!
if ( mCurrentPlotMarkerIndex >= 0 )
mStopEditor->selectStop( mCurrentPlotMarkerIndex );
}
void QgsVectorGradientColorRampV2Dialog::plotMouseRelease( QPointF )
{
mCurrentPlotColorComponent = -1;
}
void QgsVectorGradientColorRampV2Dialog::plotMouseMove( QPointF point )
{
QColor newColor = mStopEditor->selectedStop().color;
if ( mCurrentPlotColorComponent == 0 )
newColor = QColor::fromHslF( qBound( 0.0, point.y(), 1.0 ), newColor.hslSaturationF(), newColor.lightnessF(), newColor.alphaF() );
else if ( mCurrentPlotColorComponent == 1 )
newColor = QColor::fromHslF( newColor.hslHueF(), newColor.hslSaturationF(), qBound( 0.0, point.y(), 1.0 ), newColor.alphaF() );
else if ( mCurrentPlotColorComponent == 2 )
newColor = QColor::fromHslF( newColor.hslHueF(), qBound( 0.0, point.y(), 1.0 ), newColor.lightnessF(), newColor.alphaF() );
else if ( mCurrentPlotColorComponent == 3 )
newColor = QColor::fromHslF( newColor.hslHueF(), newColor.hslSaturationF(), newColor.lightnessF(), qBound( 0.0, point.y(), 1.0 ) );
mStopEditor->setSelectedStopDetails( newColor, qBound( 0.0, point.x(), 1.0 ) );
}
bool byX( QPointF p1, QPointF p2 )
{
return p1.x() < p2.x();
}
void QgsVectorGradientColorRampV2Dialog::addPlotMarker( double x, double y, const QColor& color, bool isSelected )
{
QColor borderColor = color.darker( 200 );
borderColor.setAlpha( 255 );
QColor brushColor = color;
brushColor.setAlpha( 255 );
QwtPlotMarker *marker = new QwtPlotMarker();
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
marker->setSymbol( new QwtSymbol( QwtSymbol::Ellipse, QBrush( brushColor ), QPen( borderColor, isSelected ? 2 : 1 ), QSize( 10, 10 ) ) );
#else
marker->setSymbol( QwtSymbol( QwtSymbol::Ellipse, QBrush( brushColor ), QPen( borderColor, isSelected ? 2 : 1 ), QSize( 10, 10 ) ) );
#endif
marker->setValue( x, y );
marker->attach( mPlot );
marker->setRenderHint( QwtPlotItem::RenderAntialiased, true );
mMarkers << marker;
}
void QgsVectorGradientColorRampV2Dialog::addMarkersForColor( double x, const QColor& color, bool isSelected )
{
if ( mPlotHueCheckbox->isChecked() )
addPlotMarker( x, color.hslHueF(), color, isSelected && mCurrentPlotColorComponent == 0 );
if ( mPlotLightnessCheckbox->isChecked() )
addPlotMarker( x, color.lightnessF(), color, isSelected && mCurrentPlotColorComponent == 1 );
if ( mPlotSaturationCheckbox->isChecked() )
addPlotMarker( x, color.hslSaturationF(), color, isSelected && mCurrentPlotColorComponent == 2 );
if ( mPlotAlphaCheckbox->isChecked() )
addPlotMarker( x, color.alphaF(), color, isSelected && mCurrentPlotColorComponent == 3 );
}
void QgsVectorGradientColorRampV2Dialog::updatePlot()
{
// remove existing markers
Q_FOREACH ( QwtPlotMarker* marker, mMarkers )
{
marker->detach();
delete marker;
}
mMarkers.clear();
QPolygonF lightnessPoints;
QPolygonF huePoints;
QPolygonF saturationPoints;
QPolygonF alphaPoints;
lightnessPoints << QPointF( 0.0, mRamp->color1().lightnessF() );
huePoints << QPointF( 0.0, mRamp->color1().hslHueF() );
saturationPoints << QPointF( 0.0, mRamp->color1().hslSaturationF() );
alphaPoints << QPointF( 0.0, mRamp->color1().alphaF() );
addMarkersForColor( 0, mRamp->color1(), mCurrentPlotMarkerIndex == 0 );
int i = 1;
Q_FOREACH ( const QgsGradientStop& stop, mRamp->stops() )
{
lightnessPoints << QPointF( stop.offset, stop.color.lightnessF() );
huePoints << QPointF( stop.offset, stop.color.hslHueF() );
saturationPoints << QPointF( stop.offset, stop.color.hslSaturationF() );
alphaPoints << QPointF( stop.offset, stop.color.alphaF() );
addMarkersForColor( stop.offset, stop.color, mCurrentPlotMarkerIndex == i );
i++;
}
//add extra intermediate points
for ( double p = 0.001; p < 1.0; p += 0.001 )
{
QColor c = mRamp->color( p );
lightnessPoints << QPointF( p, c.lightnessF() );
huePoints << QPointF( p, c.hslHueF() );
saturationPoints << QPointF( p, c.hslSaturationF() );
alphaPoints << QPointF( p, c.alphaF() );
}
lightnessPoints << QPointF( 1.0, mRamp->color2().lightnessF() );
huePoints << QPointF( 1.0, mRamp->color2().hslHueF() );
saturationPoints << QPointF( 1.0, mRamp->color2().hslSaturationF() );
alphaPoints << QPointF( 1.0, mRamp->color2().alphaF() );
addMarkersForColor( 1.0, mRamp->color2(), mCurrentPlotMarkerIndex == i );
qSort( lightnessPoints.begin(), lightnessPoints.end(), byX );
qSort( huePoints.begin(), huePoints.end(), byX );
qSort( saturationPoints.begin(), saturationPoints.end(), byX );
qSort( alphaPoints.begin(), alphaPoints.end(), byX );
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
mLightnessCurve->setSamples( lightnessPoints );
mHueCurve->setSamples( huePoints );
mSaturationCurve->setSamples( saturationPoints );
mAlphaCurve->setSamples( alphaPoints );
#else
mLightnessCurve->setData( lightnessPoints );
mHueCurve->setData( huePoints );
mSaturationCurve->setData( saturationPoints );
mAlphaCurve->setData( alphaPoints );
#endif
mPlot->replot();
}
void QgsVectorGradientColorRampV2Dialog::updateRampFromStopEditor()
{
*mRamp = mStopEditor->gradientRamp();
mPositionSpinBox->blockSignals( true );
mPositionSpinBox->setValue( mStopEditor->selectedStop().offset * 100 );
mPositionSpinBox->blockSignals( false );
mColorWidget->blockSignals( true );
mColorWidget->setColor( mStopEditor->selectedStop().color );
mColorWidget->blockSignals( false );
updateColorButtons();
updatePlot();
}
void QgsVectorGradientColorRampV2Dialog::setColor1( const QColor& color )
{
mRamp->setColor1( color );
updatePreview();
mStopEditor->setColor1( color );
updateColorButtons();
}
void QgsVectorGradientColorRampV2Dialog::setColor2( const QColor& color )
{
mRamp->setColor2( color );
updatePreview();
mStopEditor->setColor2( color );
updateColorButtons();
}
void QgsVectorGradientColorRampV2Dialog::setStopColor( QTreeWidgetItem* item, const QColor& color )
/// @cond PRIVATE
QgsGradientPlotEventFilter::QgsGradientPlotEventFilter( QwtPlot *plot )
: QObject( plot )
, mPlot( plot )
{
QSize iconSize( 16, 16 );
QPixmap pixmap( iconSize );
pixmap.fill( QColor( 0, 0, 0, 0 ) );
QRect rect( 1, 1, iconSize.width() - 2, iconSize.height() - 2 );
// draw a slightly rounded rectangle
QPainter p;
p.begin( &pixmap );
p.setPen( Qt::NoPen );
p.setRenderHint( QPainter::Antialiasing );
p.setBrush( color );
p.drawRoundedRect( rect, 2, 2 );
p.end();
item->setIcon( 0, QIcon( pixmap ) );
item->setData( 0, StopColorRole, color );
item->setText( 0, color.name() );
mPlot->canvas()->installEventFilter( this );
}
void QgsVectorGradientColorRampV2Dialog::setItemStopColor( const QColor& newColor )
bool QgsGradientPlotEventFilter::eventFilter( QObject *object, QEvent *event )
{
if ( mCurrentItem )
if ( !mPlot->isEnabled() )
return QObject::eventFilter( object, event );
switch ( event->type() )
{
setStopColor( mCurrentItem, newColor );
updatePreview();
}
}
void QgsVectorGradientColorRampV2Dialog::stopDoubleClicked( QTreeWidgetItem* item, int column )
{
if ( column == 0 )
{
QColor color;
QSettings settings;
//using native color dialogs?
bool useNative = settings.value( "/qgis/native_color_dialogs", false ).toBool();
if ( settings.value( "/qgis/live_color_dialogs", false ).toBool() )
case QEvent::MouseButtonPress:
{
mCurrentItem = item;
if ( useNative )
const QMouseEvent* mouseEvent = static_cast<QMouseEvent* >( event );
if ( mouseEvent->button() == Qt::LeftButton )
{
color = QgsColorDialog::getLiveColor(
item->data( 0, StopColorRole ).value<QColor>(),
this, SLOT( setItemStopColor( const QColor& ) ),
this, tr( "Edit Stop Color" ), QColorDialog::ShowAlphaChannel );
emit mousePress( mapPoint( mouseEvent->pos() ) );
}
else
{
color = QgsColorDialogV2::getLiveColor(
item->data( 0, StopColorRole ).value<QColor>(), this, SLOT( setItemStopColor( const QColor& ) ),
this, tr( "Edit Stop Color" ), true );
}
mCurrentItem = nullptr;
break;
}
else
case QEvent::MouseMove:
{
color = QgsColorDialogV2::getColor( item->data( 0, StopColorRole ).value<QColor>(), this, tr( "Edit Stop Color" ), true );
const QMouseEvent* mouseEvent = static_cast<QMouseEvent* >( event );
if ( mouseEvent->buttons() & Qt::LeftButton )
{
// only emit when button pressed
emit mouseMove( mapPoint( mouseEvent->pos() ) );
}
break;
}
if ( !color.isValid() )
return;
setStopColor( item, color );
updatePreview();
case QEvent::MouseButtonRelease:
{
const QMouseEvent* mouseEvent = static_cast<QMouseEvent* >( event );
if ( mouseEvent->button() == Qt::LeftButton )
{
emit mouseRelease( mapPoint( mouseEvent->pos() ) );
}
break;
}
default:
break;
}
else
{
bool ok;
double key = item->data( 0, StopOffsetRole ).toDouble();
// allow for floating-point values
double val = key * 100;
val = QInputDialog::getDouble( this, tr( "Offset of the stop" ),
tr( "Please enter offset in percents (%) of the new stop" ),
val, 0, 100, 2, &ok );
if ( !ok )
return;
double newkey = val / 100.0;
item->setText( 1, ( val < 10 ) ? '0' + QString::number( val ) : QString::number( val ) );
item->setData( 0, StopOffsetRole, newkey );
updatePreview();
}
return QObject::eventFilter( object, event );
}
void QgsVectorGradientColorRampV2Dialog::addStop()
QPointF QgsGradientPlotEventFilter::mapPoint( QPointF point ) const
{
// Native Mac dialog works only for Qt Carbon
// Qt bug: http://bugreports.qt.nokia.com/browse/QTBUG-14889
// Qt 4.7 Mac Cocoa bug: calling QInputDialog::getInt after QColorDialog::getColor will freeze app
// workaround: call QColorDialog::getColor below instead of here,
// but not needed at this time because of the other Qt bug
// FIXME need to also check max QT_VERSION when Qt bug(s) fixed
#ifndef Q_OS_MAC
QColor color = QgsColorDialogV2::getColor( QColor(), this, tr( "Add Color Stop" ), true );
if ( !mPlot )
return QPointF();
if ( !color.isValid() )
return;
activateWindow();
#endif
bool ok;
double val = 50.0;
val = QInputDialog::getDouble( this, tr( "Offset of the stop" ),
tr( "Please enter offset in percents (%) of the new stop" ),
val, 0, 100, 2, &ok );
if ( !ok )
return;
activateWindow();
double key = val / 100.0;
QStringList lst;
lst << "." << QString(( val < 10 ) ? '0' + QString::number( val ) : QString::number( val ) );
#ifdef Q_OS_MAC
QColor color = QgsColorDialogV2::getColor( QColor(), this, tr( "Add Color Stop" ), true );
if ( !color.isValid() )
return;
activateWindow();
#endif
QTreeWidgetItem* item = new QTreeWidgetItem( lst );
setStopColor( item, color );
item->setData( 0, StopOffsetRole, key );
treeStops->addTopLevelItem( item );
treeStops->resizeColumnToContents( 0 );
treeStops->setColumnWidth( 0, treeStops->columnWidth( 0 ) + 20 );
updatePreview();
return QPointF( mPlot->canvasMap( QwtPlot::xBottom ).invTransform( point.x() ),
mPlot->canvasMap( QwtPlot::yLeft ).invTransform( point.y() ) );
}
void QgsVectorGradientColorRampV2Dialog::removeStop()
{
QTreeWidgetItem* item = treeStops->currentItem();
if ( !item )
return;
int index = treeStops->indexOfTopLevelItem( item );
treeStops->takeTopLevelItem( index );
updatePreview();
}
void QgsVectorGradientColorRampV2Dialog::toggledStops( bool on )
{
Q_UNUSED( on );
updatePreview();
}
///@endcond

View File

@ -21,6 +21,11 @@
#include "ui_qgsvectorgradientcolorrampv2dialogbase.h"
class QgsVectorGradientColorRampV2;
class QwtPlot;
class QwtPlotCurve;
class QwtPlotMarker;
class QwtPlotPicker;
class QgsGradientPlotEventFilter;
class GUI_EXPORT QgsVectorGradientColorRampV2Dialog : public QDialog, private Ui::QgsVectorGradientColorRampV2DialogBase
{
@ -28,34 +33,81 @@ class GUI_EXPORT QgsVectorGradientColorRampV2Dialog : public QDialog, private Ui
public:
QgsVectorGradientColorRampV2Dialog( QgsVectorGradientColorRampV2* ramp, QWidget* parent = nullptr );
~QgsVectorGradientColorRampV2Dialog();
public slots:
void setColor1( const QColor& color );
void setColor2( const QColor& color );
void toggledStops( bool on );
void addStop();
void removeStop();
void stopDoubleClicked( QTreeWidgetItem* item, int column );
void setItemStopColor( const QColor& newColor );
protected slots:
void on_cboType_currentIndexChanged( int index );
void on_btnInformation_pressed();
protected:
void updateStops();
void updatePreview();
void setStopColor( QTreeWidgetItem* item, const QColor& color );
QgsVectorGradientColorRampV2* mRamp;
static const int StopColorRole = Qt::UserRole + 1;
static const int StopOffsetRole = Qt::UserRole + 2;
private slots:
QTreeWidgetItem* mCurrentItem;
void updateRampFromStopEditor();
void updateColorButtons();
void updateStopEditor();
void selectedStopChanged( const QgsGradientStop& stop );
void colorWidgetChanged( const QColor& color );
void on_mPositionSpinBox_valueChanged( double val );
void on_mPlotHueCheckbox_toggled( bool checked );
void on_mPlotLightnessCheckbox_toggled( bool checked );
void on_mPlotSaturationCheckbox_toggled( bool checked );
void on_mPlotAlphaCheckbox_toggled( bool checked );
void plotMousePress( QPointF point );
void plotMouseRelease( QPointF point );
void plotMouseMove( QPointF point );
private:
QwtPlotCurve* mLightnessCurve;
QwtPlotCurve* mSaturationCurve;
QwtPlotCurve* mHueCurve;
QwtPlotCurve* mAlphaCurve;
QList< QwtPlotMarker* > mMarkers;
QwtPlotPicker* mPicker;
QgsGradientPlotEventFilter* mPlotFilter;
int mCurrentPlotColorComponent;
int mCurrentPlotMarkerIndex;
void updatePlot();
void addPlotMarker( double x, double y, const QColor &color, bool isSelected = false );
void addMarkersForColor( double x, const QColor &color, bool isSelected = false );
};
//
// NOTE:
// For private only, not part of stable api or exposed to Python bindings
//
/// @cond PRIVATE
class GUI_EXPORT QgsGradientPlotEventFilter: public QObject
{
Q_OBJECT
public:
QgsGradientPlotEventFilter( QwtPlot *plot );
virtual ~QgsGradientPlotEventFilter() {}
virtual bool eventFilter( QObject* object, QEvent* event ) override;
signals:
void mousePress( QPointF );
void mouseRelease( QPointF );
void mouseMove( QPointF );
private:
QwtPlot* mPlot;
QPointF mapPoint( QPointF point ) const;
};
///@endcond
#endif

View File

@ -6,95 +6,27 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>379</height>
<width>868</width>
<height>638</height>
</rect>
</property>
<property name="windowTitle">
<string>Gradient color ramp</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
<item>
<layout class="QGridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,0,0,0,0,1,0">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Color 1</string>
<string>Color &amp;1</string>
</property>
<property name="buddy">
<cstring>btnColor1</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Color 2</string>
</property>
<property name="buddy">
<cstring>btnColor2</cstring>
</property>
</widget>
</item>
<item row="0" column="2" rowspan="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>71</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cboType">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsColorButtonV2" name="btnColor2">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item>
<widget class="QgsColorButtonV2" name="btnColor1">
<property name="minimumSize">
<size>
@ -113,83 +45,265 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Color &amp;2</string>
</property>
<property name="buddy">
<cstring>btnColor2</cstring>
</property>
</widget>
</item>
<item>
<widget class="QgsColorButtonV2" name="btnColor2">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Type</string>
</property>
<property name="buddy">
<cstring>cboType</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboType">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupStops">
<widget class="QgsGradientStopEditor" name="mStopEditor" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Multiple stops</string>
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_1">
<item row="0" column="0" rowspan="3">
<widget class="QTreeWidget" name="treeStops">
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="columnCount">
<number>2</number>
</property>
<column>
<property name="text">
<string>Color</string>
</property>
</column>
<column>
<property name="text">
<string>Offset (%)</string>
</property>
</column>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="btnAddStop">
<property name="text">
<string>Add stop</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="btnRemoveStop">
<property name="text">
<string>Remove stop</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Preview</string>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="lblPreview">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>850</width>
<height>494</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
<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="QgsCollapsibleGroupBox" name="groupBox_2">
<property name="title">
<string>Gradient stop</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Relative &amp;position</string>
</property>
<property name="buddy">
<cstring>mPositionSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsDoubleSpinBox" name="mPositionSpinBox">
<property name="suffix">
<string> %</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="mDeleteStopButton">
<property name="text">
<string>&amp;Delete stop</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="4">
<widget class="QgsCompoundColorWidget" name="mColorWidget" native="true">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBox" name="mPlotGroupBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Plot</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QwtPlot" name="mPlot">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="mPlotHueCheckbox">
<property name="text">
<string>Hue</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mPlotSaturationCheckbox">
<property name="text">
<string>Saturation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mPlotLightnessCheckbox">
<property name="text">
<string>Lightness</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mPlotAlphaCheckbox">
<property name="text">
<string>Alpha</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
@ -197,7 +311,7 @@
<item>
<widget class="QPushButton" name="btnInformation">
<property name="text">
<string>Information</string>
<string>&amp;Information</string>
</property>
</widget>
</item>
@ -222,17 +336,45 @@
<header>qgscolorbuttonv2.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsGradientStopEditor</class>
<extends>QWidget</extends>
<header>qgsgradientstopeditor.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsCompoundColorWidget</class>
<extends>QWidget</extends>
<header>qgscompoundcolorwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsCollapsibleGroupBox</class>
<extends>QGroupBox</extends>
<header>qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QwtPlot</class>
<extends>QFrame</extends>
<header>qwt_plot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>btnColor1</tabstop>
<tabstop>btnColor2</tabstop>
<tabstop>cboType</tabstop>
<tabstop>groupStops</tabstop>
<tabstop>treeStops</tabstop>
<tabstop>btnAddStop</tabstop>
<tabstop>btnRemoveStop</tabstop>
<tabstop>mStopEditor</tabstop>
<tabstop>mPositionSpinBox</tabstop>
<tabstop>mDeleteStopButton</tabstop>
<tabstop>mColorWidget</tabstop>
<tabstop>btnInformation</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>