QCustomPlot Discussion and Comments

Is it possible to click and drag to position a QCP legend?Return to overview

Is it possible to click and drag to position a QCP legend?

Thanks for any help!

To clarify my question - I know how to use the selection-related signals from a QCPLegend and react accordingly, but there aren't any other signals (according to the documentation at http://www.qcustomplot.com/documentation/classQCPLegend.html).

Perhaps it is my limited experience with QT in general, but I don't know how to capture the information from a click-and-drag event and then call QCPLegend::setOuterRect(...) based on that new information.

Thanks!

I have exactly the same problem. I tried to use QCPLegend::setOuterRect(...), but it does not work. What is interesting though that the plot knows about the new legend position as the shape of the mouse cursor changes to a hand when I move it over the region the legend should occupy after the move is finished and changes back to a cross over the original position of the legend. But still the legend remains visible at the same position and clicking on it again sets the cursor areas back.

To be fair the documentation states that (emphasize by me)
void QCPLayoutElement::setOuterRect ( const QRect & rect)
Sets the outer rect of this layout element. If the layout element is inside a layout, the layout sets the position and size of this layout element using this function.

Calling this function externally has no effect, since the layout will overwrite any changes to the outer rect upon the next replot.

The layout element will adapt its inner rect by applying the margins inward to the outer rect.
This is my code:

h: file:

My_Main : public QMainWindow
{
 Q_OBJECT
public:
...
private:
...
	bool bLegendSelected;		// for moving the legend with the mouse
	QPoint qpLegendOrigin;		// mouse press position inside legend
...
}

CPP file:

void My_Main::mousePress(QMouseEvent* mevent)
{
if (mevent->button() == Qt::LeftButton && ui.graph->legend->outerRect().contains(mevent->pos()))
	{
		bLegendSelected = true;
		qpLegendOrigin = mevent->pos();
	} 
}

void My_Main::mouseMove(QMouseEvent *mevent)
{
	if (bLegendSelected)	// then move legend
	{
		QRect r = ui.graph->legend->outerRect();
		QPoint qp = mevent->pos() -qpLegendOrigin;
		r.translate(qp);
		qpLegendOrigin = mevent->pos();

		ui.graph->legend->setOuterRect(r);
		return;
	}
}

void My_Main::mouseRelease(QMouseEvent *mevent)
{
	bLegendSelected = false;
}

Try something like this:

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "qcustomplot.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
  Q_OBJECT
  
public:
  explicit MainWindow(QWidget *parent = 0);
  ~MainWindow();
  
private:
  Ui::MainWindow *ui;
  QCustomPlot *plot;
  bool draggingLegend;
  QPointF dragLegendOrigin;
  
private slots:
  void mouseMoveSignal(QMouseEvent *event);
  void mousePressSignal(QMouseEvent *event);
  void mouseReleaseSignal(QMouseEvent *event);
  void beforeReplot();
};

#endif // MAINWINDOW_H


mainwindow.cpp:

#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);
  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 = plot->axisRect()->insetLayout()->insetRect(0);
    // 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;
    // 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)
  draggingLegend = false;
}

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,

In my case I had to modify the function beforeReplot() to resize the legend correctly.

void MainWindow::beforeReplot()
{
    plot->legend->setMaximumSize(plot->legend->minimumOuterSizeHint());
}

Sylm,

Hi,
confirmed.
The code from DerManu and with the correction made by Sylm is working very well.
Thank you very much.