PDA

View Full Version : Draw a line over a central widget



Thibaut
28th November 2019, 10:46
Hi!

I'm creating an application for data visualization.
To keep it simple, I have multiple plots (I'm using QCustomPlot), organized in a VLayout. I put this layout in a QWidget and set this widget as the Central Widget of the main window.

Now, I want to be able to draw a vertical line all over the central widget on the mouse position when I click it. Then I will compute the average of the data that are on this line.

I can't find a way to make it. Do you have any advice ?
I read that I may use QPainter but I can't figure out how it works...

d_stranz
28th November 2019, 18:41
First, you cannot use QWidget as the central widget if you want to do this. You must derive a class from QWidget and override the paintEvent() (and probably mouse events as well), which is where you will draw your line after calling the base class QWidget paintEvent.

Second, presumably the user can click in any of the stacked plots to initiate the averaging action. So you will need to implement a handler for these events in your plots. Those events are probably going to return values in plot (i.e. data) coordinates, not window (pixel) coordinates, so you will have to map from plot coordinate to window coordinates, since that is what you will use to draw your line.

While the user is moving the line to the final position, you will probably want to use QRubberBand to draw it. After the user has released the mouse, you would then use QPainter in the paintEvent to draw the permanent line.

You could also do this the other way around - capture the mouse events in pixel coordinates in the QWidget-based class, then map those to data coordinates for your averaging.

In any case, this is not a particularly easy problem to solve, especially if you want to also continue to use other mouse actions in the plots.

Thibaut
29th November 2019, 13:36
Thanks !
I did some research on the side too.

If I understand this correctly, I should derive a class from QWidget, CustomWidget1, to use it for the overlay (drawing and mouse event management), and another class, CustomWidget2, for the display of the plot.
Then I could make another QWidget, a wrapper, and set it to be the parent to the two previous ones.

Is it the correct way ?
I'm not sure if it's clear... I don't have access to my code now, I will post it if it doesn't work.

d_stranz
29th November 2019, 16:30
I suppose you could use a transparent overlay widget to handle the mouse events and line overlay. You would need to override the resizeEvent() in the parent widget to ensure that both the overlay and plot widget holder were resized to exactly the same size and position. This could be a bit tricky due to margins and other padding that gets added to composite widgets with layouts. You would not have to derive a custom widget as your plot widget holder if you use an overlay widget. This could remain as a plain QWidget since you no longer would have any need to override events there.

In the resizeEvent() handler, check the isVisible() value for each child widget and don't do anything if visible is false. The resizeEvent() might be called several times before the window is first shown as Qt does the initial layout, and the sizes at this point are generally not valid. Once the showEvent() occurs, the sizes in the resizeEvent() are then valid and can be used to resize the child windows.

Using an overlay also has the advantage that you can simply turn it off (hide()) when not needed.

A trick: If you find that you get excessive redraws of the plot widget when you are interacting with the overlay widget, when the mouse first gets pressed make a QImage snapshot (render()) of the plot widget, hide the plot widget, and use that snapshot as the background of your overlay. When the interaction stops (mouse released), remove the background image and show the original plot widget again.