PDA

View Full Version : How to change the mouse pattern of QwtPlotPicker?



Willothewisp
28th December 2010, 07:56
I found if I newed a QwtPlotPicker with the selection type of QwtPicker::PolygonSelection, then I could start a pick with LeftButton, and continue the following several picks with RightButton, and end the selection with another click of LeftButton. But I want my interaction like this: start with LeftButton and continue the other picks also with LeftButton, at last end the selection by double-clicking the Left Button.
How could I do?
Thanks~

d_stranz
30th December 2010, 17:40
You will probably need to implement a custom QwtPickerMachine to do this, since you are asking for the same mouse event to be interpreted in two different ways depending on the state.

You can probably use QwtPickerClickPointMachine as a base class, then override the transition() method.



QwtPickerMachine::CommandList MyQwtPickerMachine::transition( const QwtEventPattern & eventPattern, const QEvent * pEvent )
{
QwtPickerMachine::CommandList cmdList;

switch( pEvent->type() )
{
case QEvent::MouseButtonPress:
{
if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, (const QMouseEvent *)pEvent ) )
{
if ( state() == 0 )
{
cmdList += Begin;
cmdList += Append;
setState(1);
}
else if ( state() == 1 )
{
cmdList += Append;
}
}
}
break;

default:
cmdList = QwtPickerClickPointMachine::transition( eventPattern, pEvent );
break;
}
return cmdList;
}


This will handle the two interpretations of the left single-click. The state() value allows you to determine what has already happened. To handle the left double-click to end the sequence, you need another case:



case QEvent::MouseButtonDblClick:
{
if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, (const QMouseEvent *)pEvent ) )
{
if ( state() == 1 )
{
cmdList += Append;
cmdList += End;
setState( 0 );
}
}
}
break;


Next, you need to implement a custom QwtPlotPicker class, because (at least in Qwt 5.2) there is no way to set a custom QwtPickerMachine for one of the standard QwtPicker classes.

In this class, you need to create an instance of your customer picker machine, and reimplement the QwtPlotPicker::transition method to call your picker machine's transition() method:



void MyQwtPlotPicker::transition( const QEvent * pEvent )
{
// "myStateMachine" is an instance of MyQwtPickerMachine
if ( !myStateMachine )
QwtPlotPicker::transition( pEvent );
else
{
QwtPickerMachine::CommandList commandList =
myStateMachine->transition( *this, pEvent );

QPoint pos;
switch( pEvent->type() )
{
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
{
pos = ((QMouseEvent *)pEvent)->pos();
break;
}

default:
pos = parentWidget()->mapFromGlobal( QCursor::pos() );
break;
}

for ( uint i = 0; i < (uint)commandList.count(); i++ )
{
switch( commandList[i] )
{
case QwtPickerMachine::Begin:
{
begin();
break;
}

case QwtPickerMachine::Append:
{
append( pos );
break;
}

case QwtPickerMachine::Move:
{
QwtPlotPicker::move( pos );
break;
}

case QwtPickerMachine::End:
{
end();
break;
}
}
}
}
}


Sorry for the long answer. Like many things in Qwt, it takes a while to figure out what is going on inside, but when you do you realize that it is a very powerful design and the changes needed to do something special aren't so hard.

I went through this whole thing last week to implement a custom zoomer that would duplicate the behaviour of an ActiveX control I am replacing with QwtPlot. My zoomer needs to zoom in x only with left-click-drag, zoom out with left-click-only, select a range with right-click-drag, or display a context menu with right-click-delay. It took a few days to get it right, but now the behaviour matches perfectly.
Hope this helps.

d_stranz
1st January 2011, 17:00
I realized there are a couple of errors in the code I posted. In the two case statements, if the mouse button press doesn't match the mouse pattern, then they get thrown away. This is wrong; the event should be passed up to the base class for it to handle instead. So, fix the code in both places by adding this:



QwtPickerMachine::CommandList MyQwtPickerMachine::transition( const QwtEventPattern & eventPattern, const QEvent * pEvent )
{
QwtPickerMachine::CommandList cmdList;

switch( pEvent->type() )
{
case QEvent::MouseButtonPress:
{
if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, (const QMouseEvent *)pEvent ) )
{
//... same as original code
}
else // These two lines are new; add the same thing to the double-click case
cmdList = QwtPickerClickPointMachine::transition( eventPattern, pEvent );
}
break;

default:
cmdList = QwtPickerClickPointMachine::transition( eventPattern, pEvent );
break;
}
return cmdList;
}