[FEATURE] Color picker option in color button context menu, which allows 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.

This commit is contained in:
Nyall Dawson 2014-07-06 21:26:47 +10:00
parent e1e0b15798
commit 33b5e8ce1b
5 changed files with 158 additions and 16 deletions

View File

@ -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:
/**
@ -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 );
@ -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
*/

View File

@ -18,6 +18,7 @@
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgssymbollayerv2utils.h"
#include "qgscursors.h"
#include <QPainter>
#include <QSettings>
@ -26,6 +27,7 @@
#include <QMenu>
#include <QClipboard>
#include <QDrag>
#include <QDesktopWidget>
#ifdef Q_OS_WIN
#include <windows.h>
@ -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() ) );
@ -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 );
@ -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 );
@ -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?
@ -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 ) )
@ -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 )
@ -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
@ -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();
@ -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() );

View File

@ -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:
/**
@ -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
*/
@ -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.
@ -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();

View File

@ -180,3 +180,26 @@ const char *cross_hair_cursor[] =
" +.+ "
};
const char *sampler_cursor[] =
{
"16 16 3 1",
" » c None",
".» c #000000",
"+» c #FFFFFF",
".. ",
".+. ",
" .+.. ",
" .++. ",
" .+++. ",
" .+++. ",
" .+++. ",
" .+++... ",
" .++... ",
" ...... ",
" ....... ",
" ........ ",
" .......",
" ......",
" .....",
" ... "
};

1
src/gui/qgscursors.h Normal file → Executable file
View File

@ -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