Skip to content

Commit

Permalink
[FEATURE] Color picker option in color button context menu, which all…
Browse files Browse the repository at this point in the history
…ows for sampling a color from anywhere on the screen. Supports holding the left mouse button to continuously sample. Pressing space also triggers a sample for Windows, where mouse clicks outside the QGIS window are unavoidably propagated onward.
  • Loading branch information
nyalldawson committed Jul 8, 2014
1 parent e1e0b15 commit 33b5e8c
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 16 deletions.
18 changes: 14 additions & 4 deletions python/gui/qgscolorbutton.sip
Expand Up @@ -88,14 +88,14 @@ class QgsColorButton: QPushButton

public slots:
/**
* Sets the background pixmap for the button based upon set color and transparency.
* Sets the background pixmap for the button based upon color and transparency.
* Call directly to update background after adding/removing QColorDialog::ShowAlphaChannel option
* but the color has not changed, i.e. setColor() wouldn't update button and
* you want the button to retain the set color's alpha component regardless
*
* @param color Color for button background
* @note added in 1.9
*/
void setButtonBackground();
void setButtonBackground( QColor color = QColor() );

signals:
/**
Expand All @@ -112,7 +112,7 @@ class QgsColorButton: QPushButton
void showEvent( QShowEvent* e );
static const QPixmap& transpBkgrd();

/**
/**
* Reimplemented to detect right mouse button clicks on the color button and allow dragging colors
*/
void mousePressEvent( QMouseEvent* e );
Expand All @@ -122,6 +122,16 @@ class QgsColorButton: QPushButton
*/
void mouseMoveEvent( QMouseEvent *e );

/**
* Reimplemented to allow color picking
*/
void mouseReleaseEvent( QMouseEvent *e );

/**
* Reimplemented to allow cancelling color pick via keypress, and sample via space bar press
*/
void keyPressEvent( QKeyEvent *e );

/**
* Reimplemented to accept dragged colors
*/
Expand Down
106 changes: 97 additions & 9 deletions src/gui/qgscolorbutton.cpp
Expand Up @@ -18,6 +18,7 @@
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgssymbollayerv2utils.h"
#include "qgscursors.h"

#include <QPainter>
#include <QSettings>
Expand All @@ -26,6 +27,7 @@
#include <QMenu>
#include <QClipboard>
#include <QDrag>
#include <QDesktopWidget>

