PDA

View Full Version : Align standalone QwtScaleWidget with plots



SeanM
20th August 2014, 16:14
Ok, hopefully this will be my last Qwt question for a little bit!

I'm still working on having a single QwtScaleWidget that isn't associated with a specific plot and use it to display the x-axis information for a group of vertically aligned plots in that are in a scroll area. The only issue I have remaining is figuring out how to properly align the standalone QwtScaleWidget with the plots.

Here's an image of where I'm at currently:
10567
Note how the scale widget up in the top white area isn't aligned with the plots/scale widgets in the grey area.

And here's where I want to get to (photoshopped image!)
10568
Scale widget in white area is lined up with plots. The per plot bottom scale widgets are disabled.

And here's a breakdown of all the widgets in play
10569

So the specific issues I'm having:
Calculating the correct width of the standalone QwtScaleWidget. I'm assuming maybe if I dig down into QwtPlot's code (maybe inside resizeEvent()?) I can copy how Uwe calculates the correct size for the scale widgets that are internal to the QwtPlot class? Right now, I'm emitting a signal in my myPlot's resizeEvent() (after calling QwtPlot::resizeEvent()) that emits the width of the bottom scale widget. This works when the bottom scale widget is enabled, but obviously doesn't work once I disable the bottom scale widget, so my current method clearly isn't going to work.
Calculating the correct x coordinate (in plotContainer coordinates) for where the standalone QwtScaleWidget needs to be placed to be aligned with the plots. This seems to have something to do with the string length of the leftmost tick label.
Locking the x position of the standalone QwtScaleWidget to the position calculated in the previous step. Right now, the QSpacerItem that is between the "Add Plot.." QPushButton and the QwtScaleWidget seems to just be doing whatever it wants, so the scale widget doesn't line up where I need it to. Maybe once I've calculated the correct x-axis coordinate in step 3 (in plotContainer coordinates), I need to subtract off the QPushButton width, plus the various internal margins/spacings, and then set that QSpacertItem to have a fixed width?
When is the correct time to set the width and position? Right now, the plots are in layouts that allow the plots to take up as much space as they can whenever the main window resizes. I want to keep that functionality intact, but I noticed that as I explicitly resize the standalone scale widget, which I'm triggering off from the plot(s) resizeEvent(), it sometimes causes a ripple effect where the plotContainer widget tries to grow wider, which then causes another plot resize, which can get me in an infinite loop

Cah
23rd August 2014, 10:14
If I recall correctly, you have a vertical layout..
The top row is the scale widget by itself and the following rows are QwtPlots (widgets)
Now QwtPlot is a composite... Thus, I would do the following...

Use a grid layout..
The scalewiget at the top would be in row 0, column 1
and each QwtPlot should spread across column 0 and column 1 (Force the plot to occupy both columns)

Added after 49 minutes:

Did you try...
QLayout::setAlignment ( QWidget * w, Qt::Alignment alignment )

Added after 17 minutes:

Correction to earlier post...

If you are going with the grid layout..., you'll need 3 columns

row 0, column 0 -- dummy widget with width==left scaleWidget+ spacing
and height==standAloneScaleWidget + spacing

row 0, column 1 -- standAloneScaleWidget + spacing

row 0, column 2 -- dummy widget with width==right scaleWidget+ spacing
and height==standAloneScaleWidget + spacing

rows 1, 2, ...n QwtPlot widgets spread accross 3 columns.

Do... try QLayout::setAlignment ( QWidget * w, Qt::Alignment alignment ) first

Uwe
24th August 2014, 14:48
Before wasting too much time on doing it the hard way, I would have a look at the plotmatrix example. It offers a a class PlotMatrix that does most of the nasty layout code for you.

Uwe

SeanM
25th August 2014, 19:30
Thanks for both of your suggestions, but I either I'm missing how your suggestions will fit my specific case or I didn't explain my setup clearly enough.

