QCustomPlot Discussion and Comments

axes with equal spacing while displayingReturn to overview

Hello everyone!

Is there a way to autoscale X and Y axes to obtain equal axis spacing,
so that a circle is shown as a circle and not as an ellipse (depending on widget
aspect ratio) on screen?

Thanks in advance for the help!

See QCPAxis::setScaleRatio.

I've tried using setScaleRatio. I've found that the axis scales are not equal even when the aspect ratio is set to 1.

customplot->yAxis->setScaleRatio(ui->sectionPlot->xAxis,1.0);

In fact, 1 grid on the y axis might be 60 while a single grid on the x axis is 4... They should be the same if this function performs as expected.

I would love to see if this has been resolved by someone. Better yet, a complete example of setScaleRatio would be great (with an overload of resizeEvent).

I think it works like that:

_QCustomPlot.yAxis->setScaleRatio(_QCustomPlot.xAxis, _QCustomPlot.size().width() / _QCustomPlot.size().height() );

I think this is not perfect, because other items in the plot may change the aspect ration of the actual dispalying area. For instance, the size of the axes on the sides may be larger than the axes on top/bottom. For me it is good enough though.

I have had trouble with this as well, using QCPColorMap with QCPColorMap->data()->setCell( int keyIndex, int valueIndex, double z )

Using QCPAxis::setScaleRatio resulted in one axis being encompassed completely by one cell.

I tried using double QCPAxis::coordToPixel ( double value) const to transform the cell coordinates to pixels in order to measure the cell pixel widths, then iteratively use QWidget::setGeometry(...) to update the sizes until I had the same number of pixels per cell on the vertical and horizontal axes. That did not work because the pixel widths always stayed the same no matter what geometry I set (I could have done this wrong). I also could not use the scaling functionality of the axis b/c I needed to maintain strict bounds.

I noticed that what Zweistein said was true: "other items in the plot may change the aspect ration of the actual dispalying area."

My not so elegant but simple solution is to rescale the window to the same resolution that the window had before adding other items to the plot but after adding the data:

QCustomPlot customPlot;
QCPColorMap *colorMap = new QCPColorMap(customPlot.xAxis,customPlot.yAxis);
customPlot.addPlottable(colorMap);
//Add data using QCPColorMapData::setData(...)
//Get size of the axis rect after plotting data and rescaling
int preW = customPlot.axisRect()->width();
int preH = customPlot.axisRect()->height();
//Rescale to the desired bounds
//Add axis labels, tick vectors, tick vector labels, title, other items
int targetWidth = 800; //desired width of entire plot window, pixels
int postW = customPlot.axisRect()->width();
int postH = customPlot.axisRect()->height();
//rescale using ratio of window before adding other items to the plot
customPlot.setGeometry(targetWidth ,(1.0*targetWidth *preH/preW)); 

Maybe there's a misunderstanding, but I can't reproduce how setScaleRatio isn't doing what it's supposed to do.

The following code

  // setup color map to easier visualize the aspect ratio of the x-y-coordinate plane:
  QCPColorMap *colorMap = new QCPColorMap(mPlot->xAxis, mPlot->yAxis);
  mPlot->addPlottable(colorMap);
  colorMap->data()->setRange(QCPRange(-0.3, 0.3), QCPRange(-0.3, 0.3));
  colorMap->data()->setSize(2, 2);
  colorMap->data()->setCell(0, 0, 4);
  colorMap->data()->setCell(0, 1, 3);
  colorMap->data()->setCell(1, 0, 2);
  colorMap->data()->setCell(1, 1, 1);
  colorMap->rescaleDataRange();
  colorMap->setInterpolate(false);
 
  // setup axes:
  mPlot->xAxis->setRange(-2.5, 2.5);
  mPlot->yAxis->setRange(-2.5, 2.5);
  // let setScaleRatio change yAxis range:
  mPlot->yAxis->setScaleRatio(mPlot->xAxis, 1.0);
  mPlot->replot();
  mPlot->savePng("./forum-scaleratio1.png");

generates this image:
http://www.qcustomplot.com/images/forum/forum-scaleratio1.png
- As expected, the x axis range is still -2.5..2.5
- The setScaleRatio correctly changed the yAxis range to something smaller, to make the coordinate plane appear in 1:1 aspect ratio, as can be seen from the square colormap.

