Qt图形与图片(Qt位置相关函数、Qt基础图形的绘制、双缓冲机制、显示SVG格式图片)

此篇文章介绍几种主要位置函数及其之间的区别,以及各种与位置相关函数的使用场合;然后,通过一个简单绘图工具实例,介绍利用QPainter和QPainterPath两种方法绘制各种基础图形;最后,通过几个实例介绍如何利用这些基础的图形来绘制更复杂的图形。

Qt位置相关函数

区别概念

Qt提供了很多关于获取窗体位置及显示区域大小的函数,如x()、y()和pos()、rect()、size()、geometry()等,统称为“位置相关函数”或“位置函数”。几种主要位置函数及其之间的区别如下所示。
在这里插入图片描述
其中,

  • x()、y()和pos()函数的作用都是获得整个窗体左上角的坐标位置。
  • frameGeometry()函数与geometry ()函数相对应。frameGeometry()函数或获得的是真个窗体的左顶点和长、宽值,而geometry ()函数获得的是窗体内中央区域的左上顶点坐标及长、宽值。
  • 直接调用width()和height()函数获得的是中央区域的长、宽值。
  • rect()、size()函数获得的的结果也都是相对于窗体的中央区域而言的。size()函数获得的是窗体得的长、宽值是相同的,都是窗体中央区域的长、宽值,rect()函数与geometry ()函数相同,返回一个QRect对象,,这两个函数获得的长、宽值是相同的,都是窗体中央区域的长、宽值,只是左上顶点的坐标值不一样。geometry ()函数获得的左上顶点坐标是相对于父窗体而言的坐标,而rect()函数获得的左上顶点坐标始终为(0,0)。

在实际应用中,需要根据情况使用正确的位置信息函数以获得准确的位置尺寸信息,尤其是在编写对位置精度要求较高的程序(如地图浏览程序)时,更应该注意函数的选择,以避免产生不必要的误差。

“实例”位置函数的应用

本节通过一个简单的例子介绍QWidget提供的x()、y()、frameGeometry()、pos()、rect()、size()和geometry()等函数的使用场合。
创建一个Geometry项目,当改变对话框的大小或移动对话框时,调用各个函数所获得的信息也相应地发生变化,从变化中可得知各函数之间的区别,效果如下图所示。
在这里插入图片描述
布局代码如下:

Geometry::Geometry(QWidget *parent)
    : QDialog(parent)
{
    setWindowTitle(tr("Geometry"));
    xLabel =new QLabel(tr("x():"));
    xValueLabel =new QLabel;
    yLabel =new QLabel(tr("y():"));
    yValueLabel =new QLabel;
    FrmLabel =new QLabel(tr("Frame:"));
    FrmValueLabel =new QLabel;
    posLabel =new QLabel(tr("pos():"));
    posValueLabel =new QLabel;
    geoLabel =new QLabel(tr("geometry():"));
    geoValueLabel =new QLabel;
    widthLabel =new QLabel(tr("width():"));
    widthValueLabel =new QLabel;
    heightLabel =new QLabel(tr("height():"));
    heightValueLabel =new QLabel;
    rectLabel =new QLabel(tr("rect():"));
    rectValueLabel =new QLabel;
    sizeLabel =new QLabel(tr("size():"));
    sizeValueLabel =new QLabel;
    mainLayout =new QGridLayout(this);
    mainLayout->addWidget(xLabel,0,0);
    mainLayout->addWidget(xValueLabel,0,1);
    mainLayout->addWidget(yLabel,1,0);
    mainLayout->addWidget(yValueLabel,1,1);
    mainLayout->addWidget(posLabel,2,0);
    mainLayout->addWidget(posValueLabel,2,1);
    mainLayout->addWidget(FrmLabel,3,0);
    mainLayout->addWidget(FrmValueLabel,3,1);
    mainLayout->addWidget(geoLabel,4,0);
    mainLayout->addWidget(geoValueLabel,4,1);
    mainLayout->addWidget(widthLabel,5,0);
    mainLayout->addWidget(widthValueLabel,5,1);
    mainLayout->addWidget(heightLabel,6,0);
    mainLayout->addWidget(heightValueLabel,6,1);
    mainLayout->addWidget(rectLabel,7,0);
    mainLayout->addWidget(rectValueLabel,7,1);
    mainLayout->addWidget(sizeLabel,8,0);
    mainLayout->addWidget(sizeValueLabel,8,1);
    updateLabel();
}

页面数据刷新updateLabel函数逻辑如下:

void Geometry::updateLabel()
{
    QString xStr;                      	//获得x()函数的结果并显示
    xValueLabel->setText(xStr.setNum(x()));
    QString yStr;                      	//获得y()函数的结果并显示
    yValueLabel->setText(yStr.setNum(y()));
    QString frameStr;                   //获得frameGeometry()函数的结果并显示
    QString tempStr1,tempStr2,tempStr3,tempStr4;
    frameStr = tempStr1.setNum(frameGeometry().x())+","+
         tempStr2.setNum(frameGeometry().y())+","+
         tempStr3.setNum(frameGeometry().width())+","+
        tempStr4.setNum(frameGeometry().height());
    FrmValueLabel->setText(frameStr);
    QString positionStr;            	//获得pos()函数的结果并显示
    QString tempStr11,tempStr12;
    positionStr =tempStr11.setNum(pos().x())+","+
            tempStr12.setNum(pos().y());
    posValueLabel->setText(positionStr);
    QString geoStr;               		//获得geometry()函数的结果并显示
    QString tempStr21,tempStr22,tempStr23,tempStr24;
    geoStr =tempStr21.setNum(geometry().x())+","+
         tempStr22.setNum(geometry().y())+","+
         tempStr23.setNum(geometry().width())+","+
         tempStr24.setNum(geometry().height());
    geoValueLabel->setText(geoStr);
    QString wStr,hStr;                  //获得width()、height()函数的结果并显示
    widthValueLabel->setText(wStr.setNum(width()));
    heightValueLabel->setText(hStr.setNum(height()));
    QString rectStr;                    //获得rect()函数的结果并显示
    QString tempStr31,tempStr32,tempStr33,tempStr34;
    rectStr =tempStr31.setNum(rect().x())+","+
          tempStr32.setNum(rect().y())+","+
          tempStr33.setNum(/*rect().width()*/width())+","+
          tempStr34.setNum(height()/*rect().height()*/);
    rectValueLabel->setText(rectStr);
    QString sizeStr;                    //获得size()函数的结果并显示
    QString tempStr41,tempStr42;
    sizeStr =tempStr41.setNum(size().width())+","+
          tempStr42.setNum(size().height());
    sizeValueLabel->setText(sizeStr);
}

重新定义QWidget的moveEvent(QMoveEvent *event)函数,响应对话框的移动事件,使得窗体在被动时能够同步更新各函数的显示结果,具体代码如下:

void Geometry::moveEvent(QMoveEvent *)
{
    updateLabel();
}

重新定义QWidget的resizeEvent(QResizeEvent *event)函数,响应对话框的大小调整事件,使得在窗体大小发生变化时,也能够同步更新各函数的显示结果,具体代码如下:

void Geometry::resizeEvent(QResizeEvent *)
{
    updateLabel();
}

Qt基础图形的绘制

创建一个PaintEx项目,在设计页面区分各种形状及画笔颜色、画笔线宽、画笔风格、画笔顶帽、画笔连接点、填充模式、铺展效果、画刷颜色、画刷风格设置等。

绘图框架设计

利用QPainter绘制各种图形使用的框架的实例如下图所示。
在这里插入图片描述
此实例的具体实现包含两个部分的内容:左侧是用于画图的区域PaintArea类,右侧是主窗口MainWidget类。
程序中,首先在PaintArea类中完成各种图形显示功能的Widget,重绘paintEvent函数。然后在主窗口MainWidget类中完成各种图形参数的选择。

