1. Qt4中的2D绘图部分称为Arthur绘图。它由3个主要的类支撑起整个框架:
(1) QPainter:用来执行具体的绘画操作。
(2) QPaintDevice:是QPainter用来绘图的绘图设备。
(3) QPaintEngine:提供不同类型设备的接口。
2. Qpainter类能够绘制:
(1) 基本的图形:点、线、矩形、多边形等。
(2) 复杂的图形:如绘图路径。
3. 使用绘图路径(QpaintPath)的优点是复杂形状的图形只用生成一次,以后再绘制时只需要调用QPainter::drawPath()就可以了。QPaintPath对象可以用来填充、绘制轮廓。
4. 线和轮廓使用画笔(QPen)进行绘制,画刷(QBrush)进行填充。
5. 画笔的属性包括:
(1) 画笔风格(PenStyle):它在在Qt中使用Qt::PenStyle定义了6种画笔风格,分别为:
a. Qt::SolidLine
b. Qt::DashLine
c. Qt::DotLine
d. Qt::DashDotLine
e. Qt::DashDotDotLine
f. Qt::CustomDashLine
默认为Qt::SolidLine。还有一种风格为Qt::NoPen,使用它时QPainter不绘制线。
若要使用自定义风格的线风格(Qt::CustomDashLine),则需要使用QPen的setDashPattern()函数来实现自定义风格。
(2) 线宽(Width)
(3) 颜色(color)
(4) 端点风格(CapStyle):它决定了线的端点样式,但只对线宽大于等于1的线有效,对装饰笔绘制的线无效。用枚举类型Qt::PenCapStyle表示,分别为以下三种:
a. Qt::SquareCap
b. ,Qt::FlatCap
c. Qt::RoundCap。
(5) 连接风格(JoinStyle):它指两条线如何连接,仅对线宽大于等于1的线有效,对装饰笔绘制的线无效。Qt定义了4种连接方式,用枚举类型Qt::PenStyle表示,分别为:
a. Qt::MiterJoin
b. Qt::bevelJoin
c. Qt::RoundJoin
d. Qt::SvgMiterJoin
6. penWidthSpinBox->setSpecialValueText(tr("0 (cosmetic pen)"));
setSpecialValueText()函数是QSpinBox类的特殊用法,用来在QSpinBox中显示文字而不是默认的数值。
7. 画刷的属性包括:
(1) 填充颜色:在Qt中颜色使用QColor类表示。它支持RGB,HSV,CMYK颜色模型。QColor还支持alpha混合的轮廓和填充(可以实现透明效果),QColor类与平台、设备无关(通过QColormap类与硬件进行映射)。它还可以使用SVG 1.0中定义的任何颜色名为参数初始化。
(2) 填充模式(风格):它由Qt::BrushStyle枚举变量定义,包括以下几种填充模式:
a. 基本模式填充:包括各种点、线组合的模式。
b. 渐变模式填充
c. 纹理填充
8. Qt4还提供了渐变填充的画刷。渐变填充包括两个要素:颜色的变化以及路径的变化。在Qt中指定了三种渐变填充,它们都是从QGradient类继承而来。三种填充渐变如下:
(1) 线性渐变(QLinearGradient):起点(x,y),终点(x1,y1)
(2) 圆形渐变(QRadicalGradient):圆心(x,y),半径,焦点(x1,y1)
(3) 圆锥渐变(QConicalGradient):圆心(x,y),开始角
9. 双缓冲绘图:在绘图过程中,一个缓冲区绘制临时内容,一个缓冲区保存绘制好的内容,最后进行合并。在交互绘制过程中,程序将图像缓冲区复制到临时缓冲区,并在临时缓冲区上绘制,绘制完毕再将结果复制到图像缓冲区。
在Qt4种,所有窗口部件默认都是用双缓冲进行绘图,可以减轻绘制的闪烁感。
2D绘图实例:
1、用qt creator创建一个名为basicdraw的empty qt project工程;
2、在工程中添加相应文件,代码如下:
palette.h
#ifndef PALETTE_H
#define PALETTE_H
#include<QtGui>
#include "previewlabel.h"
#include "qpenstydelegate.h"
class QLabel;
class QSpinBox;
class QComboBox;
class Palette:public QWidget
{
Q_OBJECT
public:
Palette(QWidget *parent=0);
signals:
void penChanged(QPen& pen);
void brushChanged(QBrush& brush);
private slots:
void penChanged();
void brushChanged();
private:
QLabel *penColorLabel;//画笔颜色
QLabel *penWidthLabel;//画笔线宽
QLabel *penStyleLabel;//画笔风格
QLabel *brushColorLabel;//画刷颜色
QLabel *brushStyleLabel;//画刷风格
PreviewLabel *preLabel;//预览取
QSpinBox *penWidthSpinBox;
QComboBox *penColorComboBox;
QComboBox *penStyleComboBox;
QComboBox *brushColorComboBox;
QComboBox *brushStyleComboBox;
void createColorComboBox(QComboBox *comboBox);
void createStyleComboBox();
};
#endif // PALETTE_H
palette.cpp
#include "palette.h"
Palette::Palette(QWidget *parent)
:QWidget(parent)
{
//画笔颜色
penColorComboBox=new QComboBox;
createColorComboBox(penColorComboBox);
penColorLabel=new QLabel(tr("Pen Color:"));
penColorLabel->setBuddy(penColorComboBox);
//画笔线宽
penWidthSpinBox=new QSpinBox;
penWidthSpinBox->setRange(0,20);
//设置线宽初始值
penWidthSpinBox->setSpecialValueText(tr("0(cosmetic pen)"));
penWidthLabel=new QLabel(tr("pen &width"));
penWidthLabel->setBuddy(penWidthSpinBox);
//画笔样式
createStyleComboBox();
penStyleLabel=new QLabel(tr("&pen Style:"));
penStyleLabel->setBuddy(penStyleComboBox);
//画刷颜色
brushColorComboBox=new QComboBox;
createColorComboBox(brushColorComboBox);
brushColorLabel=new QLabel(tr("Brush Color:"));
brushColorLabel->setBuddy(brushColorComboBox);
//画刷填充样式
brushStyleComboBox=new QComboBox;
brushStyleComboBox->addItem(tr("None"),Qt::NoBrush);
brushStyleComboBox->addItem(tr("Linear Gradient"),Qt::LinearGradientPattern);
brushStyleComboBox->addItem(tr("Radial Gradient"),Qt::RadialGradientPattern);
brushStyleComboBox->addItem(tr("Conical Gradient"),Qt::ConicalGradientPattern);
brushStyleComboBox->addItem(tr("Texture"),Qt::TexturePattern);
brushStyleLabel=new QLabel(tr("&Brush Style:"));
brushStyleLabel->setBuddy(brushStyleComboBox);
//预览区
preLabel=new PreviewLabel(this);
//连接信号与槽
connect(penColorComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(penChanged()));
connect(penWidthSpinBox,SIGNAL(valueChanged(int)),this,SLOT(brushChanged()));
connect(penStyleComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(penChanged()));
connect(brushColorComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(brushChanged()));
connect(brushStyleComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(brushChanged()));
connect(this,SIGNAL(penChanged(QPen&)),preLabel,SLOT(penChanged(QPen&)));
connect(this,SIGNAL(brushChanged(QBrush&)),preLabel,SLOT(brushChanged(QBrush&)));
//布局
QGridLayout *mainLayout=new QGridLayout;
mainLayout->addWidget(penColorLabel,0,0,Qt::AlignRight);
mainLayout->addWidget(penColorComboBox,0,1);
mainLayout->addWidget(penWidthLabel,1,0,Qt::AlignRight);
mainLayout->addWidget(penWidthSpinBox,1,1);
mainLayout->addWidget(penStyleLabel,2,0,Qt::AlignRight);
mainLayout->addWidget(penStyleComboBox,2,1);
mainLayout->addWidget(brushStyleLabel,3,0,Qt::AlignRight);
mainLayout->addWidget(brushStyleComboBox,3,1);
mainLayout->addWidget(brushColorLabel,4,0,Qt::AlignRight);
mainLayout->addWidget(brushColorComboBox,4,1);
mainLayout->addWidget(preLabel,5,0,6,2);
setLayout(mainLayout);
penChanged();
brushChanged();
setWindowTitle(tr("Basic Drawing"));
}
void Palette::penChanged()
{
QPen pen;
int width=penWidthSpinBox->value();
pen.setWidth(width);
QColor color=penColorComboBox->itemData(
penColorComboBox->currentIndex(),Qt::UserRole).value<QColor>();
pen.setColor(color);
Qt::PenStyle penStyle=(Qt::PenStyle)penStyleComboBox->itemData(
penStyleComboBox->currentIndex(),Qt::UserRole).toInt();
pen.setStyle(penStyle);
emit penChanged(pen);
}
void Palette::createColorComboBox(QComboBox *comboBox)
{
QPixmap pix(16,16);
QPainter pt(&pix);
pt.fillRect(0,0,16,16,Qt::black);
comboBox->addItem(QIcon(pix),tr("black"),Qt::black);
pt.fillRect(0,0,16,16,Qt::red);
comboBox->addItem(QIcon(pix),tr("red"),Qt::red);
pt.fillRect(0,0,16,15,Qt::green);
comboBox->addItem(QIcon(pix),tr("green"),Qt::green);
pt.fillRect(0,0,16,16,Qt::blue);
comboBox->addItem(QIcon(pix),tr("blue"),Qt::blue);
pt.fillRect(0,0,16,16,Qt::yellow);
comboBox->addItem(QIcon(pix),tr("yellow"),Qt::yellow);
pt.fillRect(0,0,16,16,Qt::cyan);
comboBox->addItem(QIcon(pix),tr("cyan"),Qt::cyan);
pt.fillRect(0,0,16,16,Qt::magenta);
comboBox->addItem(QIcon(pix),tr("magenta"),Qt::magenta);
}
void Palette::createStyleComboBox()
{
penStyleComboBox=new QComboBox;
penStyleComboBox->setItemDelegate(
new QPenStyDelegate((QObject *)penStyleComboBox));
penStyleComboBox->addItem(tr("Solid"),Qt::SolidLine);
penStyleComboBox->addItem(tr("Dash"),Qt::DashLine);
penStyleComboBox->addItem(tr("Dot"),Qt::DotLine);
penStyleComboBox->addItem(tr("Dash Dot"),Qt::DashDotLine);
penStyleComboBox->addItem(tr("Dash Dot DOt"),Qt::DashDotDotLine);
penStyleComboBox->addItem(tr("None"),Qt::NoPen);
}
void Palette::brushChanged()
{
QBrush brush;
\
QColor color=brushColorComboBox->itemData(
brushColorComboBox->currentIndex(),
Qt::UserRole).value<QColor>();
Qt::BrushStyle style=Qt::BrushStyle(brushStyleComboBox->itemData(
brushStyleComboBox->currentIndex(),
Qt::UserRole).toInt());
if(style==Qt::LinearGradientPattern)
{
QLinearGradient linearGradient(0,0,100,100);
linearGradient.setColorAt(0.0,Qt::white);
linearGradient.setColorAt(0.2,Qt::green);
linearGradient.setColorAt(1.0,Qt::black);
brush=linearGradient;
}
else if(style==Qt::RadialGradientPattern)
{
QRadialGradient radialGradient(50,50,50,70,70);
radialGradient.setColorAt(0.0,Qt::white);
radialGradient.setColorAt(0.2,Qt::green);
radialGradient.setColorAt(1.0,Qt::black);
brush=radialGradient;
}
else if(style==Qt::ConicalGradientPattern)
{
QConicalGradient conicalGradient(50,50,150);
conicalGradient.setColorAt(0.0,Qt::white);
conicalGradient.setColorAt(0.2,Qt::green);
conicalGradient.setColorAt(1.0,Qt::black);
brush=conicalGradient;
}
else if(style==Qt::TexturePattern)
{
brush=QBrush(QPixmap(":/images/ellipse.png"));
}
else
{
brush.setColor(color);
brush.setStyle(style);
}
emit brushChanged(brush);
}
previewlabel.h
#ifndef PREVIEWLABEL_H
#define PREVIEWLABEL_H
#include<QtGui>
class PreviewLabel:public QLabel
{
Q_OBJECT
public:
PreviewLabel(QWidget *parent=0);
public slots:
void penChanged(QPen& pen);
void brushChanged(QBrush& brush);
protected:
void paintEvent(QPaintEvent *event);
private:
QPen curPen;
QBrush curBrush;
};
#endif // PREVIEWLABEL_H
previewlabel.cpp
#include "previewlabel.h"
PreviewLabel::PreviewLabel(QWidget *parent)
:QLabel(parent)
{
}
void PreviewLabel::penChanged(QPen &pen)
{
curPen=pen;
update();
}
void PreviewLabel::brushChanged(QBrush &brush)
{
curBrush=brush;
update();
}
void PreviewLabel::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setPen(curPen);
painter.setBrush(curBrush);
painter.drawRect(rect().x()+10,rect().y()+10,rect().width()-20,rect().height()-20);
}
qpenstydelegate.h
#ifndef QPENSTYDELEGATE_H
#define QPENSTYDELEGATE_H
#include<QtGui>
class QPenStyDelegate:public QAbstractItemDelegate
{
Q_OBJECT
public:
QPenStyDelegate(QObject *parent=0);
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const;
};
#endif // QPENSTYDELEGATE_H
qpenstydelegate.cpp
#include "qpenstydelegate.h"
QPenStyDelegate::QPenStyDelegate(QObject *parent)
:QAbstractItemDelegate(parent)
{
}
void QPenStyDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index)const
{
QString text=index.data(Qt::DisplayPropertyRole).toString();
Qt::PenStyle penStyle=(Qt::PenStyle)index.data(Qt::UserRole).toInt();
QRect r=option.rect;
if(option.state & QStyle::State_Selected)
{
painter->save();
painter->setBrush(option.palette.highlight());
painter->setPen(Qt::NoPen);
painter->drawRect(option.rect);
painter->setPen(QPen(option.palette.highlightedText(),2,penStyle));
}
else
painter->setPen(penStyle);
painter->drawLine(0,r.y()+r.height()/2,
r.right(),r.y()+r.height()/2);
if(option.state & QStyle::State_Selected)
painter->restore();
}
QSize QPenStyDelegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
return QSize(100,30);
}
form.h
#ifndef FORM_H
#define FORM_H
#include<QtGui>
class Form:public QWidget
{
Q_OBJECT
public:
Form(QWidget *parent=0);
enum ShapeType
{
Line,
Polyline,
Rectangle,
Polygone,
Arc,
Pie,
Chord,
Ellipse,
Text
};
void setShape(ShapeType shape);
public slots:
void fontChanged(const QFont& font);
void penChanged(QPen& pen);
void brushChanged(QBrush& brush);
protected:
bool bDrawing;
int x,y,w,h;
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
private:
QImage bufferImage; //绘图缓冲区
QImage tempImage; //临时缓冲区
ShapeType curShape; //当前图形种类
QPen curPen; //当前画笔
QBrush curBrush; //当前画刷
QFont textFont; //字体
int thickness;
void paint(QImage& image);
};
#endif // FORM_H
form.cpp
#include "form.h"
Form::Form(QWidget *parent)
:QWidget(parent)
{
//关闭窗口部件的双缓冲
setAttribute(Qt::WA_NoBackground);
bDrawing=false;
curShape=Ellipse;
resize(800,600);
bufferImage=QImage(width(),height(),
QImage::Format_ARGB32_Premultiplied);
bufferImage.fill(qRgb(255,255,255));
tempImage=QImage(width(),height(),
QImage::Format_ARGB32_Premultiplied);
}
//设置当前绘图的图形种类
void Form::setShape(ShapeType shape)
{
curShape=shape;
}
//当鼠标按下时,进入交互绘图状态
void Form::mousePressEvent(QMouseEvent *event)
{
bDrawing=true;
x=event->x();
y=event->y();
}
//当鼠标移动时将图形缓冲区中的内容复制到临时缓冲区,并绘制临时的反馈图形
void Form::mouseMoveEvent(QMouseEvent *event)
{
w=event->x()-x;
h=event->y()-y;
tempImage=bufferImage;
paint(tempImage);
}
//当鼠标释放时,在绘图缓冲区上绘制图形
void Form::mouseReleaseEvent(QMouseEvent *event)
{
bDrawing=false;
paint(bufferImage);
}
//根据当前是否在绘图,在不同的缓冲区上绘制图形
void Form::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
if(bDrawing)
painter.drawImage(QPoint(0,0),tempImage);
else
painter.drawImage(QPoint(0,0),bufferImage);
}
//实际的绘图函数,实现不同形状的绘图
void Form::paint(QImage &image)
{
QPainter painter(&image);
painter.setPen(curPen);
painter.setBrush(curBrush);
switch(curShape)
{
case Rectangle:
painter.drawRect(x,y,w,h);
break;
case Ellipse:
painter.drawEllipse(x,y,w,h);
break;
case Text:
QFontMetrics metrics(textFont);
QRect rect=metrics.boundingRect(textFont.family());
painter.setFont(textFont);
painter.translate(x,y);
painter.scale(w/rect.width(),h/rect.height());
painter.drawText(0,rect.height(),textFont.family());
break;
}
update();
}
//绘图要素的改变
void Form::fontChanged(const QFont &font)
{
textFont=font;
}
void Form::penChanged(QPen &pen)
{
curPen=pen;
}
void Form::brushChanged(QBrush &brush)
{
curBrush=brush;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include<QMainWindow>
#include<QActionGroup>
#include<QFontComboBox>
#include"form.h"
#include"palette.h"
class MainWindow:public QMainWindow
{
Q_OBJECT
public:
MainWindow();
private slots:
void draw(QAction* action);//绘制当前图形的种类
private:
void createActions();
void createMenus();
void createToolBars();
void createStatusBar();
void CreateDockWindows();
Palette *paletteWidget;
Form *form;
QMenu *drawMenu;
QToolBar *drawToolBar;
QFontComboBox *fontCmb;
//不同图形的QAction
QAction *rectangleAct;
QAction *ellipseAct;
QAction *textAct;
QActionGroup *drawActGroup;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include<QtGui>
MainWindow::MainWindow()
{
resize(800,600);
form=new Form(this);
setCentralWidget(form);
createActions();
createMenus();
createToolBars();
createStatusBar();
CreateDockWindows();
}
//绘制当前图形的种类
void MainWindow::draw(QAction* action)
{
if(action==rectangleAct)
form->setShape(Form::Rectangle);
if(action==ellipseAct)
form->setShape(Form::Ellipse);
else if(action==textAct)
form->setShape(Form::Text);
}
//创建不同图形的QAction,并将所有绘图的QAction归到一个QActionGroup中
void MainWindow::createActions()
{
rectangleAct=new QAction(QIcon(":/images/rectangle.png"),
tr("&rectangle"),this);
rectangleAct->setCheckable(true);
ellipseAct=new QAction(QIcon(":/images/ellipse.png"),
tr("&Ellipse"),this);
ellipseAct->setCheckable(true);
ellipseAct->setCheckable(true);
textAct=new QAction(QIcon(":/images/text.png"),
tr("&Text"),this);
textAct->setCheckable(true);
drawActGroup=new QActionGroup(this);
drawActGroup->addAction(rectangleAct);
drawActGroup->addAction(ellipseAct);
drawActGroup->addAction(textAct);
connect(drawActGroup,SIGNAL(triggered(QAction*)),this,
SLOT(draw(QAction*)));
}
//创建菜单
void MainWindow::createMenus()
{
drawMenu=menuBar()->addMenu(tr("&Draw"));
drawMenu->addAction(rectangleAct);
drawMenu->addAction(ellipseAct);
drawMenu->addAction(textAct);
}
//创建工具条
void MainWindow::createToolBars()
{
drawToolBar=addToolBar(tr("Draw"));
drawToolBar->addAction(rectangleAct);
drawToolBar->addAction(ellipseAct);
drawToolBar->addSeparator();//
drawToolBar->addAction(textAct);
fontCmb=new QFontComboBox(drawToolBar);
drawToolBar->addWidget(fontCmb);
connect(fontCmb,SIGNAL(currentFontChanged(const QFont&)),form,
SLOT(fontChanged(const QFont&)));
fontCmb->setCurrentFont(font());
}
//创建状态条
void MainWindow::createStatusBar()
{
statusBar()->showMessage(tr("Ready"));
}
//创建调色板
void MainWindow::CreateDockWindows()
{
QDockWidget *dock=new QDockWidget(tr("Palette"),this);
dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
paletteWidget=new Palette(dock);
dock->setWidget(paletteWidget);
addDockWidget(Qt::LeftDockWidgetArea,dock);
connect(paletteWidget,SIGNAL(penChanged(QPen&)),form,
SLOT(penChanged(QPen&)));
connect(paletteWidget,SIGNAL(brushChanged(QBrush&)),form,
SLOT(brushChanged(QBrush&)));
}
main.cpp
#include<QApplication>
#include<QtGui>
#include<QTextCodec>
#include"mainwindow.h"
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
MainWindow mainwindow;
mainwindow.show();
return app.exec();
}
注:还要新建一个名为"images"的资源文件,添加相应图片资源。