Maybe some of the users of setScaleRatio can explain with this example, what else they have expected. Please note (as mentioned in the documentation of setScaleRatio), that setScaleRatio mustn't be called in the QCustomPlot's parent constructor due to the way Qt creates widgets.

I've used setScaleRatio often and found it to work.But note, I always use QCPCurve since this I've found this feature not to work well in graph.

    QVector<double> x,y; //set x,y data
    ui->qcp1->clearGraphs();
    ui->qcp1->clearPlottables();
    ui->qcp1->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignBottom|Qt::AlignRight);
    QCPCurve *mycurve = new QCPCurve(ui->qcp1->xAxis, ui->qcp1->yAxis);
    mycurve->setData(x,y)
    ui->qcp1->addPlottable(mycurve);
    ui->qcp1->rescaleAxes();
    ui->qcp1->yAxis->setScaleRatio(ui->qcp1->xAxis,1.0);
    ui->qcp1->replot();

When I draw geometry objects they appear as they ought to be - a circle as a circle and an ellipse as an ellipse, irrespective of the widget size on screen.
Hope this helps.

This feature is somewhat like MATLAB "set axis equal" or GNUPLOT "set size ratio -1".

Here is my approach that works well for me.

In the setupChart() function, the number of ticks on the x-axis is set to 10; the rangeChanged (const QCPRange&) signal is connected to the adjustTicks() slot. The XCustomPlot subclass emits a Resized(QSize) signal that is also connected to the adjustTicks() slot. So, whenever you zoom in/out, pan the plot, or resize the plot, the adjustTicks() slot is called. That slot modifies the ticks on the y-axis based on the value of the x-axis.

void MyDialog::setupChart() {//chart is an instance of XCustomPlot (see below)
	chart->xAxis->setAutoTickCount(10);
	connect(chart->xAxis, SIGNAL(rangeChanged (const QCPRange&)), this, SLOT(adjustTicks()));
	connect(chart->yAxis, SIGNAL(rangeChanged (const QCPRange&)), this, SLOT(adjustTicks()));
	connect(chart, SIGNAL(Resized(QSize)), this, SLOT(adjustTicks()));
}
void MyDialog::adjustTicks() {
	chart->xAxis->setScaleRatio(chart->yAxis,1.0);
	chart->yAxis->setAutoTickStep(false);
	chart->yAxis->setTickStep(chart->xAxis->tickStep());
	chart->replot();
}

Here is the the XCustomPlot class.

#include "QCustomPlot.h"
class XCustomPlot: public QCustomPlot
{
	Q_OBJECT
public:
	XCustomPlot(QWidget * parent = 0) : QCustomPlot(parent){};
protected:
	virtual void resizeEvent (QResizeEvent *event)
	{
		QCustomPlot::resizeEvent(event);
		QSize s = event->size();
		emit Resized(s);
	};
signals:
	void Resized(QSize s);
};

Correction.
chart->xAxis->setScaleRatio(chart->yAxis,1.0);
in my previous post should be changed to
chart->yAxis->setScaleRatio(chart->xAxis,1.0);

Hi DarkForce,
I'm having problems resizing qcutomplot.
I get stack overflow exception when I use your code. seems like graph elements are locked by some other thread.

setup function(){
bool res = connect(ui->qcpLab->xAxis, SIGNAL(rangeChanged(const QCPRange&)), this, SLOT(adjustTicks()));
	res = connect(ui->qcpLab->yAxis, SIGNAL(rangeChanged(const QCPRange&)), this, SLOT(adjustTicks()));
	res = connect(ui->qcpLab, SIGNAL(Resized(QSize)), this, SLOT(adjustTicks()));
}
void MainWindow::adjustTicks() {
	QSharedPointer<QCPAxisTickerFixed> fixedTicker(new QCPAxisTickerFixed);
	ui->qcpLab->xAxis->setScaleRatio(ui->qcpLab->yAxis, 1.0);
	ui->qcpLab->yAxis->setTicker(fixedTicker);
	ui->qcpLab->yAxis->ticker()->setTickCount(ui->qcpLab->xAxis->ticker()->tickCount());
	ui->qcpLab->replot();
}

ui->qcpLab is widget promoted to XCustomPlot

can you please tell me what the mistake is?