PDA

View Full Version : Custom plot selection



SeanM
24th February 2014, 17:15
I'm trying to create a custom plot area selection effect and just want to make sure I'm thinking of it the right way. I've attached a couple screenshots/mockups to illustrate what I'm going for.

What I need: Our plots have time on the X axis and multiple curves. The user needs to be able to select a subset of data bounded by start and end times, but there's no need to be able to select based on Y values. So the selection should contain the entire Y content of the plot, between the two X points the user selected. After the user releases the mouse button to end the selection, the selected area should remain visible, ideally it would have solid vertical lines marking the start/end points (QPen with alpha of 255), and then have a semi-transparent filled in area (QBrush with alpha of 50% or so). The desired effect is shown in this mocked up image:
10073
So in this mockup, the user clicked at X=95.7333, dragged until 221.1770 and released.

My attempts so far:
10072
In this first image, I've got a QwtPlotPicker with a VLineRubberBand and a QwtPickerDragPointMachine. This gives a nice vertical cursor showing where the selection is starting from, but as the user performs a click/drag/release sequence there's a couple issues: the cursor moves from the starting point to the ending point, so when you reach the end, there's no visual cue to where the selection started, and the signal that is emitted is QwtPlotPicker::selected(const QPointF &pos), which only contains the ending (X,Y) values.

Second attempt:
10074
In this image, I'm now using a RectRubberBand and a QwtPickerDragRectMachine. This gives the visual of both the starting and ending selection positions (at least up until mouse release), and emits QwtPlotPicker::selected(const QRectF&), which gives me both the starting X and width, so that's good. But visually it doesn't span the entire plot Y range, and doesn't stay visible after the mouse is released.

So it seems like I want the VLineRubberBand visual combined with a QwtPickerDragRectMachine (but those don't seem compatible? http://qwt.sourceforge.net/class_qwt_picker.html#ab36c79d8ff20aba5b778d2823c1 f7894). I need to add a fill effect, and then I need some way to get the selection to remain visible until the next selection starts?

I think I need to:
- Inherit from QwtPlotPicker
- Figure out how to get a VLineRubberBand effect combined with emitting a QRectF signal so that I get both start/end or start/width, probably by overloading virtual void drawRubberBand (QPainter *) const?
- Add a fill effect during the selection, I think using QBrush? Looks like this helps? http://www.qtcentre.org/threads/48904-Set-semi-transparent-background-on-QwtPlotZoomer-RectRubberBand?highlight=rubberband+fill
- And then get the visual to persist. QwtWidgetOverlay?

Cah
24th February 2014, 23:28
I think I need to:
- Inherit from QwtPlotPicker

Yes ... I would subclass QwtPlotPicker and re-define eventFilter()

For the vertical lines, I would use QwtPlotMarker with the QwtPlotMarker::LineStyle set to VLine

Uwe
25th February 2014, 07:00
Use a picker with a RectRubberBand and one of the machines made for rectangles ( f.e. QwtPickerDragRectMachine ). Overload QwtPicker::drawRubberband() where you do a QPainter::drawRect() using the y coordinates from of bounding rectangle of the pickArea.

When the selection is done ( QwtPlotPicker::selected(const QRectF&) ) simply attach a QwtPlotZoneItem according to the x coordinates of the selected rectangle.

Uwe

SeanM
25th February 2014, 14:13
Thanks Uwe, that's essentially what I did for the drawing part, although in drawRubberband() I had created a new case for the combination of VLineRubberBand and a QwtPickerDragRectMachine. In my head, I was thinking of the selection more in the VLineRubberBand sense since I wanted the full vertical range of the plot, but your suggestion actually does seem more consistent since I really am selecting a rectangle.

I hadn't stumbled across QwtPlotZoneItem yet so I'll check that out. The name of that class just didn't trigger that it would apply.

Uwe
25th February 2014, 14:38
The name of that class just didn't trigger that it would apply.
What would be a better one ?

Uwe

SeanM
25th February 2014, 17:12
I don't necessarily think there's a better name for it, now that you've pointed me at it and I know it exists, I think it's aptly named for what it does. I think I was thinking in too narrow of a context so I was looking for some sort of QwtSelectionXXX class, and didn't really wonder what else it could be called.

In a more general sense, I think I was more expecting that the Qwt*Picker classes would have some sort of a keepVisible(bool) flag, which would indicate whether the selected area should remain visibly selected until the next selection sequence starts, since they're already doing the painting for creating the selection. Once I determined that wasn't the case, I just wasn't sure where to go next. And I didn't see any of the examples show either a selection where the selected area was filled with a QBrush, nor did I find an example where the selection visual was retained after the mouseReleaseEvent.

gorec323
28th February 2014, 20:05
Use a picker with a RectRubberBand and one of the machines made for rectangles ( f.e. QwtPickerDragRectMachine ). Overload QwtPicker::drawRubberband() where you do a QPainter::drawRect() using the y coordinates from of bounding rectangle of the pickArea.

Uwe

Function QwtPicker::drawRubberband() is not used anywhere in the source code, qwt library.

Uwe
1st March 2014, 09:59
Of course it is ( see QwtPickerRubberband::drawOverlay in qwt_picker.cpp ) - or where else do you believe gets your VLineRubberBand painted ?

Uwe

gorec323
1st March 2014, 12:31
Of course it is ( see QwtPickerRubberband::drawOverlay in qwt_picker.cpp ) - or where else do you believe gets your VLineRubberBand painted ?

Uwe

Sorry. It's my error.

class ChartZoomer : public QwtPlotZoomer
// not correct
// void drawRubberBand(QPainter *painter); // <-- it is my error!!!!
// correct
void drawRubberBand(QPainter *painter) const;
}

void drawRubberBand(QPainter *painter) const; <> void drawRubberBand(QPainter *painter);

alketi
6th May 2020, 17:37
I have a similar question to the OP, so I'll put it here rather than starting a new thread, just to keep it all in context.

How does one temporarily fill the rectangular rubberband area with a translucent color? (I want the fill visible only while the rubber band is being drawn.)

My attempt was to override QwtPicker and re-implement drawRubberBand(), however this code actually does nothing. And even commenting-out the parent call at the end still results in the rectangular zoom outline being drawn. So I'm confused. Any advice Uwe would be appreciated.

SETUP


m_picker = new XQwtPicker( QwtPicker::RectRubberBand,
QwtPicker::AlwaysOn,
this->canvas() );
m_picker->setRubberBandPen( QColor( Qt::green ) );
m_picker->setRubberBand( QwtPicker::CrossRubberBand );
m_picker->setTrackerPen( QColor( Qt::white ) );


SUBCLASS

void XQwtPicker::drawRubberBand( QPainter *painter ) const
{
const QPolygon pa = QwtPicker::selection();
const QRect rect = QRect( pa.first(), pa.last() ).normalized();

QColor fillColor = QwtPicker::rubberBandPen().color();
fillColor.setAlpha(125);

QBrush fillBrush(fillColor);

QwtPainter::fillRect( painter, rect, fillBrush );

// QwtPicker::drawRubberBand(painter);
}

Uwe
10th May 2020, 13:50
You also need to implement:


QRegion XQwtPicker::rubberBandMask() const override; The smaller the region the better - but beside of environments like remote X11 you probably won't notice much difference when returning the complete canvas rectangle.
In your case the optimal solution would be the rectangle spanned by the w points. See https://qwt.sourceforge.io/class_qwt_widget_overlay.html#a413679fb15e072d5a40 4f237062b75fc

Have a look at the implementation of QwtPicker::rubberBandMask - it will help with how to find the rectangle.

HTH,
Uwe