MindRoller
MindRoller

Reputation: 241

How to draw when holding down the mouse button?

I am trying to create a simple painting application - it is just supposed to draw when you click and drag a cursor (just like Paint). I know I have to use QPainter but how can I handle it? How to do it? Any help would be really appreciated. I tried lurking through internet but didn't find too much info (I daw drawing lines etc. by code that you launch an app and it is here but I can not find an example of drawing something by user).

Upvotes: 0

Views: 3031

Answers (2)

Jeremy Friesner
Jeremy Friesner

Reputation: 73091

It's a pretty broad question, but here are the basics:

  1. Make a subclass of the QWidget class, so that you can override some of its virtual methods later on.
  2. Create an object of your subclass and call show() on it (just before calling QApplication::exec()). This object will appear on screen as a very simple window, and it will serve as your user's painting-surface.
  3. Create a QPixmap object that you will use to store the bitmap that the user will draw. Make sure to size the QPixmap to be at least as big as the maximum size of the window that you want to support. Call fill() on the QPixmap to fill it with your favorite background color.
  4. Override the mousePressEvent(QMouseEvent *) method of your object to set a boolean is_mouse_down flag, and also to record the current position of the mouse pointer within the window (by calling pos() on the QMouseEvent object that gets passed in to the mousePressEvent() call and storing that into a member variable of your object).
  5. Override the mouseMoveEvent(QMouseEvent *) method so that if is_mouse_down_is set to true, it creates a QPainter object on the stack -- pass a pointer to the QPixmap to the QPainter object's constructor so that the QPainter will draw into your QPixmap object. Then call drawLine() on the QPainter object to draw a line from the previous mouse position to your current one. Finally, call update() to tell Qt to call paintEvent() for you ASAP.
  6. Override the mouseReleaseEvent(QMouseEvent *) method to set is_mouse_down to false again
  7. Override the paintEvent(QPaintEvent *) method to create a QPainter object on the stack -- pass a pointer to (this) to the QPainter object's constructor, so that it will paint onto the QWidget directly. Then call drawPixmap() on the QPainter object so that it will draw your QPixmap object onto the widget's visible surface.

If you'd like to see a pre-written example, check out the Scribble application included with Qt, in $QTDIR/examples/widgets/widgets/scribble.

Upvotes: 2

Here's Jeremy's answer in code instead of prose:

// https://github.com/KubaO/stackoverflown/tree/master/questions/simplepaint-39358392
#include <QtWidgets>

// Make a subclass of the QWidget class, so that you can override some of its 
// virtual methods
class PaintWidget : public QWidget {
    // Create a QPixmap object that you will use to store the bitmap 
    // that the user will draw [on].
    QPixmap m_pixmap;
    QPoint m_lastPos;
    // Override the paintEvent(QPaintEvent *) [...]
    void paintEvent(QPaintEvent *) override {
        QPainter painter{this};
        painter.drawPixmap(0, 0, m_pixmap);
    }
    void resizeEvent(QResizeEvent *) override {
        // [...] size the QPixmap to be at least as big as the maximum size of the window
        // We'll also never let it shrink so as not to lose the already drawn image.
        auto newRect = m_pixmap.rect().united(rect());
        if (newRect == m_pixmap.rect()) return;
        QPixmap newPixmap{newRect.size()};
        QPainter painter{&newPixmap};
        painter.fillRect(newPixmap.rect(), Qt::white);
        painter.drawPixmap(0, 0, m_pixmap);
        m_pixmap = newPixmap;
    }
    // Override the mousePressEvent(QMouseEvent *) [...]
    void mousePressEvent(QMouseEvent * ev) override {
        m_lastPos = ev->pos();
        draw(ev->pos());
    }
    // Override the mouseMoveEvent(QMouseEvent *) [...]
    void mouseMoveEvent(QMouseEvent * ev) override {
        draw(ev->pos());
    }
    void draw(const QPoint & pos) {
        QPainter painter{&m_pixmap};
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setPen({Qt::blue, 2.0});
        painter.drawLine(m_lastPos, pos);
        m_lastPos = pos;
        update();
    }
public:
    using QWidget::QWidget;
};

int main(int argc, char ** argv) {
    QApplication app{argc, argv};
    // Create an object of your subclass and call show()
    PaintWidget ui;
    ui.show();
    return app.exec();
}

It is not necessary to override the mouseReleaseEvent. In a widget, the default behavior is to track the mouse movement only when a mouse button is depressed. The mouseMoveEvent won't be called unless a button is depressed.

Upvotes: 5

Related Questions