0
votes

I'm trying to create a canvas to draw on with the mouse similar to most digital painting applications that I can also zoom in on (zoom in on the drawn image)

So far I've created a class that uses QWidget and added it to the ui then use the mouse events and QPaintEvent to draw on this widget which works. However the problem I'm not sure how do I zoom on this as well? I tried placing the QWidget inside of a scrollable area but it stops it from registering click events. I also tried extending from QGraphicsViewer instead of QWidget but this stops me from being able to paint as well.

//Class definition
PaintArea::PaintArea(QWidget *parent) : QWidget(parent)
{
    this->setMouseTracking(true);
}

I'm mostly looking for a recommendation of how to scrolling and drawing with a mouse on the same widget (Possibly with scroll bar but just wheel scrolling for sure) Thanks

1
QWidget has virtual mouseMoveEvent(), mousePressEvent(), mouseReleaseEvent() and wheelEvent(). So every widgets that inherits QWidget (including QScrollArea) will have the same. I think that overriding these methods for your custom widget should solve your problem.Fareanor

1 Answers

1
votes

If you follow the QWidget way, you may want to look carefully to the scribble example that is included with Qt docs. In this example, the drawing is made off-screen on a QImage object, which is then painted by the widget. The problem is to zoom the image.

I prefer your second way: QGraphicsView has a scale() function among many other excellent features. You may do something similar to the scribble example: draw off-screen on a QPixmap image which is set (every time you change the image) into a QGraphicsPixmapItem which belongs to the QGraphicsScene. I've implemented this crude example, borrowing some elements from the scribble example. Use the mouse wheel to zoom the image (it screws the scrolling a bit, sorry).

test.pro

QT += core gui widgets
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
    drawablescene.cpp \
    main.cpp \
    mainwindow.cpp
HEADERS += \
    drawablescene.h \
    mainwindow.h

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "drawablescene.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    void wheelEvent(QWheelEvent *event) override;

private:
    QGraphicsView *m_view;
    DrawableScene *m_scene;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include <QGraphicsView>
#include <QWheelEvent>
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      m_view(new QGraphicsView(this)),
      m_scene(new DrawableScene(this))
{
    setCentralWidget(m_view);
    m_scene->setSceneRect(0,0,640,480);
    m_view->setScene(m_scene);
}

void MainWindow::wheelEvent(QWheelEvent *event)
{
    qreal delta = 1 + (event->delta() > 0 ? 0.1 : -0.1);
    m_view->scale(delta, delta);
    event->accept();
}

drawablescene.h

#ifndef DRAWABLESCENE_H
#define DRAWABLESCENE_H

#include <QObject>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>

class DrawableScene : public QGraphicsScene
{
public:
    explicit DrawableScene(QObject *parent = nullptr);

private:
    void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
    void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
    void drawLineTo(const QPointF &endPoint);

    bool m_modified;
    bool m_scribbling;
    int m_penWidth;
    QColor m_penColor;
    QPointF m_lastPoint;
    QPixmap *m_image;
    QGraphicsPixmapItem *m_item;
};

#endif // DRAWABLESCENE_H

drawablescene.cpp

#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include "drawablescene.h"

DrawableScene::DrawableScene(QObject *parent)
    : QGraphicsScene(parent),
      m_modified(false),
      m_scribbling(false),
      m_penWidth(3),
      m_penColor(Qt::blue)
{
    m_image = new QPixmap(640, 480);
    m_image->fill(Qt::white);
    m_item = addPixmap(*m_image);
}

void DrawableScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if ((event->buttons() & Qt::LeftButton) && m_scribbling) {
        drawLineTo(event->scenePos());
        event->accept();
    }
    else QGraphicsScene::mouseMoveEvent(event);
}

void DrawableScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_lastPoint = event->scenePos();
        m_scribbling = true;
        event->accept();
    }
    else QGraphicsScene::mousePressEvent(event);
}

void DrawableScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton && m_scribbling) {
        drawLineTo(event->scenePos());
        m_scribbling = false;
        event->accept();
    }
    else QGraphicsScene::mouseReleaseEvent(event);
}

void DrawableScene::drawLineTo(const QPointF &endPoint)
{
    QPainter painter(m_image);
    painter.setPen(QPen(m_penColor, m_penWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
    painter.drawLine(m_lastPoint, endPoint);
    m_modified = true;
    m_lastPoint = endPoint;
    m_item->setPixmap(*m_image);
}