绘图区的实现

PaintArea类继承自QWidget类,在类声明中,首先声明一个枚举型数据Shape,列举了所有本实例可能用到的图形形状;其次声明setShape()函数用于设置形状,setPen()函数用于设置画笔,setBrush()函数用于设置画刷,setFillRule()函数用于设置填充模式,以及重绘事件paintEvent()函数;最后声明表示各种属性的私有变量。
PaintArea类的构造函数用于完成初始化工作,设置图形显示区域的背景色及最小显示尺寸,具体代码如下:

PaintArea::PaintArea(QWidget *parent) : QWidget(parent)
{
    setPalette(QPalette(Qt::white));
    setAutoFillBackground(true);
    setMinimumSize(400,400);
}

其中,setPalette(QPalette(Qt::white))、setAutoFillBackground(true)完成对窗体背景色的设置,与下面的代码效果一致:

QPalette p = palette();
p.setColor(QPalette::window, Qt::white);
setPalette(p);

setShape()函数可以设置形状,setPen()函数可以涉资画笔,setBrush()函数可以设置画刷,setFillRule函数可以设置填充模式,具体代码如下:

void PaintArea::setShape(Shape s)
{
    shape = s;
    update();
}
void PaintArea::setPen(QPen p)
{
    pen = p;
    update();
}
void PaintArea::setBrush(QBrush b)
{
    brush = b;
    update();
}
void PaintArea::setFillRule(Qt::FillRule rule)
{
    fillRule =rule;
    update();     //重画绘制区窗体
}

PaintArea类的重绘函数代码如下:

void PaintArea::paintEvent(QPaintEvent *)
{
    QPainter p(this);						//新建一个QPainter对象
    p.setPen(pen);							//设置QPainter对象的画笔
    p.setBrush(brush);						//设置QPainter对象的画刷
    QRect rect(50,100,300,200);
    static const QPoint points[4]=
    {
        QPoint(150,100),
        QPoint(300,150),
        QPoint(350,250),
        QPoint(100,300)
    };
    int startAngle = 30*16;	
    int spanAngle = 120*16;
    QPainterPath path;                      //新建一个QPainterPath对象为画路径做准备
    path.addRect(150,150,100,100);
    path.moveTo(100,100);
    path.cubicTo(300,100,200,200,300,300);
    path.cubicTo(100,300,200,200,100,100);
    path.setFillRule(fillRule);
    switch(shape)
    {
        case Line:                          //直线
            p.drawLine(rect.topLeft(),rect.bottomRight());break;
        case Rectangle:                     //长方形
            p.drawRect(rect);	break;
        case RoundRect:                     //圆角方形
            p.drawRoundedRect(rect, 20.0, 20.0); 	break;
        case Ellipse:                       //椭圆形
            p.drawEllipse(rect); 	break;
        case Polygon:                       //多边形
            p.drawPolygon(points,4); 	break;
        case Polyline:                      //多边线
            p.drawPolyline(points,4); 	break;
        case Points:                        //点
            p.drawPoints(points,4); 	break;
        case Arc:                            //弧
            p.drawArc(rect,startAngle,spanAngle);	 break;
        case Path:                           //路径
            p.drawPath(path); 	break;
        case Text:                           //文字
            p.drawText(rect,Qt::AlignCenter,tr("Hello Qt!"));break;
        case Pixmap:                         //图片
            p.drawPixmap(150,150,QPixmap("butterfly.png")); 	break;
        default: 	break;
    }
}
  • QRect rect(50,100,300,200):设定一个方形区域,为画正方形、圆角正方形、椭圆等做准备。
  • static const QPoint points[4]={…}:创建一个QPoint数组,包含4个点,为画多边形、多边形及点做准备。
  • int startAngle =3016:、int spanAngle = 12016:其中,startAngle 代表起始角,为弧形的起始点与圆心之间连线与水平方向的夹角;参数spanAngle表示的是跨度角,为弧形起点、终点分别于圆心连线之间的夹角(用QPainter画弧形所使用的角度值,是以1/16°为单位,在画弧时即1°用16表示。)

主窗口的实现

