Scribble Example▲
We reimplement the mouse event handlers to implement drawing, the paint event handler to update the application and the resize event handler to optimize the application's appearance. In addition we reimplement the close event handler to intercept the close events before terminating the application.
The example also demonstrates how to use QPainter to draw an image in real time, as well as to repaint widgets.

With the Scribble application the users can draw an image. The File menu gives the users the possibility to open and edit an existing image file, save an image and exit the application. While drawing, the Options menu allows the users to choose the pen color and pen width, as well as clear the screen. In addition the Help menu provides the users with information about the Scribble example in particular, and about Qt in general.
The example consists of two classes:
-
ScribbleArea is a custom widget that displays a QImage and allows to the user to draw on it.
-
MainWindow provides a menu above the ScribbleArea.
We will start by reviewing the ScribbleArea class. Then we will review the MainWindow class, which uses ScribbleArea.
ScribbleArea Class Definition▲
class
ScribbleArea : public
QWidget
{
Q_OBJECT
public
:
ScribbleArea(QWidget *
parent =
nullptr
);
bool
openImage(const
QString &
amp;fileName);
bool
saveImage(const
QString &
amp;fileName, const
char
*
fileFormat);
void
setPenColor(const
QColor &
amp;newColor);
void
setPenWidth(int
newWidth);
bool
isModified() const
{
return
modified; }
QColor penColor() const
{
return
myPenColor; }
int
penWidth() const
{
return
myPenWidth; }
public
slots:
void
clearImage();
void
print();
protected
:
void
mousePressEvent(QMouseEvent *
event) override
;
void
mouseMoveEvent(QMouseEvent *
event) override
;
void
mouseReleaseEvent(QMouseEvent *
event) override
;
void
paintEvent(QPaintEvent *
event) override
;
void
resizeEvent(QResizeEvent *
event) override
;
private
:
void
drawLineTo(const
QPoint &
amp;endPoint);
void
resizeImage(QImage *
image, const
QSize &
amp;newSize);
bool
modified;
bool
scribbling;
int
myPenWidth;
QColor myPenColor;
QImage image;
QPoint lastPoint;
}
;
The ScribbleArea class inherits from QWidget. We reimplement the mousePressEvent(), mouseMoveEvent() and mouseReleaseEvent() functions to implement the drawing. We reimplement the paintEvent() function to update the scribble area, and the resizeEvent() function to ensure that the QImage on which we draw is at least as large as the widget at any time.
We need several public functions: openImage() loads an image from a file into the scribble area, allowing the user to edit the image; save() writes the currently displayed image to file; clearImage() slot clears the image displayed in the scribble area. We need the private drawLineTo() function to actually do the drawing, and resizeImage() to change the size of a QImage. The print() slot handles printing.
We also need the following private variables:
-
modified is true if there are unsaved changes to the image displayed in the scribble area.
-
scribbling is true while the user is pressing the left mouse button within the scribble area.
-
penWidth and penColor hold the currently set width and color for the pen used in the application.
-
image stores the image drawn by the user.
-
lastPoint holds the position of the cursor at the last mouse press or mouse move event.
ScribbleArea Class Implementation▲
ScribbleArea::
ScribbleArea(QWidget *
parent)
:
QWidget(parent)
{
setAttribute(Qt::
WA_StaticContents);
modified =
false
;
scribbling =
false
;
myPenWidth =
1
;
myPenColor =
Qt::
blue;
}
In the constructor, we set the Qt::WA_StaticContents attribute for the widget, indicating that the widget contents are rooted to the top-left corner and don't change when the widget is resized. Qt uses this attribute to optimize paint events on resizes. This is purely an optimization and should only be used for widgets whose contents are static and rooted to the top-left corner.
bool
ScribbleArea::
openImage(const
QString &
amp;fileName)
{
QImage loadedImage;
if
(!
loadedImage.load(fileName))
return
false
;
QSize newSize =
loadedImage.size().expandedTo(size());
resizeImage(&
amp;loadedImage, newSize);
image =
loadedImage;
modified =
false
;
update();
return
true
;
}
In the openImage() function, we load the given image. Then we resize the loaded QImage to be at least as large as the widget in both directions using the private resizeImage() function and we set the image member variable to be the loaded image. At the end, we call QWidget::update() to schedule a repaint.
bool
ScribbleArea::
saveImage(const
QString &
amp;fileName, const
char
*
fileFormat)
{
QImage visibleImage =
image;
resizeImage(&
amp;visibleImage, size());
if
(visibleImage.save(fileName, fileFormat)) {
modified =
false
;
return
true
;
}
else
{
return
false
;
}
}
The saveImage() function creates a QImage object that covers only the visible section of the actual image and saves it using QImage::save(). If the image is successfully saved, we set the scribble area's modified variable to false, because there is no unsaved data.
void
ScribbleArea::
setPenColor(const
QColor &
amp;newColor)
{
myPenColor =
newColor;
}
void
ScribbleArea::
setPenWidth(int
newWidth)
{
myPenWidth =
newWidth;
}
The setPenColor() and setPenWidth() functions set the current pen color and width. These values will be used for future drawing operations.
void
ScribbleArea::
clearImage()
{
image.fill(qRgb(255
, 255
, 255
));
modified =
true
;
update();
}
The public clearImage() slot clears the image displayed in the scribble area. We simply fill the entire image with white, which corresponds to RGB value (255, 255, 255). As usual when we modify the image, we set modified to true and schedule a repaint.
void
ScribbleArea::
mousePressEvent(QMouseEvent *
event)
{
if
(event-&
gt;button() ==
Qt::
LeftButton) {
lastPoint =
event-&
gt;pos();
scribbling =
true
;
}
}
void