Cah wrote:
If I recall correctly, you have a vertical layout..
The top row is the scale widget by itself and the following rows are QwtPlots (widgets)
Cah, as you pointed out, I do have the plots in a vertical layout, but you're incorrect that the first row contains the scale widget. My plots are in a vertical layout which itself is in a scroll area because the user could potentially add enough plots to exceed the vertical size of the mainwindow and/or physical monitor. Since the plots are already aligned vertically, I want to have a single x-axis scale widget OUTSIDE the scroll area so that it is always visible, regardless of vertical scroll position. If I can do that, then I could hide the individual x-axis scale widgets on each plot, which would then provide a little more vertical resolution for the plots themselves. But obviously the scale widget ticks need to line up with where the scale widgets would be if they were enabled, otherwise it looks bad. Also even if I didn't have the scroll area, simply letting a grid layout line up a scalewidget with a plot canvas inside the same grid layout column DOESN'T actually line up the tick marks with their respective plots. Inside qwt_plot_layout.cpp class Uwe has this comment:


// +---+-----------+---+
// | <- Axis -> |
// +-^-+-----------+-^-+
// | | | | | |
// | | | |
// | A | | A |
// | x | Canvas | x |
// | i | | i |
// | s | | s |
// | | | |
// | | | | | |
// +-V-+-----------+-V-+
// | <- Axis -> |
// +---+-----------+---+

// The ticks of the axes - not the labels above - should
// be aligned to the canvas. So we try to use the empty
// corners to extend the axes, so that the label texts
// left/right of the min/max ticks are moved into them.
So you can see where the xBottom axis WIDGET can actually be positioned to the left of the plot canvas widget (which happens based on the number of characters in the tick labels), so that the plot ticks line up on with the plot.

Uwe wrote:
have a look at the plotmatrix example
Uwe, I'm not sure the plotmatrix example helps me for the same reason? I think the plotmatrix example would work great for getting all my plots lined up inside the scroll area, but I don't see how it helps me get that standalone scale widget, which is parented by a different widget and is OUTSIDE the scroll area, lined up with the plots themselves? Or did I misunderstand what part of the plot matrix example you wanted me to use?

Uwe
26th August 2014, 09:40
Uwe, I'm not sure the plotmatrix example helps me for the same reason? I think the plotmatrix example would work great for getting all my plots lined up inside the scroll area, but I don't see how it helps me get that standalone scale widget, which is parented by a different widget and is OUTSIDE the scroll area, lined up with the plots themselves? Or did I misunderstand what part of the plot matrix example you wanted me to use?
Well at least ( according to your screenshot ) you need the code that finds/sets the maximum extent of the vertical axes to align the canvases.
Assuming that your scale widget is vertically aligned to the plots you also need this extent to calculate the alignment of the scales inside of your extra scale widget ( see QwtScaleWidget::setBorderDist() ).

Uwe

SeanM
26th August 2014, 22:58
I'm still working on applying your last suggestion Uwe, but in the meantime I created a minimal example that replicates where I am currently.

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <qwt_scale_widget.h>
#include <qwt_plot.h>
#include <QVBoxLayout>
#include <QScrollArea>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

public slots:
void addPlot();

private:
Ui::MainWindow *ui;
QwtScaleWidget* scaleWidget; // standalone scale widget
QVBoxLayout* scrollVBox; // vboxlayout inside scroll area to align plots
QScrollArea* scroll;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <qwt_scale_engine.h>
#include <qwt_date_scale_draw.h>
#include <qwt_scale_widget.h>
#include <qwt_plot_grid.h>
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
#include <QScrollArea>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);