界面布局代码如下:

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    paintArea =new PaintArea;
    shapeLabel =new QLabel(QStringLiteral("形状:"));            //形状选择下拉列表框
    shapeComboBox =new QComboBox;
    shapeComboBox->addItem(QStringLiteral("Line"),PaintArea::Line);
    shapeComboBox->addItem(QStringLiteral("Rectangle"),PaintArea::Rectangle);
    shapeComboBox->addItem(QStringLiteral("RoundedRect"),PaintArea::RoundRect);
    shapeComboBox->addItem(QStringLiteral("Ellipse"),PaintArea::Ellipse);
    shapeComboBox->addItem(QStringLiteral("Polygon"),PaintArea::Polygon);
    shapeComboBox->addItem(QStringLiteral("Polyline"),PaintArea::Polyline);
    shapeComboBox->addItem(QStringLiteral("Points"),PaintArea::Points);
    shapeComboBox->addItem(QStringLiteral("Arc"),PaintArea::Arc);
    shapeComboBox->addItem(QStringLiteral("Path"),PaintArea::Path);
    shapeComboBox->addItem(QStringLiteral("Text"),PaintArea::Text);
    shapeComboBox->addItem(QStringLiteral("Pixmap"),PaintArea::Pixmap);
    connect(shapeComboBox,SIGNAL(activated(int)),this,SLOT(ShowShape (int)));
    penColorLabel =new QLabel(QStringLiteral("画笔颜色:"));      //画笔颜色选择控件
    penColorFrame =new QFrame;
    penColorFrame->setFrameStyle(QFrame::Panel|QFrame::Sunken);
    penColorFrame->setAutoFillBackground(true);
    penColorFrame->setPalette(QPalette(Qt::blue));
    penColorBtn =new QPushButton(QStringLiteral("更改"));
    connect(penColorBtn,SIGNAL(clicked()),this,SLOT(ShowPenColor()));
    penWidthLabel =new QLabel(QStringLiteral("画笔线宽:"));      //画笔线宽选择控件
    penWidthSpinBox =new QSpinBox;
    penWidthSpinBox->setRange(0,20);
    connect(penWidthSpinBox,SIGNAL(valueChanged(int)),this,SLOT (ShowPenWidth(int)));
    penStyleLabel =new QLabel(QStringLiteral("画笔风格:"));      //画笔风格选择下拉列表框
    penStyleComboBox =new QComboBox;
    penStyleComboBox->addItem(QStringLiteral("SolidLine"),
                                    static_cast<int>(Qt::SolidLine));
    penStyleComboBox->addItem(QStringLiteral("DashLine"),
                                    static_cast<int>(Qt::DashLine));
    penStyleComboBox->addItem(QStringLiteral("DotLine"),
                                    static_cast<int>(Qt::DotLine));
    penStyleComboBox->addItem(QStringLiteral("DashDotLine"),
                                    static_cast<int>(Qt::DashDotLine));
    penStyleComboBox->addItem(QStringLiteral("DashDotDotLine"),
                                static_cast<int>(Qt::DashDotDotLine));
    penStyleComboBox->addItem(QStringLiteral("CustomDashLine"),
                                static_cast<int>(Qt::CustomDashLine));
    connect(penStyleComboBox,SIGNAL(activated(int)),this,SLOT (ShowPenStyle(int)));
    penCapLabel =new QLabel(QStringLiteral("画笔顶帽:"));        //画笔顶帽风格选择下拉列表框
    penCapComboBox =new QComboBox;
    penCapComboBox->addItem(QStringLiteral("SquareCap"),Qt::SquareCap);
    penCapComboBox->addItem(QStringLiteral("FlatCap"),Qt::FlatCap);
    penCapComboBox->addItem(QStringLiteral("RoundCap"),Qt::RoundCap);
    connect(penCapComboBox,SIGNAL(activated(int)),this,SLOT (ShowPenCap (int)));
    penJoinLabel =new QLabel(QStringLiteral("画笔连接点:"));
    //画笔连接点风格选择下拉列表框
    penJoinComboBox =new QComboBox;
    penJoinComboBox->addItem(QStringLiteral("BevelJoin"),Qt::BevelJoin);
    penJoinComboBox->addItem(QStringLiteral("MiterJoin"),Qt::MiterJoin);
    penJoinComboBox->addItem(QStringLiteral("RoundJoin"),Qt::RoundJoin);
    connect(penJoinComboBox,SIGNAL(activated(int)),this,SLOT (ShowPenJoin (int)));
    fillRuleLabel =new QLabel(QStringLiteral("填充模式:"));      //填充模式选择下拉列表框
    fillRuleComboBox =new QComboBox;
    fillRuleComboBox->addItem(QStringLiteral("Odd Even"),Qt::OddEvenFill);
    fillRuleComboBox->addItem(QStringLiteral("Winding"),Qt::WindingFill);
    connect(fillRuleComboBox,SIGNAL(activated(int)),this,SLOT (ShowFillRule()));
    spreadLabel =new QLabel(QStringLiteral("铺展效果:"));        //铺展效果选择下拉列表框
    spreadComboBox =new QComboBox;
    spreadComboBox->addItem(QStringLiteral("PadSpread"),QGradient::PadSpread);														 //(f)
    spreadComboBox->addItem(QStringLiteral("RepeatSpread"),QGradient:: RepeatSpread);
    spreadComboBox->addItem(QStringLiteral("ReflectSpread"),QGradient:: ReflectSpread);
    connect(spreadComboBox,SIGNAL(activated(int)),this,SLOT (ShowSpreadStyle()));
    brushColorLabel =new QLabel(QStringLiteral("画刷颜色:"));    //画刷颜色选择控件
    brushColorFrame =new QFrame;
    brushColorFrame->setFrameStyle(QFrame::Panel|QFrame::Sunken);
    brushColorFrame->setAutoFillBackground(true);
    brushColorFrame->setPalette(QPalette(Qt::green));
    brushColorBtn =new QPushButton(QStringLiteral("更改"));
    connect(brushColorBtn,SIGNAL(clicked()),this,SLOT (ShowBrushColor()));
    brushStyleLabel =new QLabel(QStringLiteral("画刷风格:"));	//画刷风格选择下拉列表框
    brushStyleComboBox =new QComboBox;
    brushStyleComboBox->addItem(QStringLiteral("SolidPattern"),
                                static_cast<int>(Qt::SolidPattern));
    brushStyleComboBox->addItem(QStringLiteral("Dense1Pattern"),
                                static_cast<int>(Qt::Dense1Pattern));
    brushStyleComboBox->addItem(QStringLiteral("Dense2Pattern"),
                                static_cast<int>(Qt::Dense2Pattern));
    brushStyleComboBox->addItem(QStringLiteral("Dense3Pattern"),
                                static_cast<int>(Qt::Dense3Pattern));
    brushStyleComboBox->addItem(QStringLiteral("Dense4Pattern"),
                                static_cast<int>(Qt::Dense4Pattern));
    brushStyleComboBox->addItem(QStringLiteral("Dense5Pattern"),
                                static_cast<int>(Qt::Dense5Pattern));
    brushStyleComboBox->addItem(QStringLiteral("Dense6Pattern"),
                                static_cast<int>(Qt::Dense6Pattern));
    brushStyleComboBox->addItem(QStringLiteral("Dense7Pattern"),
                                static_cast<int>(Qt::Dense7Pattern));
    brushStyleComboBox->addItem(QStringLiteral("HorPattern"),
                                static_cast<int>(Qt::HorPattern));
    brushStyleComboBox->addItem(QStringLiteral("VerPattern"),
                                static_cast<int>(Qt::VerPattern));
    brushStyleComboBox->addItem(QStringLiteral("CrossPattern"),
                                static_cast<int>(Qt::CrossPattern));
    brushStyleComboBox->addItem(QStringLiteral("BDiagPattern"),
                                static_cast<int>(Qt::BDiagPattern));
    brushStyleComboBox->addItem(QStringLiteral("FDiagPattern"),
                                static_cast<int>(Qt::FDiagPattern));
    brushStyleComboBox->addItem(QStringLiteral("DiagCrossPattern"),
                                static_cast<int>(Qt:: DiagCrossPattern));
    brushStyleComboBox->addItem(QStringLiteral("LinearGradientPattern"),
                        static_cast<int>(Qt:: LinearGradientPattern));
    brushStyleComboBox->addItem(QStringLiteral("ConicalGradientPattern"),
                        static_cast<int>(Qt:: ConicalGradientPattern));
    brushStyleComboBox->addItem(QStringLiteral("RadialGradientPattern"),
                        static_cast<int>(Qt:: RadialGradientPattern));
    brushStyleComboBox->addItem(QStringLiteral("TexturePattern"),
                        static_cast<int>(Qt::TexturePattern));
    connect(brushStyleComboBox,SIGNAL(activated(int)),this,SLOT (ShowBrush(int)));
    rightLayout =new QGridLayout;                   //控制面板的布局
    rightLayout->addWidget(shapeLabel,0,0);
    rightLayout->addWidget(shapeComboBox,0,1);
    rightLayout->addWidget(penColorLabel,1,0);
    rightLayout->addWidget(penColorFrame,1,1);
    rightLayout->addWidget(penColorBtn,1,2);
    rightLayout->addWidget(penWidthLabel,2,0);
    rightLayout->addWidget(penWidthSpinBox,2,1);
    rightLayout->addWidget(penStyleLabel,3,0);
    rightLayout->addWidget(penStyleComboBox,3,1);
    rightLayout->addWidget(penCapLabel,4,0);
    rightLayout->addWidget(penCapComboBox,4,1);
    rightLayout->addWidget(penJoinLabel,5,0);
    rightLayout->addWidget(penJoinComboBox,5,1);
    rightLayout->addWidget(fillRuleLabel,6,0);
    rightLayout->addWidget(fillRuleComboBox,6,1);
    rightLayout->addWidget(spreadLabel,7,0);
    rightLayout->addWidget(spreadComboBox,7,1);
    rightLayout->addWidget(brushColorLabel,8,0);
    rightLayout->addWidget(brushColorFrame,8,1);
    rightLayout->addWidget(brushColorBtn,8,2);
    rightLayout->addWidget(brushStyleLabel,9,0);
    rightLayout->addWidget(brushStyleComboBox,9,1);
    QHBoxLayout *mainLayout =new QHBoxLayout(this);  //整体的布局
    mainLayout->addWidget(paintArea);
    mainLayout->addLayout(rightLayout);
    mainLayout->setStretchFactor(paintArea,1);
    mainLayout->setStretchFactor(rightLayout,0);
    ShowShape(shapeComboBox->currentIndex());        //显示默认的图形
}
  • shapeComboBox->addItem(QStringLiteral(“Line”),PaintArea::Line):QComboBox的addItem函数可以仅插入文本,也可以同时插入文本相对应的具体数据,通常为枚举类型数据,便于后面操作时确定选择的是哪个数据。
  • penStyleComboBox->addItem(QStringLiteral(“SolidLine”), static_cast(Qt::SolidLine)):选用不同的参数,对应画笔的不同风格,如下图所示。
    在这里插入图片描述
  • penCapComboBox->addItem(QStringLiteral(“SquareCap”),Qt::SquareCap):选用不同的参数,对应画笔顶帽的不同风格,如下图所示。
    在这里插入图片描述
    其中,Qt::SquareCap表示在线条的顶点处是方形的,且线条绘制的区域包括了端点,并且再往外延伸半个线宽长度;Qt::FlatCap表示在线条的顶点处是方形的,但线条绘制区域不包括端点在内;Qt::RoundCap表示在线条的顶点处是圆形,且线条绘制区域包含了端点。
  • penJoinComboBox->addItem(QStringLiteral(“BevelJoin”),Qt::BevelJoin):选用不同的参数,对应画笔连接点的不同风格,如下图所示。
    在这里插入图片描述
    其中,Qt::BevelJoin风格连接点是指两条线的中心线顶点相汇,相接处依然保留线条各自的方形顶端;Qt::MiterJoin风格连接点是指两条线的中心顶点相汇,相连处线条延长到线的外侧汇集至点,形成一个尖顶的连接;Qt::RoundJoin风格连接点是指两条线的中心线顶点相汇,相连处圆弧形连接。
  • fillRuleComboBox->addItem(QStringLiteral(“Odd Even”),Qt::OddEvenFill):Qt为QPainterPath类提供了两种填充规则,分别是Qt::OddEvenFill和Qt::WindingFill,如下图所示。这两种填充规则在判定图形中某一点处于内部还是外部时,判断依据不同。
    在这里插入图片描述
    其中,Qt::OddEvenFill填充规则判断的依据是从图形中某一点画一条水平线到图形外。若这条水平线与图形边线的交点数目为奇数,则说明此点位于图形的内部;若交点数目为偶数,则此点位于图形的外部,如下图所示。
    在这里插入图片描述
    而Qt::WindingFill填充规则的判断依据则是从图形中某一点画一条水平线到图形外,每个交点外边线的方向可能向上,也可能向下,将这些交点数累加,方向相反的相互抵消,若最后结果不为0则说明此点在图形内,若最后结果为0则说明在图形外,如下图所示。
    在这里插入图片描述
    其中,边线方向是由QPainterPath创建时根据描述的顺序决定的。如果采用addRect()或addPolygon()等函数加入图形,默认是按顺时针方向。
  • spreadComboBox->addItem(QStringLiteral(“PadSpread”),QGradient::PadSpread):铺展效果有三种,分别为QGradient::PadSpread、QGradient::RepeatSpread和QGradient::ReflectSpread。其中,PadSpread是默认的铺展效果,也是最常见的铺展效果,没有被渐变覆盖的区域填充单一的起始颜色或终止颜色;RepeatSpread效果与ReflectSpread效果只对线性渐变和圆形渐变起作用,如下图所示。
    在这里插入图片描述
    使用QGradient的setColorAt()函数设置起止的颜色,其中,第一个参数表示所设颜色点的位置,取值范围为0.0~1.0,0.0表示起点,1.0表示终点;第二个参数表示该点的颜色值。除可设置起点和终点的颜色外,如有需要还可以设置中间任意位置的颜色,例如,setColorAt(0.3,Qt::white),设置起、终点之间1/3位置的颜色为白色。
  • brushStyleComboBox->addItem(QStringLiteral(“SolidPattern”), static_cast(Qt::SolidPattern)):选用不同的参数,对应画刷的不同风格,如下图所示。
    在这里插入图片描述

