QCustomPlot Discussion and Comments

Using QPainter to drawLine in QCustomPlotReturn to overview

I'm trying to copy the scribble demo onto a QCustomPlot app. I inherit from QCustomPlot, override mousePressEvent, moveEvent, etc and paintEvent.
In my implementation of myclass:paintEvent, I use the QPainter class to draw the scribble as I move the mouse.
This works great, however, because i've overridden paintEvent, I call QCustomPlot::paintEvent which removes the newly drawn scribble. If I don't call QCustomPlot::paintEvent from my overridden method, most of the layers are not drawn.
Looking at the QCustomPlot::paintEvent code, the line that clears out the scribble is painter.fillRect(mViewport, mBackgroundBrush);.

I'm obviously missing something basic, and therefore hoping someone might be able to help. I've tried plenty of workarounds, but to noavail.

Thanks and appreciated.

Have you tried drawing your scribble _after_ calling the base class implementation?

call QCustomPlot::paintEvent first , then draw what you want :)

Its not that obvious unfortunately ... base class paintEvent will be called for any repaints, albeit mouse move or any new updates to the app or even menu selection.
When the method is called my scribble disappears. It seems i have to somehow register my scribble with the CustomPlot viewPort for it to be repainted, similar to QCPAbstractItems.
I've even introduced my own QCPLayers to paint on - moved them to top of the pile etc etc.

I'm sure someone has solved this problem, and it seems i'm missing something very basic.

Thanks!!

But since ::paintEvent is a virtual function it should actually always call your subclass paint event instead. Very strange. Did you double check that you've inherited correctly? Maybe add debug statements to the base- and subclass paint events to check if hat's really what's going wrong here.

Isso, yes it does, but I have to call QCustomPlot::paintEvent from my overloaded method - apart from this being good C++ practice (not that I'm lecturing), QCustomPlot::paintEvent has to be called to render the Plot, Axis, QCPItems, etc etc.
Yeah, this is a weird one - i can pretty much do anything with this extremely rich and powerful API, but I'm totally lost on this.

I was interested so I tested it with this simple code, and it works just fine. I think there is something other going wrong in your code logic.

header:

class QCustomPlotReimpl : public QCustomPlot
{
  Q_OBJECT
  
public:
  QCustomPlotReimpl(QWidget *parent = nullptr) : QCustomPlot(parent) {}
  ~QCustomPlotReimpl() {}
  
protected:
  void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
};

implementation:

void QCustomPlotReimpl::paintEvent(QPaintEvent *event)
{
  QCustomPlot::paintEvent(event);
  
  QPainter painter(this);
  painter.setPen(QPen(Qt::black));
  painter.drawLine(100, 100, 200, 200);
}

Hi Isso, many thanks for looking into it ... I will strip naked my implementation and go with the bare min, then build from there.
Just to make sure we are on th same page, was your implementation above triggered by left mouse key press?
In your implementation, ::paintEvent will be called for all CustomPlot repaints and inadvertently repaint the line each time and therefore gives the impression the line is still there. If the start and end points are different, I wonder if it still works.
I'll give it a go and revert.
Much appreciate your help and input.

Down to bare bones impl, and unfortunately the behaviour is the same. Maybe someone can have better luck with the very simple code below or point me to my error - thanks:

class MYCustomPlot : public QCustomPlot
{
public:
        MYCustomPlot(QWidget* w) : QCustomPlot(w), m_leftMove(false){};

        virtual ~MYCustomPlot(void){};

        void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE
        {
                if (m_leftMove)
                {
                        QPainter painter(this);

                        painter.setPen(QPen(QColor("RED"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
                        painter.drawLine(m_startPoint, m_endPoint);
                        painter.end();

                        m_startPoint = m_endPoint;
                }
                else
                        QCustomPlot::paintEvent(event);
        }

        void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE
        {
                if (event->buttons() & Qt::LeftButton)
                {
                        m_startPoint = event->pos();
                }

                QCustomPlot::mousePressEvent(event);
        }

        void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE
        {
                m_leftMove = false;

                QCustomPlot::mouseReleaseEvent(event);
        }

        void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE
        {
                if ((event->buttons() & Qt::LeftButton))
                {
                        m_leftMove = true;
                        m_endPoint = event->pos();

                        update();
                }

                QCustomPlot::mouseMoveEvent(event);
        }

private:

        bool m_leftMove;

        QPoint m_endPoint;
        QPoint m_startPoint;
};

#include <unistd.h>

#include <KPQtwindow.h>

KPQtwindow::KPQtwindow(QWidget *parent) : QMainWindow(parent)
{
        setObjectName(QStringLiteral("MainWindow"));
        setWindowTitle("KP QT TEST");
        resize(1700, 1000);

        QWidget *centralWidget = new QWidget;
        m_customPlot = new MYCustomPlot(centralWidget);
        QVBoxLayout *verticalLayout = new QVBoxLayout(centralWidget);

        setCentralWidget(centralWidget);
        verticalLayout->addWidget(m_customPlot);
}

KPQtwindow::~KPQtwindow(void)
{
}

int main(int argc, char **argv)
{
        QApplication a(argc, argv);
        KPQtwindow kp;
        kp.show();
        return a.exec();
}

Is there a reason you only call the base class paintEvent when the m_leftMove flag unset? From my understanding it should always be called, otherwise you'll have frames where no plot is drawn (particularly when the QWidget clears its own double buffer depending on Qt::WA_OpaquePaintEvent and QWidget::setAutoFillBackground.

Also, I think you should call update() one last time in the releaseEvent, right? Not sure about that though.

I think I now understand what you want to make. You want to draw many lines and keep them from one paint event to the next, right? The scribble demo is a bad example for how to achieve that because it uses the widget double buffer without explaining the implications. If you try to do that with anything else dynamic in the background, also that will be retained from one frame to the next and create an unholy mess. You can probably try that by setting QCustomPlot background to transparent.
Much better use your own buffer (QPixmap), draw onto that, and in the paint event draw the QCustomPlot by calling its base class paint event and then draw your pixmap. This way you have separated the two domains and they can be changed/Drawn independently without issues.

Hi Isso,
I like the idea of using different buffers, and I modified my simple impl adding QCPItemPixmap and QPixmap. It worked well, but had a couple of side effects:
1) The grid disappeared, unless I created a QCPLayer below the 'grid' layer. This then rendered my line below CandleSticks (which I can live with)
2) The drawing did not match exactly the mouse movements .... seem to be out by some factor ... ran some debug and diagnostics, but did not get far.

I agree with you, this is a much better and neater impl, just need to get to the bottom of the above issues .. 2 especially.

Many thanks and appreciate your time in dealing with this.

Make sure you don't do both. So either use QCPItemPixmap (and let QCustomPlot handle its rendering and memory management entirely), or use a QPixmap and be responsible for the rendering yourself in the paintEvent.

Hi Isso,

Not sure what you mean, but I reverted to simply using QCPItemLine - i think will is very expensive in memory and rendering.
Could you possibly send me a couple of lines of QCPItemPixmap impl that would do the same? Looks like I need to understand this more, as I can't see how to use the class without QPixmap.

Thanks much.