// create horizontal layout above scroll area, with:
// QPushButton QwtScaleWidget QLabel
QPushButton* pbAddPlot = new QPushButton("Add Plot", this);
connect(pbAddPlot, SIGNAL(clicked()),
this, SLOT(addPlot()));
scaleWidget = new QwtScaleWidget(this);
scaleWidget->setAutoFillBackground(true);
scaleWidget->setBorderDist(20,20);
scaleWidget->setAlignment(QwtScaleDraw::BottomScale);
QwtDateScaleDraw* dateScale = new QwtDateScaleDraw();
dateScale->setDateFormat(QwtDate::Millisecond, "ss.zzz");
dateScale->setDateFormat(QwtDate::Second, "mm:ss.zzz");
dateScale->setDateFormat(QwtDate::Minute, "hh:mm:ss.zzz");
dateScale->setDateFormat(QwtDate::Hour, "hh:mm:ss.zzz");
scaleWidget->setScaleDraw(dateScale);
QPalette pal = scaleWidget->palette();
pal.setColor(QPalette::Window, QColor(0,255,255));
scaleWidget->setPalette(pal);
QHBoxLayout* hbox = new QHBoxLayout();
hbox->addWidget(pbAddPlot);
hbox->addWidget(scaleWidget);
hbox->addWidget(new QLabel("Text Label", this));

// create the scroll area to hold the vertically aligned plots
scroll = new QScrollArea(this);
scroll->setWidgetResizable(true);
scroll->setSizeAdjustPolicy(QScrollArea::AdjustToContents) ;
scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOf f);
QWidget* widget = new QWidget(this);
scroll->setWidget(widget);
scrollVBox = new QVBoxLayout(widget);
widget->setLayout(scrollVBox);

QVBoxLayout* vbox = new QVBoxLayout();
vbox->addLayout(hbox);
vbox->addWidget(scroll);

centralWidget()->setLayout(vbox);
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::addPlot()
{
QPalette pal;
QwtPlot* plot;
plot = new QwtPlot(scroll);
plot->setMinimumHeight(250);
plot->enableAxis(QwtPlot::yRight,true);
//plot->enableAxis(QwtPlot::xBottom, false); // <- this needs to be enabled in the real code
plot->setAutoFillBackground(true);
pal = plot->palette();
pal.setColor(QPalette::Window, QColor(255,255,0));
plot->setPalette(pal);
plot->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);

QwtPlotGrid* grid = new QwtPlotGrid();
grid->attach(plot);
QwtScaleWidget* bottomScale = plot->axisWidget(QwtPlot::xBottom);

QwtDateScaleDraw* dateScale2 = new QwtDateScaleDraw();
dateScale2->setDateFormat(QwtDate::Millisecond, "ss.zzz");
dateScale2->setDateFormat(QwtDate::Second, "mm:ss.zzz");
dateScale2->setDateFormat(QwtDate::Minute, "hh:mm:ss.zzz");
dateScale2->setDateFormat(QwtDate::Hour, "hh:mm:ss.zzz");
bottomScale->setScaleDraw(dateScale2);
bottomScale->setAutoFillBackground(true);
pal = bottomScale->palette();
pal.setColor(QPalette::Window, QColor(0,255,255));
bottomScale->setPalette(pal);
bottomScale->repaint();

QwtScaleWidget* leftScale = plot->axisWidget(QwtPlot::yLeft);
QwtScaleWidget* rightScale = plot->axisWidget(QwtPlot::yRight);

leftScale->setAutoFillBackground(true);
rightScale->setAutoFillBackground(true);
pal = leftScale->palette();
pal.setColor(QPalette::Window, QColor(0,255,0));
leftScale->setPalette(pal);
leftScale->repaint();
rightScale->setPalette(pal);
rightScale->repaint();

QHBoxLayout* hbox = new QHBoxLayout();
QLabel* label;
label = new QLabel("Custom Legend\nLeft", scroll);
label->setAutoFillBackground(true);
label->setAlignment(Qt::AlignCenter);
label->setFixedWidth(100);
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
hbox->addWidget(label,0);
hbox->addWidget(plot,10);
label = new QLabel("Custom Legend\nRight", scroll);
label->setAutoFillBackground(true);
label->setAlignment(Qt::AlignCenter);
label->setFixedWidth(100);
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
hbox->addWidget(label,0);
QWidget* widget = new QWidget(scroll->widget());
widget->setLayout(hbox);
scrollVBox->addWidget(widget);
}

Here's what the screen looks like after you add a couple plots (via the Add Plot button) and resize and scroll it a little:
10585
And then I again marked it up a little to show what I need to accomplish:
10586