ShowShape()槽函数,根据当前下拉列表框中选择的选项,调用PaintArea类的setShape()函数设置PaintArea对象的形状参数,具体代码如下:

void MainWidget::ShowShape(int value)
{
    PaintArea::Shape shape = PaintArea::Shape(shapeComboBox->itemData(
            value,Qt::UserRole).toInt());
    paintArea->setShape(shape);
}

其中QComboBox类的itemData方法返回当前显示的下拉列表框数据,是一个QVariant对象,此对象与控件初始化时插入枚举型数据相关,调用QVariant类的toInt()函数获得此数据在枚举类型集合中的序号。
ShowPenColor()槽函数,利用标准颜色对话框QColorDialog获取所选颜色,采用QFrame和QPushButton对象组合完成,QFrame对象负责显示当前所选择的颜色,QPushButton对象用于触发标准颜色对话框进行颜色的选择。
在此函数中获得与画笔相关的所有属性值,包括画笔颜色、画笔线宽、画笔风格、画笔顶帽及画笔连接点,共同构成QPen对象,并调用PaintArea对象的setPen()函数设置PaintArea对象的画笔属性。其他画笔参数相关的响应函数完成的工作与此类似,具体代码如下:

void MainWidget::ShowPenColor()
{
    QColor color = QColorDialog::getColor(static_cast<int>(Qt::blue));
    penColorFrame->setPalette(QPalette(color));
    int value = penWidthSpinBox->value();
    Qt::PenStyle style = Qt::PenStyle(penStyleComboBox->itemData(
            penStyleComboBox->currentIndex(),Qt::UserRole).toInt());
    Qt::PenCapStyle cap = Qt::PenCapStyle(penCapComboBox->itemData(
            penCapComboBox->currentIndex(),Qt::UserRole).toInt());
    Qt::PenJoinStyle join=Qt::PenJoinStyle(penJoinComboBox->itemData(penJoinComboBox->currentIndex(),Qt::UserRole).toInt());
    paintArea->setPen(QPen(color,value,style,cap,join));
}

ShowPenWidth()槽函数的具体实现代码如下:

void MainWidget::ShowPenWidth(int value)
{
    QColor color = penColorFrame->palette().color(QPalette::Window);
    Qt::PenStyle style = Qt::PenStyle(penStyleComboBox->itemData(
            penStyleComboBox->currentIndex(),Qt::UserRole).toInt());
    Qt::PenCapStyle cap = Qt::PenCapStyle(penCapComboBox->itemData(
            penCapComboBox->currentIndex(),Qt::UserRole).toInt());
    Qt::PenJoinStyle join=Qt::PenJoinStyle(penJoinComboBox->itemData(penJoinComboBox->currentIndex(),Qt::UserRole).toInt());
    paintArea->setPen(QPen(color,value,style,cap,join));
}

ShowPenStyle()槽函数的具体实现代码如下:

void MainWidget::ShowPenStyle(int styleValue)
{
    QColor color = penColorFrame->palette().color(QPalette::Window);
    int value = penWidthSpinBox->value();
    Qt::PenStyle style = Qt::PenStyle(penStyleComboBox->itemData(
            styleValue,Qt::UserRole).toInt());
    Qt::PenCapStyle cap = Qt::PenCapStyle(penCapComboBox->itemData(
            penCapComboBox->currentIndex(),Qt::UserRole).toInt());
    Qt::PenJoinStyle join=Qt::PenJoinStyle(penJoinComboBox->itemData(
            penJoinComboBox->currentIndex(),Qt::UserRole).toInt());
    paintArea->setPen(QPen(color,value,style,cap,join));
}

ShowPenCap()槽函数的具体实现代码如下:

void MainWidget::ShowPenCap(int capValue)
{
    QColor color = penColorFrame->palette().color(QPalette::Window);
    int value = penWidthSpinBox->value();
    Qt::PenStyle style = Qt::PenStyle(penStyleComboBox->itemData(
        penStyleComboBox->currentIndex(),Qt::UserRole).toInt());
    Qt::PenCapStyle cap = Qt::PenCapStyle(penCapComboBox->itemData(
        capValue,Qt::UserRole).toInt());
    Qt::PenJoinStyle join=Qt::PenJoinStyle(penJoinComboBox->itemData(
        penJoinComboBox->currentIndex(),Qt::UserRole).toInt());
    paintArea->setPen(QPen(color,value,style,cap,join));
}

