QCustomPlot Discussion and Comments

Position of legend does not update after setInsetAlignment?Return to overview

Following an old example about drag and drop legend (https://www.qcustomplot.com/index.php/support/forum/481), to allow user set legend to predefined location and drag/drop legend, I move setInsetPlacement(0, QCPLayoutInset::ipFree); into mouse press slot as shown below. However there is a problem that when user set legend to predefined location by calling setInsetAlignment(0, Qt::AlignLeft|Qt::AlignTop); and etc, the legend location changed but plot->axisRect()->insetLayout()->insetRect(0); does not update to the new location. This causes offset when trying to move the legend.

Is it true that setInsetAlignment does not actually change the insetRect? How can I work around the problem? Thanks.

#include "mainwindow.h"
#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent) :
  QMainWindow(parent),
  ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  plot = new QCustomPlot(this);
  plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
  setCentralWidget(plot);
   
  plot->addGraph();
  plot->addGraph();
  plot->legend->setVisible(true);
  // set the placement of the legend (index 0 in the axis rect's inset layout) to not be 
  // border-aligned (default), but freely, so we can reposition it anywhere:

  //plot->axisRect()->insetLayout()->setInsetPlacement(0, QCPLayoutInset::ipFree);
  [b]plot->axisRect()->insetLayout()->setInsetPlacement(0, QCPLayoutInset::ipBorderAligned);[/b]
  [b]plot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignLeft|Qt::AlignTop);[/b]
  draggingLegend = false;
   
  connect(plot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoveSignal(QMouseEvent*)));
  connect(plot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePressSignal(QMouseEvent*)));
  connect(plot, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleaseSignal(QMouseEvent*)));
  connect(plot, SIGNAL(beforeReplot()), this, SLOT(beforeReplot()));
}
 
MainWindow::~MainWindow()
{
  delete ui;
}
 
void MainWindow::mouseMoveSignal(QMouseEvent *event)
{
  if (draggingLegend)
  {
    QRectF rect = [b]plot->axisRect()->insetLayout()->insetRect(0);[/b]
    // since insetRect is in axisRect coordinates (0..1), we transform the mouse position:
    QPointF mousePoint((event->pos().x()-plot->axisRect()->left())/(double)plot->axisRect()->width(),
                       (event->pos().y()-plot->axisRect()->top())/(double)plot->axisRect()->height());
    rect.moveTopLeft(mousePoint-dragLegendOrigin);
    plot->axisRect()->insetLayout()->setInsetRect(0, rect);
    plot->replot();
  }
   
}
 
void MainWindow::mousePressSignal(QMouseEvent *event)
{
  if (plot->legend->selectTest(event->pos(), false) > 0)
  {
    draggingLegend = true;
    [b]plot->axisRect()->insetLayout()->setInsetPlacement(0, QCPLayoutInset::ipFree);[/b]
    // since insetRect is in axisRect coordinates (0..1), we transform the mouse position:
    QPointF mousePoint((event->pos().x()-plot->axisRect()->left())/(double)plot->axisRect()->width(),
                       (event->pos().y()-plot->axisRect()->top())/(double)plot->axisRect()->height());
    dragLegendOrigin = mousePoint-plot->axisRect()->insetLayout()->insetRect(0).topLeft();
  }
}
 
void MainWindow::mouseReleaseSignal(QMouseEvent *event)
{
  Q_UNUSED(event)
[b]  if(draggingLegend){
      plot->axisRect()->insetLayout()->setInsetPlacement(0, QCPLayoutInset::ipBorderAligned);
      draggingLegend = false;
  }[/b]
  
  
}
 
void MainWindow::beforeReplot()
{
  // this is to prevent the legend from stretching if the plot is stretched.
  // Since we've set the inset placement to be ipFree, the width/height of the legend
  // is also defined in axisRect coordinates (0..1) and thus would stretch.
  // This is due to change in a future release (probably QCP 2.0) since it's basically a design mistake.
  plot->legend->setMaximumSize(plot->legend->minimumSizeHint());
}

Hi Paul,

the QCPLayoutInset has two placement modes, which are quite strongly separated. The ipFree and the ipBorderAligned mode.

In ipFree mode, you can set the inset top/left/width/height freely using setInsetPlacement.

However, if you switch to ipBorderAligned, it completely ignores any previous configuration in ipFree, and aligns the inset according to the alignment configured with setInsetAlignment. In this mode, the gap between the border and the legend (or whichever layout element is placed in the inset layout) is defined by the margin of that layout element.

If I understand your intention correctly, I think staying in mode ipFree all the time should do what you want, right? In this way you can move the legend and keep it there, and it stretches/compresses together with the outer axis rect (since in ipFree, the inset top/left/width/height are given in fractions of the axis rect width/height).

The improvement on the placement that was described in the thread you mentioned was delayed to QCP3.0 unfortunately. But there it definitely will be in place (there you can set the respective corner point in ipBorderAligned mode, thus never need ipFree for legends).

Thanks a lot. I think you meant "(there you can set the respective corner point in ipFree mode, thus never need ipBorderAligned for legends)."?

When will QCP3.0 be available?

Yes, ipFree mode is what I want, and is it possible to achieve this with QCP2.0 by creating my own functions that move legend to corner point? Is it complicated to calculate the corner points? The drawback is that when new graph with longer name is added to legend, the corner points may change. With QCP2.0, that means an manual legend update after every addgraph to place legend at corner...