Polish the Tablet example
- Introduce Qt 5 signal-slot connection syntax. - Merge MainWindow::createMenus()/createActions() into MainWindow::createMenus(), removing the need to store the actions as member variables. Use QMenu::addAction() for brevity. - For actions in QActionGroups, carry the Valuator enum in QAction::data so that the slot to handle the selection does not need to compare the QAction pointer itself. - Use a non-modal QColorDialog, so that the user can change colors more easily while drawing. - Choose saner shortcut keys: control-Q should not override the default usage for quitting the application, and using shortcuts for About dialogs is anyway dubious. - Improve the example documentation. Change-Id: I57aaf5f5b885c13a953482dbcc41275dd3d6bff4 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com> Reviewed-by: Topi Reiniö <topi.reinio@theqtcompany.com>
This commit is contained in:
parent
910f719bd1
commit
e6de387ea0
@ -34,41 +34,41 @@
|
||||
\image tabletexample.png
|
||||
|
||||
When you use a tablet with Qt applications, \l{QTabletEvent}s are
|
||||
generated. You need to reimplement the
|
||||
\l{QWidget::}{tabletEvent()} event handler if you want to handle
|
||||
tablet events. Events are generated when the device used for
|
||||
drawing enters and leaves the proximity of the tablet (i.e., when
|
||||
it is close but not pressed down on it), when a device is pushed
|
||||
down and released from it, and when a device is moved on the
|
||||
tablet.
|
||||
generated. You need to reimplement the \l{QWidget::}{tabletEvent()} event
|
||||
handler if you want to handle tablet events. Events are generated when the
|
||||
tool (stylus) used for drawing enters and leaves the proximity of the
|
||||
tablet (i.e., when it is close but not pressed down on it), when the tool
|
||||
is pressed down and released from it, when the tool is moved across the
|
||||
tablet, and when one of the buttons on the tool is pressed or released.
|
||||
|
||||
The information available in QTabletEvent depends on the device
|
||||
used. The tablet in this example has two different devices for
|
||||
drawing: a stylus and an airbrush. For both devices the event
|
||||
contains the position of the device, pressure on the tablet,
|
||||
vertical tilt, and horizontal tilt (i.e, the angle between the
|
||||
device and the perpendicular of the tablet). The airbrush has a
|
||||
finger wheel; the position of this is also available in the tablet
|
||||
event.
|
||||
The information available in QTabletEvent depends on the device used.
|
||||
This example can handle a tablet with up to three different drawing tools:
|
||||
a stylus, an airbrush, and an art pen. For any of these the event will
|
||||
contain the position of the tool, pressure on the tablet, button status,
|
||||
vertical tilt, and horizontal tilt (i.e, the angle between the device and
|
||||
the perpendicular of the tablet, if the tablet hardware can provide it).
|
||||
The airbrush has a finger wheel; the position of this is also available
|
||||
in the tablet event. The art pen provides rotation around the axis
|
||||
perpendicular to the tablet surface, so that it can be used for calligraphy.
|
||||
|
||||
In this example we implement a drawing program. You can use the
|
||||
stylus to draw on the tablet as you use a pencil on paper. When
|
||||
you draw with the airbrush you get a spray of paint; the finger
|
||||
wheel is used to change the density of the spray. The pressure and
|
||||
tilt can change the alpha and saturation values of the QColor and the
|
||||
width of the QPen used for drawing.
|
||||
In this example we implement a drawing program. You can use the stylus to
|
||||
draw on the tablet as you use a pencil on paper. When you draw with the
|
||||
airbrush you get a spray of virtual paint; the finger wheel is used to
|
||||
change the density of the spray. When you draw with the art pen, you get a
|
||||
a line whose width and endpoint angle depend on the rotation of the pen.
|
||||
The pressure and tilt can also be assigned to change the alpha and
|
||||
saturation values of the color and the width of the stroke.
|
||||
|
||||
The example consists of the following:
|
||||
|
||||
\list
|
||||
\li The \c MainWindow class inherits QMainWindow and creates
|
||||
the examples menus and connect their slots and signals.
|
||||
\li The \c MainWindow class inherits QMainWindow, creates
|
||||
the menus, and connects their slots and signals.
|
||||
\li The \c TabletCanvas class inherits QWidget and
|
||||
receives tablet events. It uses the events to paint on a
|
||||
offscreen pixmap, which it draws onto itself.
|
||||
receives tablet events. It uses the events to paint onto an
|
||||
offscreen pixmap, and then renders it.
|
||||
\li The \c TabletApplication class inherits QApplication. This
|
||||
class handles tablet events that are not sent to \c tabletEvent().
|
||||
We will look at this later.
|
||||
class handles tablet proximity events.
|
||||
\li The \c main() function creates a \c MainWindow and shows it
|
||||
as a top level window.
|
||||
\endlist
|
||||
@ -81,110 +81,99 @@
|
||||
|
||||
\snippet widgets/tablet/mainwindow.h 0
|
||||
|
||||
The QActions let the user select if the tablets pressure and
|
||||
tilt should change the pen width, color alpha component and color
|
||||
saturation. \c createActions() creates all actions, and \c
|
||||
createMenus() sets up the menus with the actions. We have one
|
||||
QActionGroup for the actions that alter the alpha channel, color
|
||||
saturation and line width respectively. The action groups are
|
||||
connected to the \c alphaActionTriggered(), \c
|
||||
colorSaturationActiontriggered(), and \c
|
||||
lineWidthActionTriggered() slots, which calls functions in \c
|
||||
myCanvas.
|
||||
|
||||
\c createMenus() sets up the menus with the actions. We have one
|
||||
QActionGroup for the actions that alter the alpha channel, color saturation
|
||||
and line width respectively. The action groups are connected to the
|
||||
\c setAlphaValuator(), \c setSaturationValuator(), and
|
||||
\c setLineWidthValuator() slots, which call functions in \c TabletCanvas.
|
||||
|
||||
\section1 MainWindow Class Implementation
|
||||
|
||||
We start width a look at the constructor \c MainWindow():
|
||||
We start with a look at the constructor \c MainWindow():
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 0
|
||||
|
||||
In the constructor we create the canvas, actions, and menus.
|
||||
We set the canvas as the center widget. We also initialize the
|
||||
canvas to match the state of our menus and start drawing with a
|
||||
red color.
|
||||
In the constructor we call \c createMenus() to create all the actions and
|
||||
menus, and set the canvas as the center widget.
|
||||
|
||||
Here is the implementation of \c brushColorAct():
|
||||
\snippet widgets/tablet/mainwindow.cpp 8
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 1
|
||||
At the beginning of \c createMenus() we populate the \b File menu.
|
||||
We use an overload of \l{QMenu::}{addAction()}, introduced in Qt 5.6, to create
|
||||
a menu item with a shortcut (and optionally an icon), add it to its menu,
|
||||
and connect it to a slot, all with one line of code. We use QKeySequence to
|
||||
get the platform-specific standard key shortcuts for these common menu items.
|
||||
|
||||
We let the user pick a color with a QColorDialog. If it is valid,
|
||||
we set a new drawing color with \c setColor().
|
||||
We also populate the \b Brush menu. The command to change a brush does not
|
||||
normally have a standard shortcut, so we use \l{QObject::}{tr()} to enable
|
||||
translating the shortcut along with the language translation of the application.
|
||||
|
||||
Here is the implementation of \c alphaActionTriggered():
|
||||
Now we will look at the creation of one group of mutually-exclusive actions
|
||||
in a submenu of the \b Tablet menu, for selecting which property of each
|
||||
QTabletEvent will be used to vary the translucency (alpha channel) of the
|
||||
line being drawn or color being airbrushed.
|
||||
(See the \l{Application Example}{application example} if you want a
|
||||
high-level introduction to QActions.)
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 9
|
||||
|
||||
We want the user to be able to choose whether the drawing color's alpha
|
||||
component should be modulated by the tablet pressure, tilt, or the position
|
||||
of the thumbwheel on the airbrush tool. We have one action for each choice,
|
||||
and an additional action to choose not to change the alpha, that is, to keep
|
||||
the color opaque. We make the actions checkable; the \c alphaChannelGroup
|
||||
will then ensure that only one of the actions are checked at any time. The
|
||||
\c triggered() signal is emitted from the group when an action is checked,
|
||||
so we connect that to \c MainWindow::setAlphaValuator(). It will need to know
|
||||
which property (valuator) of the QTabletEvent to pay attention to from now
|
||||
on, so we use the QAction::data property to pass this information along.
|
||||
(In order for this to be possible, the enum \c Valuator must be a registered
|
||||
metatype, so that it can be inserted into a QVariant. That is accomplished
|
||||
by the \c Q_ENUM declaration in tabletcanvas.h.)
|
||||
|
||||
Here is the implementation of \c setAlphaValuator():
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 2
|
||||
|
||||
The \c TabletCanvas class supports two ways by which the alpha
|
||||
channel of the drawing color can be changed: tablet pressure and
|
||||
tilt. We have one action for each and an action if the alpha
|
||||
channel should not be changed.
|
||||
It simply needs to retrieve the \c Valuator enum from QAction::data(), and
|
||||
pass that to \c TabletCanvas::setAlphaChannelValuator(). If we were not
|
||||
using the \c data property, we would instead need to compare the QAction
|
||||
pointer itself, for example in a switch statement. But that would require
|
||||
keeping pointers to each QAction in class variables, for comparison purposes.
|
||||
|
||||
Here is the implementation of \c lineWidthActionTriggered():
|
||||
Here is the implementation of \c setBrushColor():
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 3
|
||||
\snippet widgets/tablet/mainwindow.cpp 1
|
||||
|
||||
We check which action is selected in \c lineWidthGroup, and set
|
||||
how the canvas should change the drawing line width.
|
||||
We do lazy initialization of a QColorDialog the first time the user
|
||||
chooses \b {Brush color...} from the menu or via the action shortcut.
|
||||
While the dialog is open, each time the user chooses a different color,
|
||||
\c TabletCanvas::setColor() will be called to change the drawing color.
|
||||
Because it is a non-modal dialog, the user is free to leave the color
|
||||
dialog open, so as to be able to conveniently and frequently change colors,
|
||||
or close it and re-open it later.
|
||||
|
||||
Here is the implementation of \c saturationActionTriggered():
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 4
|
||||
|
||||
We check which action is selected in \c colorSaturationGroup, and
|
||||
set how the canvas should change the color saturation of the
|
||||
drawing color.
|
||||
|
||||
Here is the implementation of \c saveAct():
|
||||
Here is the implementation of \c save():
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 5
|
||||
|
||||
We use the QFileDialog to let the user select a file to save the
|
||||
drawing in. It is the \c TabletCanvas that save the drawing, so we
|
||||
call its \c saveImage() function.
|
||||
We use the QFileDialog to let the user select a file to save the drawing,
|
||||
and then call \c TabletCanvas::saveImage() to actually write it to the
|
||||
file.
|
||||
|
||||
Here is the implementation of \c loadAct():
|
||||
Here is the implementation of \c load():
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 6
|
||||
|
||||
We let the user select the image file to be opened with
|
||||
a QFileDialog; we then ask the canvas to load the image with \c
|
||||
loadImage().
|
||||
We let the user select the image file to be opened with a QFileDialog; we
|
||||
then ask the canvas to load the image with \c loadImage().
|
||||
|
||||
Here is the implementation of \c aboutAct():
|
||||
Here is the implementation of \c about():
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 7
|
||||
|
||||
We show a message box with a short description of the example.
|
||||
|
||||
\c createActions() creates all actions and action groups of
|
||||
the example. We look at the creation of one action group and its
|
||||
actions. See the \l{Application Example}{application example} if
|
||||
you want a high-level introduction to QActions.
|
||||
|
||||
Here is the implementation of \c createActions:
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 8
|
||||
\dots
|
||||
\snippet widgets/tablet/mainwindow.cpp 9
|
||||
|
||||
We want the user to be able to choose if the drawing color's
|
||||
alpha component should be changed by the tablet pressure or tilt.
|
||||
We have one action for each choice and an action if the alpha
|
||||
channel is not to be changed, i.e, the color is opaque. We make
|
||||
the actions checkable; the \c alphaChannelGroup will then ensure
|
||||
that only one of the actions are checked at any time. The \c
|
||||
triggered() signal is emitted when an action is checked.
|
||||
|
||||
\dots
|
||||
\snippet widgets/tablet/mainwindow.cpp 10
|
||||
|
||||
Here is the implementation of \c createMenus():
|
||||
|
||||
\snippet widgets/tablet/mainwindow.cpp 11
|
||||
|
||||
We create the menus of the example and add the actions to them.
|
||||
|
||||
|
||||
\section1 TabletCanvas Class Definition
|
||||
|
||||
@ -193,24 +182,22 @@
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.h 0
|
||||
|
||||
The canvas can change the alpha channel, color saturation,
|
||||
and line width of the drawing. We have one enum for each of
|
||||
these; their values decide if it is the tablet pressure or tilt
|
||||
that will alter them. We keep a private variable for each, the \c
|
||||
alphaChannelType, \c colorSturationType, and \c penWidthType,
|
||||
which we provide access functions for.
|
||||
The canvas can change the alpha channel, color saturation, and line width
|
||||
of the stroke. We have an enum listing the QTabletEvent properties with
|
||||
which it is possible to modulate them. We keep a private variable for each:
|
||||
\c m_alphaChannelValuator, \c m_colorSaturationValuator and
|
||||
\c m_lineWidthValuator, and we provide accessor functions for them.
|
||||
|
||||
We draw on a QPixmap with \c myPen and \c myBrush using \c
|
||||
myColor. The \c saveImage() and \c loadImage() saves and loads
|
||||
the QPixmap to disk. The pixmap is drawn on the widget in \c
|
||||
paintEvent(). The \c pointerType and \c deviceType keeps the type
|
||||
of pointer, which is either a pen or an eraser, and device
|
||||
currently used on the tablet, which is either a stylus or an
|
||||
airbrush.
|
||||
We draw on a QPixmap with \c m_pen and \c m_brush using \c m_color.
|
||||
Each time a QTabletEvent is received, the stroke is drawn from
|
||||
\c lastPoint to the point given in the current QTabletEvent,
|
||||
and then the position and rotation are saved in \c lastPoint for next time.
|
||||
The \c saveImage() and \c loadImage() functions save and load the QPixmap to disk.
|
||||
The pixmap is drawn on the widget in \c paintEvent().
|
||||
|
||||
The interpretation of events from the tablet is done in \c
|
||||
tabletEvent(); \c paintPixmap(), \c updateBrush(), and \c
|
||||
brushPattern() are helper functions used by \c tabletEvent().
|
||||
The interpretation of events from the tablet is done in \c tabletEvent(), and
|
||||
\c paintPixmap(), \c updateBrush(), and \c updateCursor() are helper
|
||||
functions used by \c tabletEvent().
|
||||
|
||||
|
||||
\section1 TabletCanvas Class Implementation
|
||||
@ -233,21 +220,28 @@
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 2
|
||||
|
||||
We simply call \l{QPixmap::}{load()}, which loads the image in \a
|
||||
file.
|
||||
We simply call \l{QPixmap::}{load()}, which loads the image from \a file.
|
||||
|
||||
Here is the implementation of \c tabletEvent():
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 3
|
||||
|
||||
We get three kind of events to this function: TabletPress,
|
||||
TabletRelease, and TabletMove, which is generated when a device
|
||||
is pressed down on, leaves, or moves on the tablet. We set the \c
|
||||
deviceDown to true when a device is pressed down on the tablet;
|
||||
we then know when we should draw when we receive move events. We
|
||||
have implemented the \c updateBrush() and \c paintPixmap() helper
|
||||
functions to update \c myBrush and \c myPen after the state of \c
|
||||
alphaChannelType, \c colorSaturationType, and \c lineWidthType.
|
||||
We get three kind of events to this function: \c TabletPress, \c TabletRelease,
|
||||
and \c TabletMove, which are generated when a drawing tool is pressed down on,
|
||||
lifed up from, or moved across the tablet. We set \c m_deviceDown to \c true
|
||||
when a device is pressed down on the tablet; we then know that we should
|
||||
draw when we receive move events. We have implemented \c updateBrush()
|
||||
to update \c m_brush and \c m_pen depending on which of the tablet event
|
||||
properties the user has chosen to pay attention to. The \c updateCursor()
|
||||
function selects a cursor to represent the drawing tool in use, so that
|
||||
as you hover with the tool in proximity of the tablet, you can see what
|
||||
kind of stroke you are about to make.
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 12
|
||||
|
||||
If an art pen (\c RotationStylus) is in use, \c updateCursor()
|
||||
is also called for each \c TabletMove event, and renders a rotated cursor
|
||||
so that you can see the angle of the pen tip.
|
||||
|
||||
Here is the implementation of \c paintEvent():
|
||||
|
||||
@ -259,22 +253,22 @@
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 5
|
||||
|
||||
In this function we draw on the pixmap based on the movement of the device.
|
||||
If the device used on the tablet is a stylus, we want to draw a line from
|
||||
In this function we draw on the pixmap based on the movement of the tool.
|
||||
If the tool used on the tablet is a stylus, we want to draw a line from
|
||||
the last-known position to the current position. We also assume that this
|
||||
is a reasonable handling of any unknown device, but update the status bar
|
||||
with a warning. If it is an airbrush, we want to draw a circle filled with
|
||||
a soft gradient, whose density can depend on various event parameters.
|
||||
By default it depends on the tangential pressure, which is the position of
|
||||
the finger wheel on the airbrush. If it is a rotation stylus, we simulate
|
||||
a felt marker by drawing trapezoidal strokes.
|
||||
the finger wheel on the airbrush. If the tool is a rotation stylus, we
|
||||
simulate a felt marker by drawing trapezoidal stroke segments.
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 6
|
||||
|
||||
In \c updateBrush() we set the pen and brush used for drawing
|
||||
to match \c alphaChannelType, \c lineWidthType, \c
|
||||
colorSaturationType, and \c myColor. We will examine the code to
|
||||
set up \c myBrush and \c myPen for each of these variables:
|
||||
In \c updateBrush() we set the pen and brush used for drawing to match
|
||||
\c m_alphaChannelValuator, \c m_lineWidthValuator, \c m_colorSaturationValuator,
|
||||
and \c m_color. We will examine the code to set up \c m_brush and
|
||||
\c m_pen for each of these variables:
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 7
|
||||
|
||||
@ -288,34 +282,33 @@
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 8
|
||||
|
||||
The alpha channel of QColor is given as a number between 0
|
||||
and 255 where 0 is transparent and 255 is opaque.
|
||||
\l{QTabletEvent::}{pressure()} returns the pressure as a qreal
|
||||
between 0.0 and 1.0. By subtracting 127 from the tilt values and
|
||||
taking the absolute value we get the smallest alpha values (i.e.,
|
||||
the color is most transparent) when the pen is perpendicular to
|
||||
the tablet. We select the largest of the vertical and horizontal
|
||||
tilt value.
|
||||
The alpha channel of QColor is given as a number between 0 and 255 where 0
|
||||
is transparent and 255 is opaque, or as a floating-point number where 0 is
|
||||
transparent and 1.0 is opaque. \l{QTabletEvent::}{pressure()} returns the
|
||||
pressure as a qreal between 0.0 and 1.0. We get the smallest alpha values
|
||||
(i.e., the color is most transparent) when the pen is perpendicular to the
|
||||
tablet. We select the largest of the vertical and horizontal tilt values.
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 9
|
||||
|
||||
The colorsaturation is given as a number between 0 and 255. It is
|
||||
set with \l{QColor::}{setHsv()}. We can set the tilt values
|
||||
directly, but must multiply the pressure to a number between 0 and
|
||||
255.
|
||||
The color saturation in the HSV color model can be given as an integer
|
||||
between 0 and 255 or as a floating-point value between 0 and 1. We chose to
|
||||
represent alpha as an integer, so we call \l{QColor::}{setHsv()} with
|
||||
integer values. That means we need to multiply the pressure to a number
|
||||
between 0 and 255.
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 10
|
||||
|
||||
The width of the pen increases with the pressure. When the pen
|
||||
width is controlled with the tilt we let the width increse with
|
||||
the angle between the device and the perpendicular of the tablet.
|
||||
The width of the pen stroke can increase with pressure, if so chosen.
|
||||
But when the pen width is controlled by tilt, we let the width increase
|
||||
with the angle between the tool and the perpendicular of the tablet.
|
||||
|
||||
\snippet widgets/tablet/tabletcanvas.cpp 11
|
||||
|
||||
We finally check whether the pointer is the stylus or the eraser.
|
||||
If it is the eraser, we set the color to the background color of
|
||||
the pixmap an let the pressure decide the pen width, else we set
|
||||
the colors we have set up previously in the function.
|
||||
the pixmap and let the pressure decide the pen width, else we set
|
||||
the colors we have decided previously in the function.
|
||||
|
||||
|
||||
\section1 TabletApplication Class Definition
|
||||
@ -325,13 +318,11 @@
|
||||
|
||||
\snippet widgets/tablet/tabletapplication.h 0
|
||||
|
||||
We keep a \c TabletCanvas we send the device type of the events we
|
||||
handle in the \c event() function to. The TabletEnterProximity
|
||||
and TabletLeaveProximity events are not sendt to the QApplication
|
||||
object, while other tablet events are sendt to the QWidget's
|
||||
\c event(), which sends them on to \l{QWidget::}{tabletEvent()}.
|
||||
Since we want to handle these events we have implemented \c
|
||||
TabletApplication.
|
||||
\c TabletApplication exists as a subclass of QApplication in order to
|
||||
receive tablet proximity events and forward them to \c TabletCanvas.
|
||||
The \c TabletEnterProximity and \c TabletLeaveProximity events are sent to
|
||||
the QApplication object, while other tablet events are sent to the QWidget's
|
||||
\c event() hander, which sends them on to \l{QWidget::}{tabletEvent()}.
|
||||
|
||||
|
||||
\section1 TabletApplication Class Implementation
|
||||
@ -340,24 +331,24 @@
|
||||
|
||||
\snippet widgets/tablet/tabletapplication.cpp 0
|
||||
|
||||
We use this function to handle the TabletEnterProximity and
|
||||
TabletLeaveProximity events, which is generated when a device
|
||||
enters and leaves the proximity of the tablet. The intended use of these
|
||||
events is to do work that is dependent on what kind of device is
|
||||
used on the tablet. This way, you don't have to do this work
|
||||
when other events are generated, which is more frequently than the
|
||||
leave and enter proximity events. We call \c setTabletDevice() in
|
||||
\c TabletCanvas.
|
||||
We use this function to handle the \c TabletEnterProximity and
|
||||
\c TabletLeaveProximity events, which are generated when a drawing
|
||||
tool enters or leaves the proximity of the tablet. Here we call
|
||||
\c TabletCanvas::setTabletDevice(), which then calls \c updateCursor(),
|
||||
which will set an appropriate cursor. This is the only reason we
|
||||
need the proximity events; for the purpose of correct drawing, it is
|
||||
enough for \c TabletCanvas to observe the \l{QTabletEvent::}{device()} and
|
||||
\l{QTabletEvent::}{pointerType()} in each event that it receives.
|
||||
|
||||
|
||||
\section1 The \c main() function
|
||||
|
||||
Here is the examples \c main() function:
|
||||
Here is the example's \c main() function:
|
||||
|
||||
\snippet widgets/tablet/main.cpp 0
|
||||
|
||||
In the \c main() function we create a \c MainWinow and display it
|
||||
as a top level window. We use the \c TabletApplication class. We
|
||||
need to set the canvas after the application is created. We cannot
|
||||
use classes that implement event handling before an QApplication
|
||||
object is instantiated.
|
||||
Here we create a \c MainWindow and display it as a top level window. We use
|
||||
the \c TabletApplication class. We need to set the canvas after the
|
||||
application is created. We cannot use classes that implement event handling
|
||||
before an QApplication object is instantiated.
|
||||
*/
|
||||
|
@ -45,237 +45,172 @@
|
||||
|
||||
//! [0]
|
||||
MainWindow::MainWindow(TabletCanvas *canvas)
|
||||
: m_canvas(canvas), m_colorDialog(Q_NULLPTR)
|
||||
{
|
||||
myCanvas = canvas;
|
||||
createActions();
|
||||
createMenus();
|
||||
|
||||
myCanvas->setColor(Qt::red);
|
||||
myCanvas->setLineWidthType(TabletCanvas::LineWidthPressure);
|
||||
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTangentialPressure);
|
||||
myCanvas->setColorSaturationType(TabletCanvas::NoSaturation);
|
||||
|
||||
setWindowTitle(tr("Tablet Example"));
|
||||
setCentralWidget(myCanvas);
|
||||
setCentralWidget(m_canvas);
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
void MainWindow::brushColorAct()
|
||||
void MainWindow::setBrushColor()
|
||||
{
|
||||
QColor color = QColorDialog::getColor(myCanvas->color());
|
||||
|
||||
if (color.isValid())
|
||||
myCanvas->setColor(color);
|
||||
if (!m_colorDialog) {
|
||||
m_colorDialog = new QColorDialog(this);
|
||||
m_colorDialog->setModal(false);
|
||||
m_colorDialog->setCurrentColor(m_canvas->color());
|
||||
connect(m_colorDialog, &QColorDialog::colorSelected, m_canvas, &TabletCanvas::setColor);
|
||||
}
|
||||
m_colorDialog->setVisible(true);
|
||||
}
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
void MainWindow::alphaActionTriggered(QAction *action)
|
||||
void MainWindow::setAlphaValuator(QAction *action)
|
||||
{
|
||||
if (action == alphaChannelPressureAction) {
|
||||
myCanvas->setAlphaChannelType(TabletCanvas::AlphaPressure);
|
||||
} else if (action == alphaChannelTangentialPressureAction) {
|
||||
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTangentialPressure);
|
||||
} else if (action == alphaChannelTiltAction) {
|
||||
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTilt);
|
||||
} else {
|
||||
myCanvas->setAlphaChannelType(TabletCanvas::NoAlpha);
|
||||
}
|
||||
m_canvas->setAlphaChannelValuator(action->data().value<TabletCanvas::Valuator>());
|
||||
}
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
void MainWindow::lineWidthActionTriggered(QAction *action)
|
||||
void MainWindow::setLineWidthValuator(QAction *action)
|
||||
{
|
||||
if (action == lineWidthPressureAction) {
|
||||
myCanvas->setLineWidthType(TabletCanvas::LineWidthPressure);
|
||||
} else if (action == lineWidthTiltAction) {
|
||||
myCanvas->setLineWidthType(TabletCanvas::LineWidthTilt);
|
||||
} else {
|
||||
myCanvas->setLineWidthType(TabletCanvas::NoLineWidth);
|
||||
}
|
||||
m_canvas->setLineWidthType(action->data().value<TabletCanvas::Valuator>());
|
||||
}
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
void MainWindow::saturationActionTriggered(QAction *action)
|
||||
void MainWindow::setSaturationValuator(QAction *action)
|
||||
{
|
||||
if (action == colorSaturationVTiltAction) {
|
||||
myCanvas->setColorSaturationType(TabletCanvas::SaturationVTilt);
|
||||
} else if (action == colorSaturationHTiltAction) {
|
||||
myCanvas->setColorSaturationType(TabletCanvas::SaturationHTilt);
|
||||
} else if (action == colorSaturationPressureAction) {
|
||||
myCanvas->setColorSaturationType(TabletCanvas::SaturationPressure);
|
||||
} else {
|
||||
myCanvas->setColorSaturationType(TabletCanvas::NoSaturation);
|
||||
}
|
||||
m_canvas->setColorSaturationValuator(action->data().value<TabletCanvas::Valuator>());
|
||||
}
|
||||
//! [4]
|
||||
|
||||
//! [5]
|
||||
void MainWindow::saveAct()
|
||||
void MainWindow::save()
|
||||
{
|
||||
QString path = QDir::currentPath() + "/untitled.png";
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save Picture"),
|
||||
path);
|
||||
|
||||
if (!myCanvas->saveImage(fileName))
|
||||
if (!m_canvas->saveImage(fileName))
|
||||
QMessageBox::information(this, "Error Saving Picture",
|
||||
"Could not save the image");
|
||||
}
|
||||
//! [5]
|
||||
|
||||
//! [6]
|
||||
void MainWindow::loadAct()
|
||||
void MainWindow::load()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Picture"),
|
||||
QDir::currentPath());
|
||||
|
||||
if (!myCanvas->loadImage(fileName))
|
||||
if (!m_canvas->loadImage(fileName))
|
||||
QMessageBox::information(this, "Error Opening Picture",
|
||||
"Could not open picture");
|
||||
}
|
||||
//! [6]
|
||||
|
||||
//! [7]
|
||||
void MainWindow::aboutAct()
|
||||
void MainWindow::about()
|
||||
{
|
||||
QMessageBox::about(this, tr("About Tablet Example"),
|
||||
tr("This example shows use of a Wacom tablet in Qt"));
|
||||
tr("This example shows how to use a graphics drawing tablet in Qt."));
|
||||
}
|
||||
//! [7]
|
||||
|
||||
//! [8]
|
||||
void MainWindow::createActions()
|
||||
void MainWindow::createMenus()
|
||||
{
|
||||
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
|
||||
fileMenu->addAction(tr("&Open..."), this, &MainWindow::load, QKeySequence::Open);
|
||||
fileMenu->addAction(tr("&Save As..."), this, &MainWindow::save, QKeySequence::SaveAs);
|
||||
fileMenu->addAction(tr("E&xit"), this, &MainWindow::close, QKeySequence::Quit);
|
||||
|
||||
QMenu *brushMenu = menuBar()->addMenu(tr("&Brush"));
|
||||
brushMenu->addAction(tr("&Brush Color..."), this, &MainWindow::setBrushColor, tr("Ctrl+B"));
|
||||
//! [8]
|
||||
brushColorAction = new QAction(tr("&Brush Color..."), this);
|
||||
brushColorAction->setShortcut(tr("Ctrl+C"));
|
||||
connect(brushColorAction, SIGNAL(triggered()),
|
||||
this, SLOT(brushColorAct()));
|
||||
|
||||
QMenu *tabletMenu = menuBar()->addMenu(tr("&Tablet"));
|
||||
QMenu *lineWidthMenu = tabletMenu->addMenu(tr("&Line Width"));
|
||||
|
||||
QAction *lineWidthPressureAction = lineWidthMenu->addAction(tr("&Pressure"));
|
||||
lineWidthPressureAction->setData(TabletCanvas::PressureValuator);
|
||||
lineWidthPressureAction->setCheckable(true);
|
||||
lineWidthPressureAction->setChecked(true);
|
||||
|
||||
QAction *lineWidthTiltAction = lineWidthMenu->addAction(tr("&Tilt"));
|
||||
lineWidthTiltAction->setData(TabletCanvas::TiltValuator);
|
||||
lineWidthTiltAction->setCheckable(true);
|
||||
|
||||
QAction *lineWidthFixedAction = lineWidthMenu->addAction(tr("&Fixed"));
|
||||
lineWidthFixedAction->setData(TabletCanvas::NoValuator);
|
||||
lineWidthFixedAction->setCheckable(true);
|
||||
|
||||
QActionGroup *lineWidthGroup = new QActionGroup(this);
|
||||
lineWidthGroup->addAction(lineWidthPressureAction);
|
||||
lineWidthGroup->addAction(lineWidthTiltAction);
|
||||
lineWidthGroup->addAction(lineWidthFixedAction);
|
||||
connect(lineWidthGroup, &QActionGroup::triggered, this,
|
||||
&MainWindow::setLineWidthValuator);
|
||||
|
||||
//! [9]
|
||||
alphaChannelPressureAction = new QAction(tr("&Pressure"), this);
|
||||
QMenu *alphaChannelMenu = tabletMenu->addMenu(tr("&Alpha Channel"));
|
||||
QAction *alphaChannelPressureAction = alphaChannelMenu->addAction(tr("&Pressure"));
|
||||
alphaChannelPressureAction->setData(TabletCanvas::PressureValuator);
|
||||
alphaChannelPressureAction->setCheckable(true);
|
||||
|
||||
alphaChannelTangentialPressureAction = new QAction(tr("T&angential Pressure"), this);
|
||||
QAction *alphaChannelTangentialPressureAction = alphaChannelMenu->addAction(tr("T&angential Pressure"));
|
||||
alphaChannelTangentialPressureAction->setData(TabletCanvas::TangentialPressureValuator);
|
||||
alphaChannelTangentialPressureAction->setCheckable(true);
|
||||
alphaChannelTangentialPressureAction->setChecked(true);
|
||||
|
||||
alphaChannelTiltAction = new QAction(tr("&Tilt"), this);
|
||||
QAction *alphaChannelTiltAction = alphaChannelMenu->addAction(tr("&Tilt"));
|
||||
alphaChannelTiltAction->setData(TabletCanvas::TiltValuator);
|
||||
alphaChannelTiltAction->setCheckable(true);
|
||||
|
||||
noAlphaChannelAction = new QAction(tr("No Alpha Channel"), this);
|
||||
QAction *noAlphaChannelAction = alphaChannelMenu->addAction(tr("No Alpha Channel"));
|
||||
noAlphaChannelAction->setData(TabletCanvas::NoValuator);
|
||||
noAlphaChannelAction->setCheckable(true);
|
||||
|
||||
alphaChannelGroup = new QActionGroup(this);
|
||||
QActionGroup *alphaChannelGroup = new QActionGroup(this);
|
||||
alphaChannelGroup->addAction(alphaChannelPressureAction);
|
||||
alphaChannelGroup->addAction(alphaChannelTangentialPressureAction);
|
||||
alphaChannelGroup->addAction(alphaChannelTiltAction);
|
||||
alphaChannelGroup->addAction(noAlphaChannelAction);
|
||||
connect(alphaChannelGroup, SIGNAL(triggered(QAction*)),
|
||||
this, SLOT(alphaActionTriggered(QAction*)));
|
||||
|
||||
connect(alphaChannelGroup, &QActionGroup::triggered,
|
||||
this, &MainWindow::setAlphaValuator);
|
||||
//! [9]
|
||||
colorSaturationVTiltAction = new QAction(tr("&Vertical Tilt"), this);
|
||||
|
||||
QMenu *colorSaturationMenu = tabletMenu->addMenu(tr("&Color Saturation"));
|
||||
|
||||
QAction *colorSaturationVTiltAction = colorSaturationMenu->addAction(tr("&Vertical Tilt"));
|
||||
colorSaturationVTiltAction->setData(TabletCanvas::VTiltValuator);
|
||||
colorSaturationVTiltAction->setCheckable(true);
|
||||
|
||||
colorSaturationHTiltAction = new QAction(tr("&Horizontal Tilt"), this);
|
||||
QAction *colorSaturationHTiltAction = colorSaturationMenu->addAction(tr("&Horizontal Tilt"));
|
||||
colorSaturationHTiltAction->setData(TabletCanvas::HTiltValuator);
|
||||
colorSaturationHTiltAction->setCheckable(true);
|
||||
|
||||
colorSaturationPressureAction = new QAction(tr("&Pressure"), this);
|
||||
QAction *colorSaturationPressureAction = colorSaturationMenu->addAction(tr("&Pressure"));
|
||||
colorSaturationPressureAction->setData(TabletCanvas::PressureValuator);
|
||||
colorSaturationPressureAction->setCheckable(true);
|
||||
|
||||
noColorSaturationAction = new QAction(tr("&No Color Saturation"), this);
|
||||
QAction *noColorSaturationAction = colorSaturationMenu->addAction(tr("&No Color Saturation"));
|
||||
noColorSaturationAction->setData(TabletCanvas::NoValuator);
|
||||
noColorSaturationAction->setCheckable(true);
|
||||
noColorSaturationAction->setChecked(true);
|
||||
|
||||
colorSaturationGroup = new QActionGroup(this);
|
||||
QActionGroup *colorSaturationGroup = new QActionGroup(this);
|
||||
colorSaturationGroup->addAction(colorSaturationVTiltAction);
|
||||
colorSaturationGroup->addAction(colorSaturationHTiltAction);
|
||||
colorSaturationGroup->addAction(colorSaturationPressureAction);
|
||||
colorSaturationGroup->addAction(noColorSaturationAction);
|
||||
connect(colorSaturationGroup, SIGNAL(triggered(QAction*)),
|
||||
this, SLOT(saturationActionTriggered(QAction*)));
|
||||
connect(colorSaturationGroup, &QActionGroup::triggered,
|
||||
this, &MainWindow::setSaturationValuator);
|
||||
|
||||
lineWidthPressureAction = new QAction(tr("&Pressure"), this);
|
||||
lineWidthPressureAction->setCheckable(true);
|
||||
lineWidthPressureAction->setChecked(true);
|
||||
|
||||
lineWidthTiltAction = new QAction(tr("&Tilt"), this);
|
||||
lineWidthTiltAction->setCheckable(true);
|
||||
|
||||
lineWidthFixedAction = new QAction(tr("&Fixed"), this);
|
||||
lineWidthFixedAction->setCheckable(true);
|
||||
|
||||
lineWidthGroup = new QActionGroup(this);
|
||||
lineWidthGroup->addAction(lineWidthPressureAction);
|
||||
lineWidthGroup->addAction(lineWidthTiltAction);
|
||||
lineWidthGroup->addAction(lineWidthFixedAction);
|
||||
connect(lineWidthGroup, SIGNAL(triggered(QAction*)),
|
||||
this, SLOT(lineWidthActionTriggered(QAction*)));
|
||||
|
||||
exitAction = new QAction(tr("E&xit"), this);
|
||||
exitAction->setShortcuts(QKeySequence::Quit);
|
||||
connect(exitAction, SIGNAL(triggered()),
|
||||
this, SLOT(close()));
|
||||
|
||||
loadAction = new QAction(tr("&Open..."), this);
|
||||
loadAction->setShortcuts(QKeySequence::Open);
|
||||
connect(loadAction, SIGNAL(triggered()),
|
||||
this, SLOT(loadAct()));
|
||||
|
||||
saveAction = new QAction(tr("&Save As..."), this);
|
||||
saveAction->setShortcuts(QKeySequence::SaveAs);
|
||||
connect(saveAction, SIGNAL(triggered()),
|
||||
this, SLOT(saveAct()));
|
||||
|
||||
aboutAction = new QAction(tr("A&bout"), this);
|
||||
aboutAction->setShortcut(tr("Ctrl+B"));
|
||||
connect(aboutAction, SIGNAL(triggered()),
|
||||
this, SLOT(aboutAct()));
|
||||
|
||||
aboutQtAction = new QAction(tr("About &Qt"), this);
|
||||
aboutQtAction->setShortcut(tr("Ctrl+Q"));
|
||||
connect(aboutQtAction, SIGNAL(triggered()),
|
||||
qApp, SLOT(aboutQt()));
|
||||
//! [10]
|
||||
QMenu *helpMenu = menuBar()->addMenu("&Help");
|
||||
helpMenu->addAction(tr("A&bout"), this, &MainWindow::about);
|
||||
helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
|
||||
}
|
||||
//! [10]
|
||||
|
||||
//! [11]
|
||||
void MainWindow::createMenus()
|
||||
{
|
||||
fileMenu = menuBar()->addMenu(tr("&File"));
|
||||
fileMenu->addAction(loadAction);
|
||||
fileMenu->addAction(saveAction);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(exitAction);
|
||||
|
||||
brushMenu = menuBar()->addMenu(tr("&Brush"));
|
||||
brushMenu->addAction(brushColorAction);
|
||||
|
||||
tabletMenu = menuBar()->addMenu(tr("&Tablet"));
|
||||
|
||||
lineWidthMenu = tabletMenu->addMenu(tr("&Line Width"));
|
||||
lineWidthMenu->addAction(lineWidthPressureAction);
|
||||
lineWidthMenu->addAction(lineWidthTiltAction);
|
||||
lineWidthMenu->addAction(lineWidthFixedAction);
|
||||
|
||||
alphaChannelMenu = tabletMenu->addMenu(tr("&Alpha Channel"));
|
||||
alphaChannelMenu->addAction(alphaChannelPressureAction);
|
||||
alphaChannelMenu->addAction(alphaChannelTangentialPressureAction);
|
||||
alphaChannelMenu->addAction(alphaChannelTiltAction);
|
||||
alphaChannelMenu->addAction(noAlphaChannelAction);
|
||||
|
||||
colorSaturationMenu = tabletMenu->addMenu(tr("&Color Saturation"));
|
||||
colorSaturationMenu->addAction(colorSaturationVTiltAction);
|
||||
colorSaturationMenu->addAction(colorSaturationHTiltAction);
|
||||
colorSaturationMenu->addAction(noColorSaturationAction);
|
||||
|
||||
helpMenu = menuBar()->addMenu("&Help");
|
||||
helpMenu->addAction(aboutAction);
|
||||
helpMenu->addAction(aboutQtAction);
|
||||
}
|
||||
//! [11]
|
||||
|
@ -44,10 +44,7 @@
|
||||
#include <QMainWindow>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
class QActionGroup;
|
||||
class QMenu;
|
||||
class QStatusBar;
|
||||
class QColorDialog;
|
||||
QT_END_NAMESPACE
|
||||
class TabletCanvas;
|
||||
|
||||
@ -60,54 +57,19 @@ public:
|
||||
MainWindow(TabletCanvas *canvas);
|
||||
|
||||
private slots:
|
||||
void brushColorAct();
|
||||
void alphaActionTriggered(QAction *action);
|
||||
void lineWidthActionTriggered(QAction *action);
|
||||
void saturationActionTriggered(QAction *action);
|
||||
void saveAct();
|
||||
void loadAct();
|
||||
void aboutAct();
|
||||
void setBrushColor();
|
||||
void setAlphaValuator(QAction *action);
|
||||
void setLineWidthValuator(QAction *action);
|
||||
void setSaturationValuator(QAction *action);
|
||||
void save();
|
||||
void load();
|
||||
void about();
|
||||
|
||||
private:
|
||||
void createActions();
|
||||
void createMenus();
|
||||
|
||||
TabletCanvas *myCanvas;
|
||||
|
||||
QAction *brushColorAction;
|
||||
QActionGroup *brushActionGroup;
|
||||
|
||||
QActionGroup *alphaChannelGroup;
|
||||
QAction *alphaChannelPressureAction;
|
||||
QAction *alphaChannelTangentialPressureAction;
|
||||
QAction *alphaChannelTiltAction;
|
||||
QAction *noAlphaChannelAction;
|
||||
|
||||
QActionGroup *colorSaturationGroup;
|
||||
QAction *colorSaturationVTiltAction;
|
||||
QAction *colorSaturationHTiltAction;
|
||||
QAction *colorSaturationPressureAction;
|
||||
QAction *noColorSaturationAction;
|
||||
|
||||
QActionGroup *lineWidthGroup;
|
||||
QAction *lineWidthPressureAction;
|
||||
QAction *lineWidthTiltAction;
|
||||
QAction *lineWidthFixedAction;
|
||||
|
||||
QAction *exitAction;
|
||||
QAction *saveAction;
|
||||
QAction *loadAction;
|
||||
|
||||
QAction *aboutAction;
|
||||
QAction *aboutQtAction;
|
||||
|
||||
QMenu *fileMenu;
|
||||
QMenu *brushMenu;
|
||||
QMenu *tabletMenu;
|
||||
QMenu *helpMenu;
|
||||
QMenu *colorSaturationMenu;
|
||||
QMenu *lineWidthMenu;
|
||||
QMenu *alphaChannelMenu;
|
||||
TabletCanvas *m_canvas;
|
||||
QColorDialog *m_colorDialog;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
|
@ -47,7 +47,7 @@ bool TabletApplication::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::TabletEnterProximity ||
|
||||
event->type() == QEvent::TabletLeaveProximity) {
|
||||
myCanvas->setTabletDevice(static_cast<QTabletEvent *>(event));
|
||||
m_canvas->setTabletDevice(static_cast<QTabletEvent *>(event));
|
||||
return true;
|
||||
}
|
||||
return QApplication::event(event);
|
||||
|
@ -56,10 +56,10 @@ public:
|
||||
|
||||
bool event(QEvent *event) Q_DECL_OVERRIDE;
|
||||
void setCanvas(TabletCanvas *canvas)
|
||||
{ myCanvas = canvas; }
|
||||
{ m_canvas = canvas; }
|
||||
|
||||
private:
|
||||
TabletCanvas *myCanvas;
|
||||
TabletCanvas *m_canvas;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
|
@ -45,17 +45,18 @@
|
||||
|
||||
//! [0]
|
||||
TabletCanvas::TabletCanvas()
|
||||
: QWidget(Q_NULLPTR)
|
||||
, m_alphaChannelValuator(TangentialPressureValuator)
|
||||
, m_colorSaturationValuator(NoValuator)
|
||||
, m_lineWidthValuator(PressureValuator)
|
||||
, m_color(Qt::red)
|
||||
, m_brush(m_color)
|
||||
, m_pen(m_brush, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)
|
||||
, m_deviceDown(false)
|
||||
{
|
||||
resize(500, 500);
|
||||
myColor = Qt::red;
|
||||
myBrush = QBrush(myColor);
|
||||
myPen = QPen(myBrush, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
|
||||
initPixmap();
|
||||
setAutoFillBackground(true);
|
||||
deviceDown = false;
|
||||
alphaChannelType = AlphaTangentialPressure;
|
||||
colorSaturationType = NoSaturation;
|
||||
lineWidthType = LineWidthPressure;
|
||||
}
|
||||
|
||||
void TabletCanvas::initPixmap()
|
||||
@ -63,24 +64,24 @@ void TabletCanvas::initPixmap()
|
||||
QPixmap newPixmap = QPixmap(width(), height());
|
||||
newPixmap.fill(Qt::white);
|
||||
QPainter painter(&newPixmap);
|
||||
if (!pixmap.isNull())
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
if (!m_pixmap.isNull())
|
||||
painter.drawPixmap(0, 0, m_pixmap);
|
||||
painter.end();
|
||||
pixmap = newPixmap;
|
||||
m_pixmap = newPixmap;
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
bool TabletCanvas::saveImage(const QString &file)
|
||||
{
|
||||
return pixmap.save(file);
|
||||
return m_pixmap.save(file);
|
||||
}
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
bool TabletCanvas::loadImage(const QString &file)
|
||||
{
|
||||
bool success = pixmap.load(file);
|
||||
bool success = m_pixmap.load(file);
|
||||
|
||||
if (success) {
|
||||
update();
|
||||
@ -96,8 +97,8 @@ void TabletCanvas::tabletEvent(QTabletEvent *event)
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::TabletPress:
|
||||
if (!deviceDown) {
|
||||
deviceDown = true;
|
||||
if (!m_deviceDown) {
|
||||
m_deviceDown = true;
|
||||
lastPoint.pos = event->posF();
|
||||
lastPoint.rotation = event->rotation();
|
||||
}
|
||||
@ -105,17 +106,17 @@ void TabletCanvas::tabletEvent(QTabletEvent *event)
|
||||
case QEvent::TabletMove:
|
||||
if (event->device() == QTabletEvent::RotationStylus)
|
||||
updateCursor(event);
|
||||
if (deviceDown) {
|
||||
if (m_deviceDown) {
|
||||
updateBrush(event);
|
||||
QPainter painter(&pixmap);
|
||||
QPainter painter(&m_pixmap);
|
||||
paintPixmap(painter, event);
|
||||
lastPoint.pos = event->posF();
|
||||
lastPoint.rotation = event->rotation();
|
||||
}
|
||||
break;
|
||||
case QEvent::TabletRelease:
|
||||
if (deviceDown && event->buttons() == Qt::NoButton)
|
||||
deviceDown = false;
|
||||
if (m_deviceDown && event->buttons() == Qt::NoButton)
|
||||
m_deviceDown = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -128,7 +129,7 @@ void TabletCanvas::tabletEvent(QTabletEvent *event)
|
||||
void TabletCanvas::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
painter.drawPixmap(0, 0, m_pixmap);
|
||||
}
|
||||
//! [4]
|
||||
|
||||
@ -142,10 +143,10 @@ void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
|
||||
case QTabletEvent::Airbrush:
|
||||
{
|
||||
painter.setPen(Qt::NoPen);
|
||||
QRadialGradient grad(lastPoint.pos, myPen.widthF() * 10.0);
|
||||
QColor color = myBrush.color();
|
||||
QRadialGradient grad(lastPoint.pos, m_pen.widthF() * 10.0);
|
||||
QColor color = m_brush.color();
|
||||
color.setAlphaF(color.alphaF() * 0.25);
|
||||
grad.setColorAt(0, myBrush.color());
|
||||
grad.setColorAt(0, m_brush.color());
|
||||
grad.setColorAt(0.5, Qt::transparent);
|
||||
painter.setBrush(grad);
|
||||
qreal radius = grad.radius();
|
||||
@ -154,11 +155,11 @@ void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
|
||||
break;
|
||||
case QTabletEvent::RotationStylus:
|
||||
{
|
||||
myBrush.setStyle(Qt::SolidPattern);
|
||||
m_brush.setStyle(Qt::SolidPattern);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(myBrush);
|
||||
painter.setBrush(m_brush);
|
||||
QPolygonF poly;
|
||||
qreal halfWidth = myPen.widthF();
|
||||
qreal halfWidth = m_pen.widthF();
|
||||
QPointF brushAdjust(qSin(qDegreesToRadians(lastPoint.rotation)) * halfWidth,
|
||||
qCos(qDegreesToRadians(lastPoint.rotation)) * halfWidth);
|
||||
poly << lastPoint.pos + brushAdjust;
|
||||
@ -195,7 +196,7 @@ void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
|
||||
}
|
||||
// FALL-THROUGH
|
||||
case QTabletEvent::Stylus:
|
||||
painter.setPen(myPen);
|
||||
painter.setPen(m_pen);
|
||||
painter.drawLine(lastPoint.pos, event->posF());
|
||||
break;
|
||||
}
|
||||
@ -206,68 +207,69 @@ void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
|
||||
void TabletCanvas::updateBrush(const QTabletEvent *event)
|
||||
{
|
||||
int hue, saturation, value, alpha;
|
||||
myColor.getHsv(&hue, &saturation, &value, &alpha);
|
||||
m_color.getHsv(&hue, &saturation, &value, &alpha);
|
||||
|
||||
int vValue = int(((event->yTilt() + 60.0) / 120.0) * 255);
|
||||
int hValue = int(((event->xTilt() + 60.0) / 120.0) * 255);
|
||||
//! [7] //! [8]
|
||||
|
||||
switch (alphaChannelType) {
|
||||
case AlphaPressure:
|
||||
myColor.setAlphaF(event->pressure());
|
||||
switch (m_alphaChannelValuator) {
|
||||
case PressureValuator:
|
||||
m_color.setAlphaF(event->pressure());
|
||||
break;
|
||||
case AlphaTangentialPressure:
|
||||
case TangentialPressureValuator:
|
||||
if (event->device() == QTabletEvent::Airbrush)
|
||||
myColor.setAlphaF(qMax(0.01, (event->tangentialPressure() + 1.0) / 2.0));
|
||||
m_color.setAlphaF(qMax(0.01, (event->tangentialPressure() + 1.0) / 2.0));
|
||||
else
|
||||
myColor.setAlpha(255);
|
||||
m_color.setAlpha(255);
|
||||
break;
|
||||
case AlphaTilt:
|
||||
myColor.setAlpha(maximum(abs(vValue - 127), abs(hValue - 127)));
|
||||
case TiltValuator:
|
||||
m_color.setAlpha(maximum(abs(vValue - 127), abs(hValue - 127)));
|
||||
break;
|
||||
default:
|
||||
myColor.setAlpha(255);
|
||||
m_color.setAlpha(255);
|
||||
}
|
||||
|
||||
//! [8] //! [9]
|
||||
switch (colorSaturationType) {
|
||||
case SaturationVTilt:
|
||||
myColor.setHsv(hue, vValue, value, alpha);
|
||||
switch (m_colorSaturationValuator) {
|
||||
case VTiltValuator:
|
||||
m_color.setHsv(hue, vValue, value, alpha);
|
||||
break;
|
||||
case SaturationHTilt:
|
||||
myColor.setHsv(hue, hValue, value, alpha);
|
||||
case HTiltValuator:
|
||||
m_color.setHsv(hue, hValue, value, alpha);
|
||||
break;
|
||||
case SaturationPressure:
|
||||
myColor.setHsv(hue, int(event->pressure() * 255.0), value, alpha);
|
||||
case PressureValuator:
|
||||
m_color.setHsv(hue, int(event->pressure() * 255.0), value, alpha);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
//! [9] //! [10]
|
||||
switch (lineWidthType) {
|
||||
case LineWidthPressure:
|
||||
myPen.setWidthF(event->pressure() * 10 + 1);
|
||||
switch (m_lineWidthValuator) {
|
||||
case PressureValuator:
|
||||
m_pen.setWidthF(event->pressure() * 10 + 1);
|
||||
break;
|
||||
case LineWidthTilt:
|
||||
myPen.setWidthF(maximum(abs(vValue - 127), abs(hValue - 127)) / 12);
|
||||
case TiltValuator:
|
||||
m_pen.setWidthF(maximum(abs(vValue - 127), abs(hValue - 127)) / 12);
|
||||
break;
|
||||
default:
|
||||
myPen.setWidthF(1);
|
||||
m_pen.setWidthF(1);
|
||||
}
|
||||
|
||||
//! [10] //! [11]
|
||||
if (event->pointerType() == QTabletEvent::Eraser) {
|
||||
myBrush.setColor(Qt::white);
|
||||
myPen.setColor(Qt::white);
|
||||
myPen.setWidthF(event->pressure() * 10 + 1);
|
||||
m_brush.setColor(Qt::white);
|
||||
m_pen.setColor(Qt::white);
|
||||
m_pen.setWidthF(event->pressure() * 10 + 1);
|
||||
} else {
|
||||
myBrush.setColor(myColor);
|
||||
myPen.setColor(myColor);
|
||||
m_brush.setColor(m_color);
|
||||
m_pen.setColor(m_color);
|
||||
}
|
||||
}
|
||||
//! [11]
|
||||
|
||||
//! [12]
|
||||
void TabletCanvas::updateCursor(const QTabletEvent *event)
|
||||
{
|
||||
QCursor cursor;
|
||||
@ -285,7 +287,7 @@ void TabletCanvas::updateCursor(const QTabletEvent *event)
|
||||
case QTabletEvent::RotationStylus: {
|
||||
QImage origImg(QLatin1String(":/images/cursor-felt-marker.png"));
|
||||
QImage img(32, 32, QImage::Format_ARGB32);
|
||||
QColor solid = myColor;
|
||||
QColor solid = m_color;
|
||||
solid.setAlpha(255);
|
||||
img.fill(solid);
|
||||
QPainter painter(&img);
|
||||
@ -307,6 +309,7 @@ void TabletCanvas::updateCursor(const QTabletEvent *event)
|
||||
}
|
||||
setCursor(cursor);
|
||||
}
|
||||
//! [12]
|
||||
|
||||
void TabletCanvas::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
|
@ -61,27 +61,26 @@ class TabletCanvas : public QWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum AlphaChannelType { AlphaPressure, AlphaTangentialPressure, AlphaTilt, NoAlpha };
|
||||
enum ColorSaturationType { SaturationVTilt, SaturationHTilt,
|
||||
SaturationPressure, NoSaturation };
|
||||
enum LineWidthType { LineWidthPressure, LineWidthTilt, NoLineWidth };
|
||||
enum Valuator { PressureValuator, TangentialPressureValuator,
|
||||
TiltValuator, VTiltValuator, HTiltValuator, NoValuator };
|
||||
Q_ENUM(Valuator)
|
||||
|
||||
TabletCanvas();
|
||||
|
||||
bool saveImage(const QString &file);
|
||||
bool loadImage(const QString &file);
|
||||
void setAlphaChannelType(AlphaChannelType type)
|
||||
{ alphaChannelType = type; }
|
||||
void setColorSaturationType(ColorSaturationType type)
|
||||
{ colorSaturationType = type; }
|
||||
void setLineWidthType(LineWidthType type)
|
||||
{ lineWidthType = type; }
|
||||
void setColor(const QColor &color)
|
||||
{ myColor = color; }
|
||||
void setAlphaChannelValuator(Valuator type)
|
||||
{ m_alphaChannelValuator = type; }
|
||||
void setColorSaturationValuator(Valuator type)
|
||||
{ m_colorSaturationValuator = type; }
|
||||
void setLineWidthType(Valuator type)
|
||||
{ m_lineWidthValuator = type; }
|
||||
void setColor(const QColor &c)
|
||||
{ if (c.isValid()) m_color = c; }
|
||||
QColor color() const
|
||||
{ return myColor; }
|
||||
{ return m_color; }
|
||||
void setTabletDevice(QTabletEvent *event)
|
||||
{ myTabletDevice = event->device(); updateCursor(event); }
|
||||
{ updateCursor(event); }
|
||||
int maximum(int a, int b)
|
||||
{ return a > b ? a : b; }
|
||||
|
||||
@ -97,17 +96,14 @@ private:
|
||||
void updateBrush(const QTabletEvent *event);
|
||||
void updateCursor(const QTabletEvent *event);
|
||||
|
||||
AlphaChannelType alphaChannelType;
|
||||
ColorSaturationType colorSaturationType;
|
||||
LineWidthType lineWidthType;
|
||||
QTabletEvent::PointerType pointerType;
|
||||
QTabletEvent::TabletDevice myTabletDevice;
|
||||
QColor myColor;
|
||||
|
||||
QPixmap pixmap;
|
||||
QBrush myBrush;
|
||||
QPen myPen;
|
||||
bool deviceDown;
|
||||
Valuator m_alphaChannelValuator;
|
||||
Valuator m_colorSaturationValuator;
|
||||
Valuator m_lineWidthValuator;
|
||||
QColor m_color;
|
||||
QPixmap m_pixmap;
|
||||
QBrush m_brush;
|
||||
QPen m_pen;
|
||||
bool m_deviceDown;
|
||||
|
||||
struct Point {
|
||||
QPointF pos;
|
||||
|
Loading…
Reference in New Issue
Block a user