ShowPenJoin()槽函数的具体实现代码如下:

void MainWidget::ShowPenJoin(int joinValue)
{
    QColor color = penColorFrame->palette().color(QPalette::Window);
    int value = penWidthSpinBox->value();
    Qt::PenStyle style = Qt::PenStyle(penStyleComboBox->itemData(
        penStyleComboBox->currentIndex(),Qt::UserRole).toInt());
    Qt::PenCapStyle cap = Qt::PenCapStyle(penCapComboBox->itemData(
        penCapComboBox->currentIndex(),Qt::UserRole).toInt());
    Qt::PenJoinStyle join=Qt::PenJoinStyle(penJoinComboBox->itemData(
        joinValue,Qt::UserRole).toInt());
    paintArea->setPen(QPen(color,value,style,cap,join));
}

ShowFillRule()槽函数的具体实现代码如下:

void MainWidget::ShowFillRule()
{
    Qt::FillRule rule = Qt::FillRule(fillRuleComboBox->itemData(
    fillRuleComboBox->currentIndex(),Qt::UserRole).toInt());
    paintArea->setFillRule(rule);
}

ShowSpreadStyle()槽函数的具体实现代码如下:

void MainWidget::ShowSpreadStyle()
{
    spread = QGradient::Spread(spreadComboBox->itemData(
        spreadComboBox->currentIndex(),Qt::UserRole).toInt());
}

ShowBrushColor()槽函数,与设置画笔颜色函数类似,但选定颜色后并不直接调用PaintArea对象的setBrush()函数,而是嗲用ShowBrush()函数设置显示区的画刷属性,具体实现代码如下:

void MainWidget::ShowBrushColor()
{
    QColor color = QColorDialog::getColor(static_cast<int>(Qt:: blue));
    brushColorFrame->setPalette(QPalette(color));
    ShowBrush(brushStyleComboBox->currentIndex());
}

ShowBrush()槽函数具体实现代码如下:

void MainWidget::ShowBrush(int value)
{
    //获得画刷的颜色
    QColor color = brushColorFrame->palette().color(QPalette:: Window);
    Qt::BrushStyle style = Qt::BrushStyle(brushStyleComboBox-> itemData(
        value,Qt::UserRole).toInt());
    if(style == Qt::LinearGradientPattern)	
    {
        QLinearGradient linearGradient(0,0,400,400);
        linearGradient.setColorAt(0.0,Qt::white);
        linearGradient.setColorAt(0.2,color);
        linearGradient.setColorAt(1.0,Qt::black);
        linearGradient.setSpread(spread);
        paintArea->setBrush(linearGradient);
    }
    else if(style == Qt::RadialGradientPattern)
    {
        QRadialGradient radialGradient(200,200,150,150,100);
        radialGradient.setColorAt(0.0,Qt::white);
        radialGradient.setColorAt(0.2,color);
        radialGradient.setColorAt(1.0,Qt::black);
        radialGradient.setSpread(spread);
        paintArea->setBrush(radialGradient);
    }
    else if(style == Qt::ConicalGradientPattern)
    {
        QConicalGradient conicalGradient(200,200,30);
        conicalGradient.setColorAt(0.0,Qt::white);
        conicalGradient.setColorAt(0.2,color);
        conicalGradient.setColorAt(1.0,Qt::black);
        paintArea->setBrush(conicalGradient);
    }
    else if(style == Qt::TexturePattern)
    {
        paintArea->setBrush(QBrush(QPixmap("butterfly.png")));
    }
    else
    {
        paintArea->setBrush(QBrush(color,style));
    }
}
  • Qt::BrushStyle style = Qt::BrushStyle(brushStyleComboBox-> itemData(
    value,Qt::UserRole).toInt()):获得所选的画刷风格,若选择的是渐变或者纹理图案,则需要进行一定处理。
  • if(style == Qt::LinearGradientPattern):
    主窗口的style变量值为Qt::LinearGradientPattern时,表明选择的是圆形渐变。
    QLinearGradient(const QPointF &start, const QPointF &finalStop)创建线性渐变类对象需要两个参数,分别表示起止点位置。
  • if(style == Qt::RadialGradientPattern):主窗口style变量值为Qt::RadialGradientPattern时,表明选择的是圆形渐变。
    QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint)创建圆形渐变类对象需要三个参数,分别表示圆心位置、半径值和焦点位置。QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint)表示以center为作为圆心和焦点位置,以center和focalPoint之间的距离radius为半径,当然圆心和焦点的位置也可以不重合。
  • if(style == Qt::ConicalGradientPattern):主窗口的style变量值为Qt::ConicalGradientPattern时,表明选择的是锥形渐变。
    QConicalGradient(const QPointF &center, qreal angle)创建锥形渐变类对象需要两个参数,分别是锥形的顶点位置和渐变分界线与水平方向的夹角,如下图所示。锥形渐变不需要设置铺展效果,它的铺展效果只能是QGradient::PadSpread (注意:锥形渐变的方向默认是逆时针方向)
    在这里插入图片描述

双缓冲机制

原理与设计

所谓双缓冲机制,是指在绘制控件时,首先将要绘制的内容绘制在一个图片中,再将图片一次性地绘制到控件上。在早期地Qt本版中,若直接在控件上进行绘制工作,则在控件重绘时会产生闪烁地现象,控件重绘频繁时,闪烁尤为明显。双缓冲机制可以有效地消除这种闪烁现象。自Qt5版本之后,QWidget控件已经能够自动处理闪烁地问题。因此,在控件上直接绘图时,不用再操心显示的闪烁问题,但双缓冲机制在很多场合仍然尤其用武之地。当所需绘制的内容比较复杂并需要频繁刷新,或者每次只需要刷新整个控件的一小部分时,仍应尽量采用双缓冲机制。

创建一个DrawWidget项目实现一个简单的绘图工具,可以选择线型、线宽、颜色等基本要素,如下图所示。QMainWindows对象作为主窗口,QToolBar对象作为工具栏,QWidget对象作为主窗口的中央窗体,也就是绘图区。
在这里插入图片描述
由于本实例是完成一个通过响应鼠标事件进行绘图的功能,而这是在绘图区窗体完成的,所以首先实现此窗体DrawWidget对鼠标事件进行重定义;然后实现可以选择线型、线宽及颜色等基本要素的主窗口。

绘图区的实现

DrawWidget类继承自QWidget类,在类声明中对鼠标事件mousePressEvent()和mouseMoveEvent()、重绘事件paintEvent()、尺寸变化事件resizeEvent()进行了重定义。setStyle()、setWidth()及setColor()函数主要用于为主窗口传递各种与绘图有关的参数。

DrawWidget类布局代码如下:

DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
    setAutoFillBackground(true);    	//对窗体背景色的设置
    setPalette(QPalette(Qt::white));
    pix =new QPixmap(size());           //此QPixmap对象用来准备随时接收绘制的内容
    pix->fill(Qt::white);            	//填充背景色为白色
    setMinimumSize(600,400);            //设置绘制区窗体的最小尺寸
}

setStyle函数接收主窗口传来的线型风格参数,setWidth函数接收主窗口传来的线宽参数值,setColor函数接收主窗口传来的画笔颜色值。具体代码如下:

void DrawWidget::setStyle(int s)
{
    style = s;
}
void DrawWidget::setWidth(int w)
{
    weight = w;
}
void DrawWidget::setColor(QColor c)
{
    color = c;
}

重定义鼠标按下事件mousePressEvent函数,在按下鼠标按键时,记录当前的鼠标位置值startPos。

void DrawWidget::mousePressEvent(QMouseEvent *e)
{
    startPos = e->pos();
}