#ifdef Q_OS_WIN
#include <windows.h>
Expand Down Expand Up @@ -70,6 +72,7 @@ QgsColorButton::QgsColorButton( QWidget *parent, QString cdt, QColorDialog::Colo
, mAcceptLiveUpdates( true )
, mTempPNG( NULL )
, mColorSet( false )
, mPickingColor( false )
{
setAcceptDrops( true );
connect( this, SIGNAL( clicked() ), this, SLOT( onButtonClicked() ) );
Expand Down Expand Up @@ -114,6 +117,13 @@ void QgsColorButton::onButtonClicked()

void QgsColorButton::mousePressEvent( QMouseEvent *e )
{
if ( mPickingColor )
{
//don't show dialog if in color picker mode
e->accept();
return;
}

if ( e->button() == Qt::RightButton )
{
showContextMenu( e );
Expand Down Expand Up @@ -174,6 +184,23 @@ bool QgsColorButton::colorFromMimeData( const QMimeData * mimeData, QColor& resu

void QgsColorButton::mouseMoveEvent( QMouseEvent *e )
{
if ( mPickingColor )
{
//currently in color picker mode
if ( e->buttons() & Qt::LeftButton )
{
//if left button depressed, sample color under cursor and temporarily update button color
//to give feedback to user
QPixmap snappedPixmap = QPixmap::grabWindow( QApplication::desktop()->winId(), e->globalPos().x(), e->globalPos().y(), 1, 1 );
QImage snappedImage = snappedPixmap.toImage();
QColor hoverColor = snappedImage.pixel( 0, 0 );
setButtonBackground( hoverColor );
}
e->accept();
return;
}

//handle dragging colors from button
if ( !( e->buttons() & Qt::LeftButton ) )
{
QPushButton::mouseMoveEvent( e );
Expand Down Expand Up @@ -209,6 +236,52 @@ void QgsColorButton::mouseMoveEvent( QMouseEvent *e )
setDown( false );
}

void QgsColorButton::mouseReleaseEvent( QMouseEvent *e )
{
if ( mPickingColor )
{
//end color picking operation by sampling the color under cursor
stopPicking( e->globalPos() );
e->accept();
return;
}

QPushButton::mouseReleaseEvent( e );
}

void QgsColorButton::stopPicking( QPointF eventPos, bool sampleColor )
{
//release mouse and reset cursor
releaseMouse();
unsetCursor();
mPickingColor = false;

if ( !sampleColor )
{
//not sampling color, nothing more to do
return;
}

//grab snapshot of pixel under mouse cursor
QPixmap snappedPixmap = QPixmap::grabWindow( QApplication::desktop()->winId(), eventPos.x(), eventPos.y(), 1, 1 );
QImage snappedImage = snappedPixmap.toImage();
//extract color from pixel and set color
setColor( snappedImage.pixel( 0, 0 ) );
}

void QgsColorButton::keyPressEvent( QKeyEvent *e )
{
if ( !mPickingColor )
{
//if not picking a color, use default push button behaviour
QPushButton::keyPressEvent( e );
return;
}

//cancel picking, sampling the color if space was pressed
stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
}

void QgsColorButton::dragEnterEvent( QDragEnterEvent *e )
{
//is dragged data valid color data?
Expand Down Expand Up @@ -238,8 +311,10 @@ void QgsColorButton::showContextMenu( QMouseEvent *event )
colorContextMenu.addAction( copyColorAction );
QAction* pasteColorAction = new QAction( tr( "Paste color" ), 0 );
pasteColorAction->setEnabled( false );
colorContextMenu.addSeparator();
colorContextMenu.addAction( pasteColorAction );
QAction* pickColorAction = new QAction( tr( "Pick color" ), 0 );
colorContextMenu.addSeparator();
colorContextMenu.addAction( pickColorAction );

QColor clipColor;
if ( colorFromMimeData( QApplication::clipboard()->mimeData(), clipColor ) )
Expand All @@ -258,9 +333,18 @@ void QgsColorButton::showContextMenu( QMouseEvent *event )
//paste color
setColor( clipColor );
}
else if ( selectedAction == pickColorAction )
{
//pick color
QPixmap samplerPixmap = QPixmap(( const char ** ) sampler_cursor );
setCursor( QCursor( samplerPixmap, 0, 0 ) );
grabMouse();
mPickingColor = true;
}

delete copyColorAction;
delete pasteColorAction;
delete pickColorAction;
}

void QgsColorButton::setValidColor( const QColor& newColor )
Expand Down Expand Up @@ -321,8 +405,12 @@ void QgsColorButton::setColor( const QColor &color )
mColorSet = true;
}

void QgsColorButton::setButtonBackground()
void QgsColorButton::setButtonBackground( QColor color )
{
if ( !color.isValid() )
{
color = mColor;
}
if ( !text().isEmpty() )
{
// generate icon pixmap for regular pushbutton
Expand Down Expand Up @@ -355,11 +443,11 @@ void QgsColorButton::setButtonBackground()
p.setRenderHint( QPainter::Antialiasing );
p.setClipPath( roundRect );
p.setPen( Qt::NoPen );
if ( mColor.alpha() < 255 )
if ( color.alpha() < 255 )
{
p.drawTiledPixmap( rect, transpBkgrd() );
}
p.setBrush( mColor );
p.setBrush( color );
p.drawRect( rect );
p.end();

Expand All @@ -381,12 +469,12 @@ void QgsColorButton::setButtonBackground()
//QgsDebugMsg( QString( "%1 margin: %2" ).arg( objectName() ).arg( margin ) );

QString bkgrd = QString( " background-color: rgba(%1,%2,%3,%4);" )
.arg( mColor.red() )
.arg( mColor.green() )
.arg( mColor.blue() )
.arg( useAlpha ? mColor.alpha() : 255 );
.arg( color.red() )
.arg( color.green() )
.arg( color.blue() )
.arg( useAlpha ? color.alpha() : 255 );

if ( useAlpha && mColor.alpha() < 255 )
if ( useAlpha && color.alpha() < 255 )
{
QPixmap pixmap = transpBkgrd();
QRect rect( 0, 0, pixmap.width(), pixmap.height() );
Expand Down
26 changes: 23 additions & 3 deletions src/gui/qgscolorbutton.h
Expand Up @@ -113,14 +113,14 @@ class GUI_EXPORT QgsColorButton: public QPushButton

public slots:
/**
* Sets the background pixmap for the button based upon set color and transparency.
* Sets the background pixmap for the button based upon color and transparency.
* Call directly to update background after adding/removing QColorDialog::ShowAlphaChannel option
* but the color has not changed, i.e. setColor() wouldn't update button and
* you want the button to retain the set color's alpha component regardless
*
* @param color Color for button background
* @note added in 1.9
*/
void setButtonBackground();
void setButtonBackground( QColor color = QColor() );

signals:
/**
Expand All @@ -147,6 +147,16 @@ class GUI_EXPORT QgsColorButton: public QPushButton
*/
void mouseMoveEvent( QMouseEvent *e );

/**
* Reimplemented to allow color picking
*/
void mouseReleaseEvent( QMouseEvent *e );

/**
* Reimplemented to allow cancelling color pick via keypress, and sample via space bar press
*/
void keyPressEvent( QKeyEvent *e );

/**
* Reimplemented to accept dragged colors
*/
Expand All @@ -166,6 +176,7 @@ class GUI_EXPORT QgsColorButton: public QPushButton
bool mColorSet; // added in QGIS 2.1

QPoint mDragStartPosition;
bool mPickingColor;

/**
* Shows the color button context menu and handles copying and pasting color values.
Expand Down Expand Up @@ -201,6 +212,15 @@ class GUI_EXPORT QgsColorButton: public QPushButton
QString fullPath( const QString &path );
#endif

/**
* Ends a color picking operation
* @param eventPos global position of pixel to sample color from
* @param sampleColor set to true to actually sample the color, false to just cancel
* the color picking operation
* @note added in 2.5
*/
void stopPicking( QPointF eventPos, bool sampleColor = true );

private slots:
void onButtonClicked();

Expand Down
23 changes: 23 additions & 0 deletions src/gui/qgscursors.cpp
Expand Up @@ -180,3 +180,26 @@ const char *cross_hair_cursor[] =
" +.+ "
};

const char *sampler_cursor[] =
{
"16 16 3 1",
" » c None",
".» c #000000",
"+» c #FFFFFF",
".. ",
".+. ",
" .+.. ",
" .++. ",
" .+++. ",
" .+++. ",
" .+++. ",
" .+++... ",
" .++... ",
" ...... ",
" ....... ",
" ........ ",
" .......",
" ......",
" .....",
" ... "
};
1 change: 1 addition & 0 deletions src/gui/qgscursors.h 100644 → 100755
Expand Up @@ -31,6 +31,7 @@ extern GUI_EXPORT const char *capture_point_cursor[];
extern GUI_EXPORT const char *select_cursor[];
extern GUI_EXPORT const char *identify_cursor[];
extern GUI_EXPORT const char *cross_hair_cursor[];
extern GUI_EXPORT const char *sampler_cursor[];

#endif

0 comments on commit 33b5e8c

Please sign in to comment.