重定义鼠标事件mouseMoveEvent,鼠标移动事件在默认情况下,在刷表按键按下的同时拖拽鼠标时被触发。
QWidget的mouseTracking属性指示窗体是否追踪鼠标,默认为false(不追踪),即在至少有一个鼠标按键被按下的前提下移动鼠标才触发mouseMoveEvent事件,可以通过setMouseTracking(bool enable)方法对该属性值进行设置。如果设置的追踪,则无论鼠标按键是否按下,只要鼠标移动,就会触发mouseMoveEvent事件。在此事件处理函数中,完成QPixmap对象中绘图的工作。具体代码如下:

void DrawWidget::mouseMoveEvent(QMouseEvent *e)
{
    QPainter *painter = new QPainter;		//新建一个QPainter对象
    QPen pen;								//新建一个QPen对象
    pen.setStyle((Qt::PenStyle)style);	
    pen.setWidth(weight);					//设置画笔的线宽值
    pen.setColor(color);					//设置画笔的颜色
    painter->begin(pix);
    painter->setPen(pen);					//将QPen对象应用到绘制对象中
    //绘制从startPos到鼠标当前位置的直线
    painter->drawLine(startPos,e->pos());
    painter->end();
    startPos =e->pos();				//更新鼠标的当前位置,为下次绘制做准备
    update();						//重绘绘制区窗体
}
  • pen.setStyle((Qt::PenStyle)style):设置画笔的线型,style表示当前选择的线型是Qt::PenStyle枚举数据的第几个元素。
  • painter->begin(pix)、painter->end():以QPixmap对象为QPaintDevice参数绘制。在构造一个QPainter对象时,就立即开始对绘画进行绘制。此构造QPainter对象是短时期的,如应定义在QWidget::paintEvent中,并只能调用一次。此构造函数调用开始于begin函数,并且在QPainter的析构函数中自动调用end函数。由于一个QPainter对象的初始化失败时构造函数不能提供反馈信息,所以在绘制外部设备时应使用begin和end函数,如打印机等外部设备。
    下面是使用begin和end函数的一个例子:
void DrawWidget::paintEvent(QPaintEvent *)
{
    QPainter painter;
    painter->begin(this);
    painter->drawLine(...);
    painter->end();
}

类似下面的形式:

void DrawWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawLine(...;
}

重绘函数paintEvent完成绘制区窗体的更新工作,只需要用drawPixmap函数将用于接收图形绘制的QPixmao对象绘制再绘制区窗体控件上。具体代码如下:

void DrawWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawPixmap(QPoint(0,0),*pix);
}

调整绘制区大小函数resizeEvent,当窗体的大小发生改变时,效果看起来像是绘制区大小改变了,但实际能够进行绘制的区域仍然没有改变。因为绘图的大小并没有改变,还是原来绘制区窗口的大小,所以在窗体尺寸变化是应及时调整用于绘制的QPixmap对象的大小。具体代码如下:

void DrawWidget::resizeEvent(QResizeEvent *event)
{
    if(height()>pix->height()||width()>pix->width())
    {
        QPixmap *newPix = new QPixmap(size());	//创建一个新的QPixmap对象
        newPix->fill(Qt::white);                //填充新QPixmap对象newPix的颜色为白色背景色
        QPainter p(newPix);
        p.drawPixmap(QPoint(0,0),*pix);         //在newPix中绘制原pix中的内容
        pix = newPix;                           //将newPix赋值给pix作为新的绘制图形接收对象
    }
    QWidget::resizeEvent(event);                //完成其余的工作
}
  • if(height()>pix->height()||width()>pix->width()):判断改变后的窗体长或宽是否大于原窗体的长和宽。若大于则进行相应的调整,否则直接调用QWidget的resizeEvent函数返回。
    clear函数完成绘制区的清楚工作,只需要调用一个新的、干净的QPixmap对象来代替pix,并调用update函数重绘即可。具体代码如下:
void DrawWidget::clear()
{
    QPixmap *clearPix =new QPixmap(size());
    clearPix->fill(Qt::white);
    pix = clearPix;
    update();
}

至此,一个能够响应鼠标事件进行绘图功能的窗体类DrawWidget已实现,可以进行接下来的工作,即在主窗口中应用此窗体类。

主窗口的实现

主窗口类MainWindow类继承了QMainWindow类,只包含一个工具栏和一个中央窗体。首先,声明一个构造函数、一个用于创建工具栏的函数createToolBar、一个用于进行选择线型风格的槽函数ShowStyle和一个用于进行颜色选择的槽函数ShowColor。然后,声明一个DrawWidget类对象作为窗口的私有变量,以及声明代表线型风格、线宽选择、颜色选择及清除按钮的私有变量。
主界面构建代码逻辑如下:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    drawWidget =new DrawWidget;     //新建一个DrawWidget对象
    setCentralWidget(drawWidget);	//新建的DrawWidget对象作为主窗口的中央窗体
    createToolBar();               	//实现一个工具栏
    setMinimumSize(600,400);      	//设置主窗口的最小尺寸
    ShowStyle();                    //初始化线型,设置控件中的当前值作为初始值
    drawWidget->setWidth(widthSpinBox->value());        //初始化线宽
    drawWidget->setColor(Qt::black);             		//初始化颜色
}

createToolBar实现一个工具函数功能代码如下:

void MainWindow::createToolBar()
{
    QToolBar *toolBar = addToolBar("Tool");  				//为主窗口新建一个工具栏对象
    styleLabel =new QLabel(QStringLiteral("线型风格:"));   	//创建线型选择控件
    styleComboBox =new QComboBox;
    styleComboBox->addItem(QStringLiteral("SolidLine"),
                                static_cast<int>(Qt::SolidLine));
    styleComboBox->addItem(QStringLiteral("DashLine"),
                                static_cast<int>(Qt::DashLine));
    styleComboBox->addItem(QStringLiteral("DotLine"),
                                static_cast<int>(Qt::DotLine));
    styleComboBox->addItem(QStringLiteral("DashDotLine"),
                                static_cast<int>(Qt::DashDotLine));
    styleComboBox->addItem(QStringLiteral("DashDotDotLine"),
                                static_cast<int>(Qt::DashDotDotLine));
                                                		//关联相应的槽函数
    connect(styleComboBox,SIGNAL(activated(int)),this,SLOT(ShowStyle()));
    widthLabel =new QLabel(QStringLiteral("线宽:"));   //创建线宽选择控件
    widthSpinBox =new QSpinBox;
    connect(widthSpinBox,SIGNAL(valueChanged(int)),drawWidget,SLOT (setWidth(int)));
    colorBtn =new QToolButton;                  //创建颜色选择控件
    QPixmap pixmap(20,20);
    pixmap.fill(Qt::black);
    colorBtn->setIcon(QIcon(pixmap));
    connect(colorBtn,SIGNAL(clicked()),this,SLOT(ShowColor()));
    clearBtn =new QToolButton();               	//创建清除按钮
    clearBtn->setText(QStringLiteral("清除"));
    connect(clearBtn,SIGNAL(clicked()),drawWidget,SLOT(clear()));
    toolBar->addWidget(styleLabel);
    toolBar->addWidget(styleComboBox);
    toolBar->addWidget(widthLabel);
    toolBar->addWidget(widthSpinBox);
    toolBar->addWidget(colorBtn);
    toolBar->addWidget(clearBtn);
}

改变线型参数的槽函数ShowStyle,通过调用DrawWidget类的setStyle函数将当前线型选择控件中的线型参数传给绘制区;设置画笔颜色的槽函数ShowColor,通过调用DrawWidget类的setColor函数将用户在标准颜色对话框中选择的颜色值传给绘制区。这两个函数的具体代码如下:

void MainWindow::ShowStyle()
{
    drawWidget->setStyle(styleComboBox->itemData(
        styleComboBox->currentIndex(),Qt::UserRole).toInt());
}

void MainWindow::ShowColor()
{
    QColor color = QColorDialog::getColor(static_cast<int> (Qt::black), this);
    //使用标准颜色对话框QColorDialog获得一个颜色值
    if(color.isValid())
    {
       //将新选择的颜色传给绘制区,用于改变画笔的颜色值
        drawWidget->setColor(color);
        QPixmap p(20,20);
        p.fill(color);
        colorBtn->setIcon(QIcon(p));		//更新颜色选择按钮上的颜色显示
    }
}

显示Qt5 SVG格式图片

SVG的英文全称是Scalable Vector Graphics,即可缩放的矢量图形。它是由万维网联盟在2000年8月制定的一种新的二维矢量图形格式,也是规范中的网格矢量图形标准,是一个开放的图形标准。

SVG格式的特点如下:

  1. 基于XML。
  2. 采用文本来描述对象。
  3. 具有交互性和动态性。
  4. 完全支持DOM。

SVG相对于GIF、JPEG格式的优势是,SVG是一种矢量图形格式,比GIF、JPEG等栅格格式具有众多优势,如文件小,对于网络而言,下载速度快;可任意缩放而不会破坏图像的清晰度和细节;图像中的文字独立于图像,文字保留可编辑和可搜寻的状态,也没有字体限制,用户系统即使没有安装某一种字体,也可以看到与制作时完全相同的画面等。正式基于其格式的各种优点及开放性,SVG得到了众多组织和知名厂商的支持与认可,因此能够迅速地开发和推广应用。
Qt为SVG格式图片地显示与生成提供了专门的QtSvg模块,此模块中包含了与SVG图片相关的所有类,主要有QSvgWidget、QSvgRender和QGraphicsSvgItem。

创建一个SVGTest项目,通过利用QSvgWidget类和QSvgRenderer类实现一个SVG图片浏览器,显示以".svg"结尾的文件以介绍SVG格式图片显示的方法,显示效果如下图所示。
在这里插入图片描述
注意,在此我们使用了显示SVG的控件,需要在Makefile添加svg模块的库,如下图所示。
在这里插入图片描述

打开svgwidget.cpp文件,在SvgWidget类构造函数获得本窗体的QSvgRenderer对象,具体代码如下:

SvgWidget::SvgWidget(QWidget *parent):QSvgWidget(parent)
{
    render =renderer();
}

以下是鼠标滚轮的响应事件,使SVG图片能够通过鼠标滚轮的滚动进行缩放。具体代码如下:

void SvgWidget::wheelEvent(QWheelEvent *e)
{
    const double diff=0.1;
    QSize size =render->defaultSize();
    int width =size.width();
    int height =size.height();
    if(e->delta()>0)
    {
       //对图片的长、宽值进行处理,放大一定的比例
       width =int(this->width()+this->width()*diff);
       height =int(this->height()+this->height()*diff);
    }
    else
    {
       //对图片的长、宽值进行处理,缩小一定的比例
       width =int(this->width()-this->width()*diff);
       height =int(this->height()-this->height()*diff);
    }
    resize(width,height);	//利用新的长、宽值对图片进行resize()操作
}
  • const double diff=0.1:diff的值表示每次滚动一定的值,图片大小改变的比例。
  • QSize size =render->defaultSize():该行代码及下面两行代码用于获取图片显示区的尺寸,以便进行下一步的缩放操作。
  • if(e->delta()>0):利用QWheelEvent的delta函数获得滚动的距离值,通过此值来判断滚轮滚动的方向。若delta值大于0,则表示滚轮向前(远离用户的方向)滚动;若小于0则表示向后(靠近用户的方向)滚动。
    鼠标滚动事件,滚轮每滚动1°相当于移动了8°,而常见的滚轮鼠标拨动以下滚动的角度为15°,因此滚轮波动一下相当于移动了120(=15*8)。

SvgWindow类继承自QScrollArea类,是一个带滚动条的显示区域。在SvgWindow类实现中包含SvgWidget类的头文件。SvgWindow类使图片在放大到超过主窗口大小时,能够通过拖拽滚动条的方式进行查看。
SvgWindow类的构造函数,构造SvgWidget对象,并调用QScrollArea类的setWidget函数设置滚动区的窗体,使svgWidget成为SvgWindow的子窗口。具体代码如下。

SvgWindow::SvgWindow(QWidget *parent):QScrollArea(parent)
{
    svgWidget =new SvgWidget;
    setWidget(svgWidget);
}

当主窗口中对文件进行了选择或修改时,将调用setFile函数设置新的文件,具体代码如下:

void SvgWindow::setFile(QString fileName)
{
    svgWidget->load(fileName);
    QSvgRenderer *render =svgWidget->renderer();
    svgWidget->resize(render->defaultSize());
}
  • svgWidget->load(fileName):将新的SVG文件加载到svgWidget中进行显示。
  • svgWidget->resize(render->defaultSize()):使svgWidget窗体按SVG图片的默认尺寸进行显示。

当鼠标被按下时,对mousePressPos和scrollBarValuesOnMousePress进行初始化,QScrollArea类的horizontalScrollBar和verticalScrollBar函数可以分别获得svgWidget的水平滚动条和垂直滚动条。具体代码如下:

void SvgWindow::mousePressEvent(QMouseEvent *event)
{
    mousePressPos =event->pos();
    scrollBarValuesOnMousePress.rx()=horizontalScrollBar()->value();
    scrollBarValuesOnMousePress.ry()=verticalScrollBar()->value();
    event->accept();
}

当鼠标被按下并拖拽鼠标时触发mouseMoveEvent函数,通过滚动条的位置设置实现图片的拖拽效果,具体代码如下:

void SvgWindow::mouseMoveEvent(QMouseEvent *event)
{
    //对水平滑动条的新位置进行设置
    horizontalScrollBar()->setValue(scrollBarValuesOnMousePress.x()-event->pos().x()+mousePressPos.x());
    //对垂直滑动条的新位置进行设置
    verticalScrollBar()->setValue(scrollBarValuesOnMousePress.y()-event->pos().y()+mousePressPos.y());
    horizontalScrollBar()->update();
    verticalScrollBar()->update();
    event->accept();
}

主窗口MainWindow继承QMainWindow类,包含一个菜单栏,其中有一个“文件”菜单条,包含一个“打开”菜单项。
MainWindow构造函数中,创建一个SvgWindow对象作为主窗口的中央窗体。具体代码如下:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle(QStringLiteral("SVG Viewer"));
    createMenu();
    svgWindow =new SvgWindow;
    setCentralWidget(svgWindow);
    setMinimumSize(400, 300);
}

创建菜单栏具体代码如下:

void MainWindow::createMenu()
{
    QMenu *fileMenu = menuBar()->addMenu(QStringLiteral("文件"));
    QAction *openAct = new QAction(QStringLiteral("打开"),this);
    connect(openAct,SIGNAL(triggered()),this,SLOT(slotOpenFile()));
    fileMenu->addAction(openAct);
}

通过标准文件对话框选择SVG文件,并调用SvgWindow窗口的setFile函数将选择的文件名传递给svgWindow进行显示,具体代码如下:

void MainWindow::slotOpenFile()
{
    QString name =QFileDialog::getOpenFileName(this, QStringLiteral("打开"),"./","svg files(*.svg)");
    svgWindow->setFile(name);
}

工程源码

文章设计所有代码可点击工程源码下载查看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/793942.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Golang | Leetcode Golang题解之第230题二叉搜索树中第K小的元素

题目&#xff1a; 题解&#xff1a; type MyBst struct {root *TreeNodenodeNum map[*TreeNode]int // 统计以每个结点为根结点的子树的结点数&#xff0c;并存储在哈希表中 }// 统计以 node 为根结点的子树的结点数 func (t *MyBst) countNodeNum(node *TreeNode) int {if…

达梦数据库dm8安装步骤及迁移

目录 前言: 一、安装部署 1、下载 2、创建用户及安装目录 3、挂载下载的镜像 4、环境配置 5、安装 二、基本使用 1、DM工具使用 2、兼容性配置 2.1 兼容GBK字符集编码 2.2 兼容UTF-8字符集编码 3、创建用户和密码,表空间 4、整理数据库配置 5、启动脚本设置 …

基于YOLOV8的数粒机视觉计数解决方案

一:行业背景调查 随着全球市场商品大规模工业化生产技术的大规模发展,其中对各类产品生产包装以及原材料供给有了更多精准计数的要求,这些要求主要分布在一些产量较大,产品颗粒较小,单个成本较高的商品中,近几年主要从医药包装领域和接插件包装领域开始对产品包装中的计…

ENSP实现防火墙区域策略与用户管理

目录 实验拓扑与要求​编辑 交换机与防火墙接口的配置 交换机&#xff1a; 创建vlan 接口配置 防火墙配置及接口配置 防火墙IP地址配置 云配置​编辑​编辑​编辑 在浏览器上使用https协议登陆防火墙&#xff0c;并操作 访问网址&#xff1a;https://192.168.100.1:844…

弥合人类与人工智能的知识差距:AlphaZero 中的概念发现和迁移(1)

文章目录 一、摘要二、简介三、相关工作3.1 基于概念的解释3.2 强化学习中生成解释3.3 国际象棋与人工智能 四、什么是概念&#xff1f;五、发掘概念5.1 挖掘概念向量5.1.1 静态概念的概念约束5.1.2 动态概念的概念约束 5.2 过滤概念 一、摘要 人工智能&#xff08;AI&#xff…

2023年全国大学生电子信息竞赛E题——自动追踪系统(stm32和openmv+普通舵机)完美解决第四问

当时做的时候&#xff0c;当时看别人开源的23年的题&#xff0c;感觉一头雾水。两个字没思路。确实只有做了才会有思路。我这里清晰的整理出来思路。 1.第一问的复位问题就是写一个函数&#xff0c;如果按键按下&#xff0c;就进入&#xff0c;再按下就退出 当然这个复位是写死…

git提交大文件服务500

错误如图 需保证git服务端能接收大文件 修改项目下.git文件中的config文件&#xff0c;加入 [http] postBuffer 524288000

Mybatis——增删改查

目录 一、准备工作 二、mybatis——新增 三、mybatis——删除 四、mybatis——更新 五、mybatis——查询 六、XML映射文件 一、准备工作 1.准备一个数据库表 2.创建一个新的springboot工程&#xff0c;选择引入对应的起步依赖&#xff08;Mybatis、mybatis等&#xff09…

C:数据结构---算法

1.1排序算法 稳定排序 不稳定排序 ①冒泡排序&#xff08;稳定&#xff09; 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。对每一对相邻元素作同样的工作&#xff0c;从开始第一对到结尾的最后一对 ②选择排序 在未排序序列中找到最小&#xff08;大…

一文入门【NestJs】Providers

Nest学习系列 ✈️一文入门【NestJS】 ✈️一文入门【NestJs】Controllers 控制器 &#x1f6a9; 前言 在NestJS的世界里&#xff0c;理解“Providers”是构建健壮、可维护的后端服务的关键。NestJS&#xff0c;作为Node.js的一个现代框架&#xff0c;采用了Angular的一些核…

【Dison夏令营 Day 16】如何使用 Python 中的 PyGame 制作俄罗斯方块游戏

俄罗斯方块(Tetris)是一款经典的益智游戏&#xff0c;游戏的目的是将落下的几何图形片&#xff08;称为 “俄罗斯方块”&#xff09;排列起来&#xff0c;填满水平线&#xff0c;不留空隙。当一条线被完全填满时&#xff0c;它就被清除了&#xff0c;玩家就能获得分数。随着四角…

【企业级监控】源码部署Zabbix与监控主机

Zabbix企业级分布式监控 文章目录 Zabbix企业级分布式监控资源列表基础环境一、LNMP环境搭建&#xff08;在zbx主机上&#xff09;1.1、配置Yum仓库1.1.1、下载阿里云的仓库文件1.2.2、安装PHP7的仓库1.2.3、生成Mariadb10.11的仓库文件1.2.4、快速重建Yum缓存 1.2、安装PHP7.4…

小巧低调的黑盒子,打造个性化音乐体验,欧尼士ONIX Alpha小尾巴上手

欧尼士ONIX的产品很有辨识度&#xff0c;这家来自英国的品牌&#xff0c;有着鲜明的黑金设计色彩&#xff0c;以及低调奢华的质感&#xff0c;当然最重要的是&#xff0c;欧尼士的音质表现非常出色&#xff0c;因此深受音乐爱好者的喜爱。在以手机等设备为载体的流媒体音乐盛行…

uniapp中使用uni-ui组件库

src目录下新建components目录从uni-ui引入对应的组件目录&#xff0c;如下图 直接使用组件&#xff0c;demo <template><view id"my" data-name"王五" data-age"18">my页面</view><uni-data-select :localdata"local…

WebDriver与浏览器通信的深度剖析与探索

在自动化测试的世界里&#xff0c;WebDriver无疑是连接测试脚本与浏览器之间的桥梁&#xff0c;它让复杂的自动化测试成为可能。本文将深入探讨WebDriver与浏览器之间的通信机制&#xff0c;揭示它们之间如何协同工作&#xff0c;以及这一过程中涉及的关键技术和挑战。 一、We…

sqlmap使用之-post注入、head注入(ua、cookie、referer)

1、post注入 1.1、方法一&#xff0c;通过保存数据包文件进行注入 bp抓包获取post数据 将数据保存到post.txt文件 加上-r指定数据文件 1.2、方法二、通过URL注入 D:\Python3.8.6\SQLmap>python sqlmap.py -u "http://localhost/login.php" --data "userna…

2024年06月CCF-GESP编程能力等级认证C++编程三级真题解析

本文收录于专栏《C等级认证CCF-GESP真题解析》&#xff0c;专栏总目录&#xff1a;点这里。订阅后可阅读专栏内所有文章。 一、单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 第 1 题 小杨父母带他到某培训机构给他报名参加CCF组织的GESP认证考试的第1级&…

【JavaScript 报错】未捕获的范围错误:Uncaught RangeError

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、错误原因分析1. 递归调用次数过多2. 数组长度超出限制3. 数值超出允许范围 二、解决方案1. 限制递归深度2. 控制数组长度3. 检查数值范围 三、实例讲解四、总结 Uncaught RangeError 是JavaScript中常见的一种错误&…

k8s集群离线部署

K8s离线部署 环境 目标 k8s离线部署 步骤 部署docker 详情见文章&#xff1a;《离线安装docker及后端项目离线打包》 https://blog.csdn.net/qq_45371023/article/details/140279746?spm1001.2014.3001.5501 所用到的所有文件在&#xff1a; 链接&#xff1a;https://pan…

9.Python学习:Socket

1.网络通信要素&#xff08;IP端口传输协议&#xff09; 2.Socket编程 2.1TCP、UDP协议了解 2.2 Socket流程 服务端有两个socket对象&#xff0c;客户端有一个 3.Socket实战 服务端代码&#xff1a; import socket #创建Socket对象 sksocket.socket() #绑定ip与端口号-使…