Qt开发 | Qt绘图技术 | 常见图像绘制 | Qt移动鼠标绘制任意形状 | Qt绘制带三角形箭头的窗口

文章目录

  • 一、基本绘图技术介绍
  • 二、常见的18种图形、路径、文字、图片绘制
  • 三、Qt移动鼠标绘制任意形状
  • 四、Qt绘制带三角形箭头的窗口

一、基本绘图技术介绍

  Qt提供了绘图技术,程序员可以在界面上拖动鼠标,或者在代码里指定参数进行绘图。

Qt绘图技术介绍

  • QPainter

    基本要素

    • QPen:用于控制线条的颜色、宽度、线型等;

      • 宽度(Width):
        • 通过 setWidth() 方法设置画笔的宽度,这将决定绘制线条的粗细。
      • 颜色(Color):
        • 使用 setColor() 方法设置画笔的颜色。颜色可以是 Qt 命名的颜色常量,如 Qt::red,或者使用 QColor 对象定义的更具体的颜色。
      • 样式(Style):
        • setStyle() 方法定义了线条的样式。可以是实线(Qt::SolidLine)、虚线(Qt::DashLine)、点线(Qt::DotLine)等。
      • 端点样式(Cap Style):
        • setCapStyle() 方法设置线条端点的样式。常见的端点样式有方形(Qt::SquareCap)、圆形(Qt::RoundCap)和平面(Qt::FlatCap)。
      • 连接样式(Join Style):
        • setJoinStyle() 方法定义了线条连接处的样式。斜接(Qt::MiterJoin)是默认样式,适用于大多数情况。还可以选择圆角(Qt::RoundJoin)或斜切(Qt::BevelJoin)。
      • 线型(Dash Pattern):
        • 对于虚线或点线,可以通过 setDashPattern() 方法设置具体的虚线模式,例如定义点和间隔的长度。
      • 笔刷(Brush):
        • 虽然 QPen 主要用于线条,但它也有一个 setBrush() 方法,可以设置用于填充形状轮廓的画刷样式。
      • 使用 QPen:
        • 创建 QPen 对象后,可以通过上述方法设置其属性,然后通过 QPainter::setPen() 方法将其应用到 QPainter 对象上。之后,QPainter 将使用这个画笔的样式来绘制线条和形状的轮廓。

      示例:

      QPainter painter;
      Qpen pen;
      
      //设置画笔属性
      pen.setWidth(10);  		//设置画笔的宽度为10像素。
      pen.setColor(Qt::red);	//设置画笔的颜色为红色
      pen.setStyle(Qt::SolidLine);    //设置画笔的样式为实线
      pen.setCapStyle(Qt::SquareCap);	//设置画笔端点的样式为方形
      pen.setJoinStyle(Qt::MiterJoin); // 设置画笔线条连接处的样式为斜接,这是最常用的线条连接方式,适用于大多数情况。
      
      painter.setPen(pen);    //将设置好的QPen对象应用到QPainter对象上
      
    • QBrush:设置区域填充特性,可以设置填充颜色、填充方式、渐变特性等,还可以采用图片做纹理填充

      • 颜色设置:
        • QBrush 可以通过 setColor() 方法设置填充颜色。颜色可以使用 QColor 对象指定,提供广泛的颜色选择。
      • 填充样式:
        • setStyle()方法用于设置填充样式。Qt::BrushStyle枚举提供了多种预定义的填充样式,如:
          • Qt::SolidPattern:纯色填充。
          • Qt::HorizontalPatternQt::VerticalPattern:水平或垂直条纹填充。
          • Qt::CrossPatternQt::BDiagPattern:交叉或对角线条纹填充。
          • Qt::DenseNPattern:提供不同密度的条纹或点状图案。
      • 渐变填充:
        • QBrush 支持使用渐变作为填充模式。可以创建 QLinearGradientQRadialGradient 对象,并将其设置为 QBrush 的渐变属性。
      • 纹理填充:
        • 通过 setTexture() 方法,可以将 QPixmapQImage 对象设置为纹理,用于填充形状。
      • 透明度:
        • setOpacity() 方法允许设置填充的透明度,范围从 0(完全透明)到 1(完全不透明)。
      • 变换:
        • setTransform() 方法可以对 QBrush 应用变换,如旋转、缩放等,这会影响纹理或渐变在形状上的呈现方式。
      • 使用 QBrush:
        • 在绘制过程中,一旦 QBrush 对象被设置好,就可以通过QPainter::setBrush() 方法将其应用到 QPainter 对象上。随后,使用 QPainter 绘制的封闭形状将使用该画刷进行填充。

      示例:

      QPainter painter;
      QBrush brush;
      
      brush.setColor(Qt::yellow); // 设置为黄色
      brush.setStyle(Qt::SolidPattern);	//设置填充样式为纯色填充
      
      painter.setBrush(brush);
      
    • QFont:绘制文字时,设置文字的字体样式、大小等属性

      • 字体名称(Family):
        • 使用 setFamily() 方法设置字体的名称,如 “Arial”、“Times New Roman” 等。
      • 字体大小(Size):
        • setPointSize() 方法用于设置字体的大小,通常以点为单位。
      • 字体风格(Style):
        • setStyle() 方法可以设置字体的风格,如 QFont::StyleNormalQFont::StyleItalic 等。
      • 字体粗细(Weight):
        • 使用 setWeight() 方法设置字体的粗细,如 QFont::LightQFont::NormalQFont::Bold
      • 字体拉伸(Stretch):
        • setStretch() 方法用于设置字体的宽度拉伸,可以使得字体更宽或更窄。
      • 字体间距(Kerning):
        • setKerning() 方法可以启用或禁用字符间距调整,影响字符之间的空间。
      • 字体字距(Letter Spacing):
        • setLetterSpacing() 方法设置字符之间的水平间距。
      • 文本方向(Text Direction):
        • setStyleStrategy() 方法可以设置文本的方向,如从左到右或从右到左。
      • 固定宽度字体(Fixed Pitch):
        • setFixedPitch() 方法用于设置是否使用固定宽度字体。
      • 使用 QFont:
        • 创建 QFont 对象后,可以通过上述方法设置其属性,然后通过 QPainter::setFont() 方法将其应用到 QPainter 对象上。之后,使用 QPainter 绘制的文本将使用这个字体。

      示例:

      QFont font;
      font.setFamily("Helvetica");
      font.setPointSize(12);
      font.setBold(true); // 设置为粗体
      font.setStyleItalic(true); // 设置为斜体
      
      QPainter painter;
      painter.setFont(font);
      painter.drawText(10, 20, "Hello, World!"); // 使用字体绘制文本
      
    • 渐变填充

      • QLinearGradient线性渐变填充,颜色沿着直线从一点渐变到另一点。可以指定起点和终点,以及中间的颜色停止点来创建平滑的颜色过渡。

        • 起点和终点:
          • QLinearGradient 需要两个点来定义渐变的方向和范围:起始点和终点。渐变的颜色将从起始点开始,向终点过渡。
        • 颜色停止:
          • 使用 setColorAt() 方法可以设置颜色在渐变中的停止点。这个方法接受一个0到1之间的值,表示从起始点到终点的相对位置,以及一个 QColor 对象,表示该位置的颜色。
        • 渐变坐标系统:
          • 渐变的坐标系统可以是对象坐标系或绝对坐标系。通过 setCoordinateMode() 方法可以设置渐变的坐标模式。
        • 坐标模式:
          • QLinearGradient支持两种坐标模式:
            • ObjectBoundingMode:渐变是相对于使用渐变的对象的边界框。
            • StretchToDeviceMode:渐变是相对于整个设备(如画布或窗口)的尺寸。
        • 使用 QLinearGradient:
          • 创建 QLinearGradient 对象后,设置起始点、终点和颜色停止点,然后将它作为 QBrush 对象的样式来使用。

        示例:

        QLinearGradient gradient(0, 0, 100, 100); // 定义从左到右的渐变
        gradient.setColorAt(0.0, QColor("red"));    // 起始颜色为红色
        gradient.setColorAt(1.0, QColor("blue"));   // 结束颜色为蓝色
        
        QBrush brush(gradient); // 创建一个使用渐变的画刷
        QPainter painter;
        painter.setBrush(brush);
        painter.drawRect(10, 10, 100, 100); // 使用画刷填充矩形
        
      • QRadialGradient:径向渐变填充,颜色从中心点向外辐射,形成一个圆形或椭圆形的渐变效果。这种渐变通常用于创建按钮或图标的立体感。

      • QConicalGradient:圆锥形渐变填充,颜色从一个点向外辐射,但与径向渐变不同,圆锥形渐变的颜色过渡是沿着圆锥的侧面进行的,而不是沿着半径。这种渐变较少使用,但可以创造出独特的视觉效果。

  • QPaintDevice

    是一个可以使用QPainter进行绘图的抽象的二维界面

    常用的绘图设备:

    • QWidget
    • QPixmap、QImage:可用来绘制视频图片数据
  • QPaintEngine

    • 给QPainter提供在不同设备上绘图的接口
    • 应用程序一般无需与QPaintEngine打交道
    • 可以禁用QPaintEngine,使用DX或OPenGL自渲染
  • 绘图事件paintEvent()

    绘图事件,需要用户override

  • 坐标系:原点在左上角,x向左为正,y向下为正

  • 基本的绘图元素

    在Qt框架中,这些操作是通过QPainter类实现的。QPainter是一个低级的绘图类,提供了丰富的方法来绘制线条、形状、文本和图像。使用QPainter时,你通常会先创建一个QPainter对象,然后设置画笔、画刷等属性,最后调用相应的绘图函数来绘制内容。

      • drawPoint: 用于绘制单个点。
      • drawPoints: 用于绘制多个点。通常需要一个点的数组或列表作为参数。
    • 线
      • 直线: drawLine 用于绘制直线。通常需要两个点(起点和终点)作为参数。
      • 圆弧: drawArc 用于绘制圆或椭圆的一部分,即圆弧。需要指定圆心、半径、起始角度和结束角度。
    • 封闭的图形
      • 矩形
        • 普通矩形:drawRect: 用于绘制一个矩形的边框或填充整个矩形。
        • 圆角矩形:drawRoundedRect: 用于绘制带有圆角的矩形。
      • 弧弦:drawChord 用于绘制一个圆弧以及与圆弧两端点相连的直线,形成一个封闭的图形
      • 椭圆: drawEllipse 用于绘制椭圆的边框或填充整个椭圆
    • 任意路径绘制
      • drawPolygon:绘制一个多边形。传入的点列表中最后一个点会自动与第一个点相连,形成一个闭合的多边形。
      • drawPolyline:与drawPolygon类似,drawPolyline用于绘制一系列点连接成的折线,但最后一个点不会与第一个点相连,因此结果是开放的。
      • drawConvexPolygon:用于绘制任意凸多边形。凸多边形是指任意两个顶点之间的线段都不会与其他顶点相交的多边形。
      • drawLines:绘制一系列的线段。你需要传入一系列的起点和终点来定义这些线段。
      • drawPath:用于绘制更复杂的路径,可以包含直线段、曲线段等
      • drawPie:用于绘制扇形,即圆的一部分。你需要指定圆心、半径、起始角度和结束角度来定义扇形。
    • 图片绘制
      • drawPixmap: 用于在指定位置绘制QPixmap对象,QPixmap是Qt中用于存储图像数据的类。
      • drawImage: 类似于drawPixmap,但用于绘制QImage对象,QImage是另一个图像类,通常用于处理像素数据。
    • 绘制文本
      • drawText: 用于在指定位置绘制文本。你可以指定文本内容、位置、对齐方式等属性。
    • 其他操作
      • eraseRect: 用于擦除指定矩形区域内的内容,通常是用当前画笔的背景色来填充这个区域。
      • fillPath: 用于填充由QPainterPath定义的路径。如果设置了画笔颜色,路径内部会被填充,但不会绘制路径的轮廓线。
      • fillRect: 用于填充一个矩形区域,如果设置了画笔样式,矩形的边框也会被绘制。

二、常见的18种图形、路径、文字、图片绘制

  利用QTreeView类的信号void clicked(const QModelIndex &index);得到要绘制的图形,触发槽函数,槽函数用于设置绘图样式并调用update()函数触发paintEvent()绘图事件函数,在该函数中绘制已选择的图形。

示例:

drawtype.h

#pragma once
//基本画图枚举
enum class DRAW_TYPE
{
    point,
    multipoints,
    line,
    arc,
    rect,
    roundrect,
    chord,
    ellipse,
    polygon,
    polyline,
    ConvexPloygon,
    lines,
    path,
    pie,
    image,
    pixmap,
    draw_text,
    draw_erase,
    draw_fillpath,
    draw_fillrect
};

CPaintWidget.h

#pragma once
#include <QWidget>
#include "drawtype.h"

class CPaintWidget : public QWidget
{
	Q_OBJECT

public:
	CPaintWidget(QWidget* p = nullptr);
	~CPaintWidget() {}

	void setDrawType(DRAW_TYPE type);   //设置绘图类型

private:
	void paintEvent(QPaintEvent* event) override; //绘图事件

private:
        /* 绘制基本图形 */
        void draw_point();
        void draw_multipoints();
        void draw_line();
        void draw_arc();
        void draw_rect();
        void draw_roundrect();
        void draw_chord();
        void draw_ellipse();
        void draw_polygon();
        void draw_polyline();
        void draw_ConvexPloygon();
        void draw_lines();
        void draw_path();
        void draw_pie();
        void draw_image();
        void draw_pixmap();
        void draw_text();
        void draw_erase();
        void draw_fillpath();
        void draw_fillrect();
    
private:
	DRAW_TYPE m_drawType;

	int W = 0;
	int H = 0;
};

CPaintWidget.cpp

#include "CPaintWidget.h"
#include <QPainter>
#include <QPainterPath>

CPaintWidget::CPaintWidget(QWidget* p)
	:QWidget(p)
{
	this->setMinimumSize(800, 600);

	m_drawType = DRAW_TYPE::polygon;
}

void CPaintWidget::paintEvent(QPaintEvent* event)
{
	W = this->width();
	H = this->height();

	switch (m_drawType)
	{
	case DRAW_TYPE::point:
		draw_point();
		break;

	case DRAW_TYPE::multipoints:
		draw_multipoints();
		break;

	case DRAW_TYPE::line:
		draw_line();
		break;

	case DRAW_TYPE::arc:
		draw_arc();
		break;

	case DRAW_TYPE::rect:
		draw_rect();
		break;

	case DRAW_TYPE::roundrect:
		draw_roundrect();
		break;

	case DRAW_TYPE::chord:
		draw_chord();
		break;

	case DRAW_TYPE::ellipse:
		draw_ellipse();
		break;

	case DRAW_TYPE::polygon:
		draw_polygon();
		break;

	case DRAW_TYPE::polyline:
		draw_polyline();
		break;

	case DRAW_TYPE::ConvexPloygon:
		draw_ConvexPloygon();
		break;

	case DRAW_TYPE::lines:
		draw_lines();
		break;

	case DRAW_TYPE::path:
		draw_path();
		break;

	case DRAW_TYPE::pie:
		draw_pie();
		break;

	case DRAW_TYPE::image:
		draw_image();
		break;

	case DRAW_TYPE::pixmap:
		draw_pixmap();
		break;

	case DRAW_TYPE::draw_text:
		draw_text();
		break;

	case DRAW_TYPE::draw_erase:
		draw_erase();
		break;

	case DRAW_TYPE::draw_fillpath:
		draw_fillpath();
		break;

	case DRAW_TYPE::draw_fillrect:
		draw_fillrect();
		break;

	default:
		break;
	}
}

void CPaintWidget::draw_point()
{
	QPainter painter(this);
	QPen pen;
	pen.setWidth(10);
	pen.setColor(Qt::red);
	pen.setStyle(Qt::SolidLine);
	painter.setPen(pen);
	painter.drawPoint(QPoint(W / 2, H / 2));
}

void CPaintWidget::draw_multipoints()
{
	QPainter painter(this);
	QPen pen;
	pen.setWidth(10);
	pen.setColor(Qt::blue);
	pen.setStyle(Qt::SolidLine);
	painter.setPen(pen);

	// 画很多点
	QPoint points[] = {
	QPoint(5 * W / 12,H / 4),
	QPoint(3 * W / 4, 5 * H / 12),
	QPoint(2 * W / 4, 5 * H / 12) };
	painter.drawPoints(points, 3);
}

void CPaintWidget::draw_line()
{
	QPainter painter(this);

	//QLine Line(W / 4, H / 4, W / 2, H / 2);
	QLine Line(W / 4, H / 4, W / 2, H / 4);
	painter.drawLine(Line);
}

void CPaintWidget::draw_arc()
{
	QPainter painter(this);
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	int startAngle = 90 * 16;
	int spanAngle = 90 * 16;       //旋转 90°
	painter.drawArc(rect, startAngle, spanAngle);
}

void CPaintWidget::draw_rect()
{
	QPainter painter(this);

	// 画矩形
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	painter.drawRect(rect);
}

void CPaintWidget::draw_roundrect()
{
	QPainter painter(this);

	// 画圆角矩形
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	painter.drawRoundedRect(rect, 20, 20);
}

void CPaintWidget::draw_chord()
{
	QPainter painter(this);
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	int startAngle = 90 * 16;
	int spanAngle = 90 * 16;
	painter.drawChord(rect, startAngle, spanAngle);
}

void CPaintWidget::draw_ellipse()
{
	QPainter painter(this);

	QRect rect(W / 4, H / 4, W / 2, H / 2);
	painter.drawEllipse(rect);
}

void CPaintWidget::draw_polygon()
{
	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing);

	QPen pen;
	pen.setWidth(10);
	pen.setColor(Qt::red);
	pen.setStyle(Qt::SolidLine);
	pen.setCapStyle(Qt::SquareCap);
	pen.setJoinStyle(Qt::MiterJoin);  //画笔断点的样式
	painter.setPen(pen);

	QBrush brush;
	brush.setColor(Qt::yellow);
	brush.setStyle(Qt::SolidPattern);
	painter.setBrush(brush);

	// 画多边形,最后一个点会和第一个点闭合
	QPoint points[] = {
	QPoint(5 * W / 12,H / 4),
	QPoint(3 * W / 4,5 * H / 12),
	QPoint(5 * W / 12,3 * H / 4),
	QPoint(2 * W / 4,5 * H / 12) };
	painter.drawPolygon(points, 4);
}

void CPaintWidget::draw_polyline()
{
	QPainter painter(this);

	// 画多点连接的线,最后一个点不会和第一个点连接
	QPoint points[] = {
	QPoint(5 * W / 12, H / 4),
	QPoint(3 * W / 4, 5 * H / 12),
	QPoint(5 * W / 12, 3 * H / 4),
	QPoint(2 * W / 4, 5 * H / 12)};
	painter.drawPolyline(points, 4);
}

void CPaintWidget::draw_ConvexPloygon()
{
	QPainter painter(this);

	QPoint points[4] = {
		QPoint(5 * W / 12, H / 4),
		QPoint(3 * W / 4, 5 * H / 12),
		QPoint(5 * W / 12, 3 * H / 4),
		QPoint(W / 4, 5 * H / 12)};

	painter.drawConvexPolygon(points, 4);
}

void CPaintWidget::draw_lines()
{
	QPainter painter(this);

	// 画一批直线
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	QVector<QLine> Lines;
	Lines.append(QLine(rect.topLeft(), rect.bottomRight()));
	Lines.append(QLine(rect.topRight(), rect.bottomLeft()));
	Lines.append(QLine(rect.topLeft(), rect.bottomLeft()));
	Lines.append(QLine(rect.topRight(), rect.bottomRight()));
	painter.drawLines(Lines);
}

void CPaintWidget::draw_path()
{
	QPainter painter(this);
	// 绘制由QPainterPath对象定义的路线
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	QPainterPath path;
	path.addEllipse(rect);
	path.addRect(rect);
	painter.drawPath(path);
}

void CPaintWidget::draw_pie()
{
	QPainter painter(this);
	// 绘制扇形
	QRect    rect(W / 4, H / 4, W / 2, H / 2);
	int startAngle = 40 * 16; //起始 40。
	int spanAngle = 120 * 16; //旋转 120。 
	painter.drawPie(rect, startAngle, spanAngle);
}

void CPaintWidget::draw_image()
{
	QPainter painter(this);

	// 在指定的矩形区域内绘制图片
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	QImage image(":/resources/tupian.png");
	painter.drawImage(rect, image);
}

void CPaintWidget::draw_pixmap()
{
	QPainter painter(this);
	// 绘制Pixmap图片
	QRect    rect(W / 4, H / 4, W / 2, H / 2);
	QPixmap    pixmap(":/resources/tupix.png");
	painter.drawPixmap(rect, pixmap);
}

void CPaintWidget::draw_text()
{
	QPainter painter(this);

	// 绘制文本,只能绘制单行文字,字体的大小等属性由QPainter::font()决定。
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	QFont font;
	font.setPointSize(30);
	font.setBold(true);
	painter.setFont(font);
	painter.drawText(rect, "Hello,Qt");
}

void CPaintWidget::draw_erase()
{
	QPainter painter(this);

	// 擦除某个矩形区域,等效于用背景色填充该区域
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	painter.eraseRect(rect);
}

void CPaintWidget::draw_fillpath()
{
	QPainter painter(this);

	// 填充某个QPainterPath定义的绘图路径,但是轮廓线不显示
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	QPainterPath path;
	path.addEllipse(rect);
	path.addRect(rect);
	painter.fillPath(path, Qt::red);
}

void CPaintWidget::draw_fillrect()
{
	QPainter painter(this);
	// 填充一个矩形,无边框线
	QRect rect(W / 4, H / 4, W / 2, H / 2);
	painter.fillRect(rect, Qt::green);
}

void CPaintWidget::setDrawType(DRAW_TYPE type)
{
	m_drawType = type;
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <memory>
#include <QTreeView>
#include "CPaintWidget.h"

using namespace std;


class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    void treeView();

private slots:
    void treeViewExpand(const QModelIndex &index);

private:
    QTreeView* m_pLeftTree = nullptr;
    CPaintWidget* m_pPaintWidget = nullptr;
};

#endif // WIDGET_H

widget.cpp

#pragma execution_character_set("utf-8")

#include "widget.h"
#include <QStandardItemModel>
#include <QHBoxLayout>
#include "drawtype.h"

Widget::Widget(QWidget* parent)
    : QWidget(parent)
{
    setWindowTitle(u8"draw all");

    QHBoxLayout* pHLay = new QHBoxLayout(this);
    m_pLeftTree = new QTreeView(this);
    m_pLeftTree->setEditTriggers(QAbstractItemView::NoEditTriggers);  //设置不可编辑
    m_pLeftTree->setFixedWidth(300);

    m_pPaintWidget = new CPaintWidget(this);
    pHLay->addWidget(m_pLeftTree);
    pHLay->addWidget(m_pPaintWidget);

    treeView();
}

Widget::~Widget()
{
   
}

void Widget::treeView()
{
    m_pLeftTree->setFrameShape(QFrame::NoFrame);

    QStandardItemModel* model = new QStandardItemModel(m_pLeftTree);
    model->setHorizontalHeaderLabels(QStringList() << "draw all");

    QStandardItem* pParentItem = NULL;
    QStandardItem* pChildItem = NULL;

    // 点
    pParentItem = new QStandardItem(QIcon(":/resources/point.png"), "draw ponit");
    model->appendRow(pParentItem);

    pChildItem = new QStandardItem(QIcon(":/resources/point.png"), "point");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/multipoints.png"), "multipoints");
    pParentItem->appendRow(pChildItem);

    // 线
    pParentItem = new QStandardItem(QIcon(":/resources/line.png"), "draw line");
    model->appendRow(pParentItem);

    pChildItem = new QStandardItem(QIcon(":/resources/line.png"), "line");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/arc.png"), "arc");
    pParentItem->appendRow(pChildItem);

    // 封闭的图形
    pParentItem = new QStandardItem(QIcon(":/resources/rect.png"), "draw rect");
    model->appendRow(pParentItem);

    pChildItem = new QStandardItem(QIcon(":/resources/rect.png"), "rect");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/roundrect.png"), "roundrect");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/chord.png"), "chord");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/ellipse.png"), "ellipse");
    pParentItem->appendRow(pChildItem);

    // 任意路径绘制
    pParentItem = new QStandardItem(QIcon(":/resources/polygon.png"), "draw polygon");
    model->appendRow(pParentItem);

    pChildItem = new QStandardItem(QIcon(":/resources/polygon.png"), "polygon");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/polyline.png"), "polyline");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/ConvexPloygon.png"), "ConvexPloygon");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/lines.png"), "lines");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/path.png"), "path");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/pie.png"), "pie");
    pParentItem->appendRow(pChildItem);

    // 图片绘制
    pParentItem = new QStandardItem(QIcon(":/resources/image.png"), "draw image");
    model->appendRow(pParentItem);

    pChildItem = new QStandardItem(QIcon(":/resources/image.png"), "image");
    pParentItem->appendRow(pChildItem);

    pChildItem = new QStandardItem(QIcon(":/resources/pixmap.png"), "pixmap");
    pParentItem->appendRow(pChildItem);

    // 文本绘制
    pParentItem = new QStandardItem(QIcon(":/resources/text.png"), "draw text");
    model->appendRow(pParentItem);

    // 擦除
    pParentItem = new QStandardItem(QIcon(":/resources/erase.png"), "draw erase");
    model->appendRow(pParentItem);

    // 路径填充
    pParentItem = new QStandardItem(QIcon(":/resources/fillpath.png"), "draw fillpath");
    model->appendRow(pParentItem);

    // 矩形填充
    pParentItem = new QStandardItem(QIcon(":/resources/fillrect.png"), "draw fillrect");
    model->appendRow(pParentItem);

    m_pLeftTree->setModel(model);   //设置要展示的模型

    connect(m_pLeftTree, &QAbstractItemView::clicked, this, &Widget::treeViewExpand);
}

void Widget::treeViewExpand(const QModelIndex& index)
{
    QString text = index.data().toString();

    if (text.compare("point") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::point);
        /*调用QWidget的update()函数触发重写 paintEvent() 函数*/
        m_pPaintWidget->update();  //不更新就不会立即显示
    }
    else if (text.compare("multipoints") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::multipoints);
        m_pPaintWidget->update();  
    }
    else if (text.compare("line") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::line);
        m_pPaintWidget->update();
    }
    else if (text.compare("arc") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::arc);
        m_pPaintWidget->update();
    }
    else if (text.compare("rect") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::rect);
        m_pPaintWidget->update();
    }
    else if (text.compare("roundrect") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::roundrect);
        m_pPaintWidget->update();
    }
    else if (text.compare("chord") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::chord);
        m_pPaintWidget->update();
    }
    else if (text.compare("ellipse") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::ellipse);
        m_pPaintWidget->update();
    }
    else if (text.compare("polygon") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::polygon);
        m_pPaintWidget->update();
    }
    else if (text.compare("polyline") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::polyline);
        m_pPaintWidget->update();
    }
    else if (text.compare("ConvexPloygon") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::ConvexPloygon);
        m_pPaintWidget->update();
    }
    else if (text.compare("lines") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::lines);
        m_pPaintWidget->update();  
    }
    else if (text.compare("path") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::path);
        m_pPaintWidget->update();
    }
    else if (text.compare("pie") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::pie);
        m_pPaintWidget->update();
    }
    else if (text.compare("image") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::image);
        m_pPaintWidget->update();
    }
    else if (text.compare("pixmap") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::pixmap);
        m_pPaintWidget->update();
    }
    else if (text.compare("draw text") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::draw_text);
        m_pPaintWidget->update();
    }
    else if (text.compare("draw erase") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::draw_erase);
        m_pPaintWidget->update();
    }
    else if (text.compare("draw fillpath") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::draw_fillpath);
        m_pPaintWidget->update();
    }
    else if (text.compare("draw fillrect") == 0)
    {
        m_pPaintWidget->setDrawType(DRAW_TYPE::draw_fillrect);
        m_pPaintWidget->update();
    }
}

main.cpp

#include "widget.h"

#include <QApplication>


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Widget w;
    w.show();
    return a.exec();
}

运行结果

image-20240709161702260

三、Qt移动鼠标绘制任意形状

  在Qt中,实现移动鼠标绘制任意形状通常涉及到鼠标事件的处理,如 mousePressEventmouseMoveEventmouseReleaseEventmouseDoubleClickEventpaintEvent

void paintEvent(QPaintEvent *) override;
void mousePressEvent(QMouseEvent *e) override;       //按下
void mouseMoveEvent(QMouseEvent *e) override;        //移动
void mouseReleaseEvent(QMouseEvent *e) override;     //松开
void mouseDoubleClickEvent(QMouseEvent *event) override;     //双击
  • 鼠标按下事件 (mousePressEvent):

    当用户按下鼠标按钮时触发。标志开始绘制

    // 按下
    void MyPainterWidget::mousePressEvent(QMouseEvent *e)
    {
        if (e->button() == Qt::LeftButton)
        {
            if(!m_bStartDraw)
            {
                pointList.clear();
                m_bStartDraw = true;
            }
        }   
    }
    
  • 鼠标移动事件 (mouseMoveEvent):

    当鼠标移动时触发,如果按下了鼠标按钮,则可以在绘图事件中进行绘制,并标志处于绘制时的鼠标移动状态

    // 移动
    void MyPainterWidget::mouseMoveEvent(QMouseEvent *e)
    {
        if(m_bStartDraw)
        {
            movePoint = e->pos();
            
            this->update();
    
            // 先刷新再设为true, 防止第一点和(0,0)连在一块
            bMove = true;
        }
    }
    
  • 鼠标释放事件 (mouseReleaseEvent):

    当用户释放鼠标按钮时触发,将鼠标松开后的点需要添加到路径中

    // 松开
    void MyPainterWidget::mouseReleaseEvent(QMouseEvent *e)
    {
        if (e->button() == Qt::LeftButton)
        {
            if (m_bStartDraw)
            {
                // 鼠标松开后的点需要添加到路径中
                pointList.push_back(QPointF(e->x(), e->y()));
                bMove = false;
                this->update();
            }
        }
    }
    
  • 鼠标双击事件(mouseDoubleClickEvent):

    当用户双击鼠标时,结束绘制

    // 双击结束绘制
    void MyPainterWidget::mouseDoubleClickEvent(QMouseEvent *event)
    {
        endDraw();
    }
    
    void MyPainterWidget::endDraw()
    {
        m_bStartDraw = false;
    
        //需要把第一个点连起来
        pointList.push_back(pointList[0]);
        this->update();
    }
    
  • 绘图事件(paintEvent):

    QWidget 的派生类中重写 paintEvent 来实现自定义绘制逻辑

    void MyPainterWidget::paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        painter.setPen(QColor(255,0,0));
    
        QVector<QLineF> lines;
    
        for(int i = 0; i < pointList.size()-1; i++)
        {
            QLineF line(QPointF(pointList[i].x(), pointList[i].y()), 
                QPointF(pointList[i+1].x(), pointList[i+1].y()));
    
            lines.push_back(line);
        }
    	
        //绘制鼠标移动状态
        if (m_bStartDraw)
        {
            int size = pointList.size();
    
            if (bMove && size > 0)
            {
                QLineF line(QPointF(pointList[pointList.size() - 1].x(), pointList[pointList.size() - 1].y()),
                    movePoint);
    
                lines.push_back(line);
            }
        }
    
        painter.drawLines(lines);	//画多条线
    }
    

示例:

需要维护布尔变量来跟踪是否处于绘制模式与移动模式

MyPainterWidget.h

#ifndef GRAPHICSPAINTER_H
#define GRAPHICSPAINTER_H

#include <QWidget>

class MyPainterWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MyPainterWidget(QWidget *parent = nullptr);

    void endDraw();     //结束绘制
    void clearPath();   //清除路径

protected:
    void paintEvent(QPaintEvent *) override;
    void mousePressEvent(QMouseEvent *e) override;       //按下
    void mouseMoveEvent(QMouseEvent *e) override;        //移动
    void mouseReleaseEvent(QMouseEvent *e) override;     //松开
    void mouseDoubleClickEvent(QMouseEvent *event) override;     //双击

    bool m_bStartDraw = false;    //是否已经开始左键点击,同时标识是否开始进行绘制
    bool bMove = false;           //是否处于绘制时的鼠标移动状态

    QVector<QPointF> pointList;
    QPointF movePoint;
};

#endif // GRAPHICSPAINTER_H

MyPainterWidget.cpp

#include "MyPainterWidget.h"
#include <QPainter>
#include <QMouseEvent>

MyPainterWidget::MyPainterWidget(QWidget *parent) : QWidget(parent)
{
    setMouseTracking(true);  //窗口启用鼠标捕获

    pointList.clear();
}

void MyPainterWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setPen(QColor(255,0,0));

    QVector<QLineF> lines;

    for(int i = 0; i < pointList.size()-1; i++)
    {
        QLineF line(QPointF(pointList[i].x(), pointList[i].y()), 
            QPointF(pointList[i+1].x(), pointList[i+1].y()));

        lines.push_back(line);
    }

    if (m_bStartDraw)
    {
        int size = pointList.size();

        if (bMove && size > 0)
        {
            QLineF line(QPointF(pointList[pointList.size() - 1].x(), pointList[pointList.size() - 1].y()),
                movePoint);

            lines.push_back(line);
        }
    }

    painter.drawLines(lines);
}

// 按下
void MyPainterWidget::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        if(!m_bStartDraw)
        {
            pointList.clear();
            m_bStartDraw = true;
        }
    }   
}

// 移动
void MyPainterWidget::mouseMoveEvent(QMouseEvent *e)
{
    if(m_bStartDraw)
    {
        movePoint = e->pos();
        
        this->update();

        // 先刷新再设为true, 防止第一点和(0,0)连在一块
        bMove = true;
    }
}

// 松开
void MyPainterWidget::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        if (m_bStartDraw)
        {
            // 鼠标松开后的点需要添加到路径中
            pointList.push_back(QPointF(e->x(), e->y()));
            bMove = false;
            this->update();
        }
    }
}

// 双击结束绘制
void MyPainterWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    endDraw();
}

void MyPainterWidget::endDraw()
{
    m_bStartDraw = false;

    //需要把第一个点连起来
    pointList.push_back(pointList[0]);
    this->update();
}

void MyPainterWidget::clearPath()
{
    pointList.clear();
    this->update();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMenu>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

protected:
    // 右键菜单需要重写的类
    //处理窗口的右键点击事件,以弹出一个菜单
    void contextMenuEvent(QContextMenuEvent* event) override;

private:
    Ui::MainWindow *ui;

    QMenu* m_pMenu;
};
#endif // MAINWINDOW_H

mainwindow.cpp

/*
使用方式
左键点击,移动鼠标开始绘制,双击左键结束绘制,或者右键点击结束绘制
*/

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMenu>
#include <QAction>


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

	m_pMenu = new QMenu(this);

	QAction* pAc1 = new QAction(QString::fromLocal8Bit("结束绘制"), this);
	pAc1->setShortcut(QKeySequence("Ctrl+E"));
	QAction* pAc2 = new QAction(QString::fromLocal8Bit("清除"), this);
	pAc2->setShortcut(QKeySequence("Ctrl+D"));

	// 这是个假动作,为了让菜单消失,且不影响绘制路径
	QAction* pAc3 = new QAction(QString::fromLocal8Bit("退出菜单"), this);

	m_pMenu->addAction(pAc1);
	m_pMenu->addAction(pAc2);
	m_pMenu->addAction(pAc3);

	m_pMenu->setStyleSheet("QMenu{font:18px;}");

	connect(pAc1, &QAction::triggered, [=] {
		ui->graphicsPainter->endDraw();
		});

	connect(pAc2, &QAction::triggered, [=] {
		ui->graphicsPainter->clearPath();
		});
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 这段代码的作用是当用户在 MainWindow 中通过鼠标右键触发上下文菜单时,将一个已经创建好的菜单 m_pMenu 移动到鼠标当前的位置,并显示出来。
void MainWindow::contextMenuEvent(QContextMenuEvent* event)
{
	m_pMenu->move(cursor().pos());  //将菜单的位置设置为当前鼠标光标的位置
	m_pMenu->show();  //显示菜单
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

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

运行结果

image-20240709192615096

四、Qt绘制带三角形箭头的窗口

  本案例:在点击按钮时,弹出带三角形箭头的窗口(含窗口阴影)。

  • QPainterPath组合绘制

    // 小三角区域,这里是等腰三角形
    QPolygon trianglePolygon;
    trianglePolygon << QPoint(m_startX, m_triangleHeight + SHADOW_WIDTH);
    trianglePolygon << QPoint(m_startX + m_triangleWidth / 2, SHADOW_WIDTH);
    trianglePolygon << QPoint(m_startX + m_triangleWidth, m_triangleHeight + SHADOW_WIDTH);
    
    QPainterPath drawPath;
    drawPath.addRoundedRect(QRect(SHADOW_WIDTH, SHADOW_WIDTH + m_triangleHeight,
                                  this->width() - SHADOW_WIDTH * 2, this->height() - SHADOW_WIDTH * 2 - 								m_triangleHeight),
                    		BORDER_RADIUS, BORDER_RADIUS);
    // 矩形路径加上三角形
    drawPath.addPolygon(trianglePolygon);
    
    // 绘制路径
    painter.drawPath(drawPath);
    

示例:

  首先,绘制顶层窗口

userinfowidget.h

#ifndef USERINFOWIDGET_H
#define USERINFOWIDGET_H

#include <QWidget>

namespace Ui {
class UserInfoWidget;
}

class UserInfoWidget : public QWidget
{
    Q_OBJECT

public:
    explicit UserInfoWidget(QWidget *parent = nullptr);
    ~UserInfoWidget();

private:
    Ui::UserInfoWidget *ui;
};

#endif // USERINFOWIDGET_H

userinfowidget.cpp

#include "userinfowidget.h"
#include "ui_userinfowidget.h"

UserInfoWidget::UserInfoWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::UserInfoWidget)
{
    ui->setupUi(this);

    setFixedSize(this->width(), this->height());

    setAttribute(Qt::WA_StyledBackground);
    QString qss = "QLabel{font-family:Microsoft YaHei; font-size:18px; color:#ffffff; background-color:#363636;}";

    ui->label_UserImage->setText("");
    QPixmap pmp1(":/resources/user_image.png");
    pmp1.scaled(ui->label_UserImage->size(), Qt::KeepAspectRatio);
    ui->label_UserImage->setScaledContents(true);
    ui->label_UserImage->setPixmap(pmp1);

    ui->label_UserName->setText(u8"没有做完的梦最痛");
    ui->label_UserName->setStyleSheet(qss);

    ui->label_VipInfo->setText("");
    QPixmap pmp2(":/resources/vipinfo.png");
    pmp2.scaled(ui->label_VipInfo->size(), Qt::KeepAspectRatio);
    ui->label_VipInfo->setScaledContents(true);
    ui->label_VipInfo->setPixmap(pmp2);
}

UserInfoWidget::~UserInfoWidget()
{
    delete ui;
}

  接着,绘制带三角的窗口(含窗口阴影)

triangledialog.h

/*

带三角形箭头的对话框

*/

#ifndef TRIANGLEDIALOG_H
#define TRIANGLEDIALOG_H

#include <QDialog>

class TriangleDialog : public QDialog
{
    Q_OBJECT
public:
    explicit TriangleDialog(int fixedW, int fixedH, QWidget *parent = nullptr);

    // 设置小三角行的起始位置、宽、高
    void setTriangleInfo(int startX, int width, int height);

protected:
    void paintEvent(QPaintEvent *e);

private:
    // 小三角形的起始位置
    int m_startX;

    // 小三角形的宽度
    int m_triangleWidth;

    // 小三角形的高度
    int m_triangleHeight;
};

#endif // TRIANGLEDIALOG_H

triangledialog.cpp

#include "triangledialog.h"
#include <QVBoxLayout>
#include <QPainter>
#include <QGraphicsDropShadowEffect>
#include <QPainterPath>
#include "userinfowidget.h"

#define SHADOW_WIDTH    15                 // 窗口阴影宽度
#define TRIANGLE_WIDTH  15                 // 小三角行的宽度
#define TRIANGLE_HEIGHT 10                 // 小三角行的高度
#define BORDER_RADIUS   5                  // 窗口圆角


TriangleDialog::TriangleDialog(int fixedW, int fixedH, QWidget *parent) : QDialog(parent)
  ,m_startX(50)
  ,m_triangleWidth(TRIANGLE_WIDTH)
  ,m_triangleHeight(TRIANGLE_HEIGHT)
{
    setWindowFlags(Qt::FramelessWindowHint | Qt::Popup | Qt::NoDropShadowWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);  // 设置透明窗口, 为窗口阴影和圆角做准备

    UserInfoWidget* pUserinfoWidget = new UserInfoWidget(this);

    QVBoxLayout* pVlay = new QVBoxLayout(this);
    pVlay->addWidget(pUserinfoWidget);

    //设置布局的四周边距
    pVlay->setContentsMargins(SHADOW_WIDTH, SHADOW_WIDTH + TRIANGLE_HEIGHT, SHADOW_WIDTH, SHADOW_WIDTH);

    // 设置阴影边框
    auto shadowEffect = new QGraphicsDropShadowEffect(this);
    shadowEffect->setOffset(0, 0);
    shadowEffect->setColor(Qt::red);
    shadowEffect->setBlurRadius(SHADOW_WIDTH);
    this->setGraphicsEffect(shadowEffect);

    //设置窗口固定大小
    setFixedSize(fixedW, fixedH);
}

void TriangleDialog::setTriangleInfo(int startX, int width, int height)
{
    m_startX = startX;
    m_triangleWidth = width;
    m_triangleHeight = height;
}

void TriangleDialog::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(55, 55, 55));

    // 小三角区域,这里是等腰三角形
    QPolygon trianglePolygon;
    trianglePolygon << QPoint(m_startX, m_triangleHeight + SHADOW_WIDTH);
    trianglePolygon << QPoint(m_startX + m_triangleWidth / 2, SHADOW_WIDTH);
    trianglePolygon << QPoint(m_startX + m_triangleWidth, m_triangleHeight + SHADOW_WIDTH);

    QPainterPath drawPath;
    drawPath.addRoundedRect(QRect(SHADOW_WIDTH, SHADOW_WIDTH + m_triangleHeight,
                                  this->width() - SHADOW_WIDTH * 2, this->height() - SHADOW_WIDTH * 2 - m_triangleHeight),
                            BORDER_RADIUS, BORDER_RADIUS);
    // 矩形路径加上三角形
    drawPath.addPolygon(trianglePolygon);

    // 绘制路径
    painter.drawPath(drawPath);
}

  然后,调用 show() 方法会触发 paintEvent 来绘制窗口。

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "triangledialog.h"
#include <QLabel>
#include "userinfowidget.h"
#include <QPropertyAnimation>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    int triangle_start_x = 60;

    TriangleDialog *pDialog = new TriangleDialog(343, 320, this);
    pDialog->setTriangleInfo(triangle_start_x, 20, 12);

    // 设置三角窗口的弹出位置, 有Qt::Popup属性
    QPoint p1 = ui->pushButton->mapToGlobal(QPoint(0, 0));  //按钮左上角相对于桌面的绝对位置
    QRect rect1 = ui->pushButton->rect();

    int x = p1.x() + rect1.width() / 2 - triangle_start_x - 20 / 2;
    int y = p1.y() + rect1.height() + 1 - 15;
    pDialog->move(x, y);

    pDialog->show();
}

main.cpp

#include "widget.h"

#include <QApplication>

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

运行结果

image-20240709203859903

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

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

相关文章

Linux开发板(正点原子阿尔法_IMX6U)QT5.12.9交叉编译到ARM开发板(已解决)

问题记录&#xff1a;Qt下ctrlR直接构建项目&#xff0c;然后在build-01_led-Desktop_Qt_5_12_9_GCC_64bit-Debugz中将构建的执行文件&#xff0c;scp到ARM开发板下&#xff0c;发现通过指令./01_led后出现以下报错。 问题原因&#xff1a;因为Qt构建默认使用的是64bit的gcc&am…

钉钉扫码登录第三方

钉钉文档 实现登录第三方网站 - 钉钉开放平台 (dingtalk.com) html页面 将html放在 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>登录</title>// jquery<script src"http://code.jqu…

【优先级队列PriorityQueue】

目录 1&#xff0c;优先级队列 1.1 概念 2&#xff0c;优先级队列的模拟实现 2.1 堆的概念 2.2 堆的存储方式 2.3 堆的创建 2.3.1 堆的向下调整&#xff08;大根堆&#xff09; 2.3.2 建堆的时间复杂度​编辑 2.4 堆的插入与删除 2.4.1 堆的插入 2.4.2 堆的删除 3&a…

香橙派5plus上跑云手机方案二 waydroid

前言 上篇文章香橙派5plus上跑云手机方案一 redroid(带硬件加速)说了怎么跑带GPU加速的redroid方案&#xff0c;这篇说下怎么在香橙派下使用Waydroid。 温馨提示 虽然能运行&#xff0c;但是体验下来只能用软件加速&#xff0c;无法使用GPU加速&#xff0c;所有会很卡。而且…

SpringCloudAlibaba Nacos配置中心与服务发现

目录 1.配置 1.1配置的特点 只读 伴随应用的整个生命周期 多种加载方式 配置需要治理 1.2配置中心 2.Nacos简介 2.1特性 服务发现与服务健康检查 动态配置管理 动态DNS服务 服务和元数据管理 3.服务发现 1.配置 应用程序在启动和运行的时候往往需要读取一些配置信…

AI古风插画视频:成都亚恒丰创教育科技有限公司

AI古风插画视频&#xff1a;科技与传统美学的诗意交融 在数字技术的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;以其惊人的学习能力与创造力&#xff0c;正逐步渗透并重塑着艺术的边界。成都亚恒丰创教育科技有限公司其中&#xff0c;AI古风插画视频作为一股清流&a…

Windows 黑暗模式是什么意思?如何开启它?

随着计算机和移动设备的普及&#xff0c;长时间盯着屏幕已经成为现代人生活和工作的常态。为了减轻眼睛疲劳&#xff0c;并在低光环境中提供更舒适的视觉体验&#xff0c;许多操作系统和应用程序都引入了黑暗模式&#xff08;Dark Mode&#xff09;。 Windows 黑暗模式就是其中…

Xubuntu24.04之图形界面挂载硬盘(二百六十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

双端队列,双指针

思路&#xff1a; 其实很容易想到是双指针或者双端队列。 我们设置一个type表示当前区间已经有了多少种厨师&#xff0c;同时还需要记录区间中每个元素出现的次数&#xff0c;然后比较棘手的是移动问题了&#xff0c;什么时候移动呢&#xff1f; 我们可以发现当区间当队头元…

静脉分割YOLOV8-SEG

静脉分割&#xff0c;YOLOV8*SEG资源-CSDN文库 首先使用YOLOV8-SEG训练&#xff0c;得到PT模型&#xff0c;然后转换成ONNX&#xff0c;OPENCV的DNN调用&#xff0c;从而摆脱PYTORCH依赖&#xff0c;支持C,PYTHON,ANDROID调用

STM32G474使用HRTIM触发多路ADC采样,通过DMA传输,通过串口打印显示,实现PWM中间时刻采样,避免开关噪声

本工程使用CUBEIDE进行配置以及编译调试&#xff0c;使用的硬件为STM32G474官方开发板NUCLEO-G474RE CUBEIDE配置 HRTIM配置 本章工程使用HRTIM定时器进行ADC的触发&#xff0c;打开主定时器&#xff0c;子定时器A,B,C。&#xff08;本工程未使用到A与C定时器&#xff0c;配置…

项目进度计划、软件部署、调试方案

项目进度计划 项目计划工期:合计3000日历天完成整体项目交付。 软件部署 办公管理系统是一项复杂、长期的系统工程,为保证业务系统能够顺利地进行实施,必须要制定科学、合理、切实可行的实施计划。一方面要从组织上进行落实,成立强有力的项目领导小组和经验丰富的项目实…

【中台建设-Word资料】企业数字化转型之数据中台架构、大数据支撑平台、资源库建设方案

通过中台建设实现企业能力复用&#xff0c;包括能力整合、业务创新、业务和数据闭环、组织模式演进等。 数字能力整合 企业的数字能力一般包括数字化营销、数字化产品、数字化供应链、数字化生产、数字化运营等。企业的数字化能力的充分利用&#xff0c;从而达到可持续发展。数…

2024年上海市安全员C3证证考试题库及上海市安全员C3证试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年上海市安全员C3证证考试题库及上海市安全员C3证试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试…

一键式创建GTest TDD测试平台

适用于C GTest测试平台搭建。直接上python脚本。 #!/usr/bin/env python3 # -*- coding: utf-8 -*-import argparse import os import platform import subprocess from xml.etree import ElementTree as ETdefault_root_path "d:\\test\\UTtest"class DeveloperTe…

江苏职教高考 计算机 C语言 复习资料

江苏职教高考计算机专业考试内容为 文化课专业课 其中专业课包含&#xff1a; 计算机原理45分 计算机组维45分 计算机网络60分 C语言 6080分 电子电工90分 具体资料可查看链接 链接&#xff1a;https://pan.baidu.com/s/1OXD-zK4V3NsLLDMwfXcTlA?pwd2822 提取码&…

Matlab|基于改进鲸鱼优化算法的微网系统能量优化管理matlab-源码

目录 一、主要内容 二、部分代码 三、运行结果 四、下载链接 一、主要内容 该程序为《基于改进鲸鱼优化算法的微网系统能量优化管理》源码&#xff0c;主要内容如下&#xff1a; 针对包含多种可再生能源的冷热电联供型微网系统的能量优化问题&#xff0c;为了优化其运行过程…

24小时悬停系留照明无人机技术详解

24小时悬停系留照明无人机是一款专门设计用于提供长时间、高效能照明服务的无人机系统。该系统结合了无人机技术与先进的照明设备&#xff0c;通过系留技术实现无人机的稳定悬停&#xff0c;从而提供连续不断的照明服务。该无人机能够在各种环境条件下进行24小时不间断工作&…

请编写函数,判断一字符串是否是回文,若是回文函数返回值为1,否则返回值为0,回文是顺读和倒读都一样的字符串

int gets_arr(char* p) {int i 0;int j strlen(p) - 1;while (i < j && p[i] p[j]){i;j--;}if (i<j){return 0;}else {return 1;}} int main() {printf("请输入一串字符串\n");char arr[100];gets(arr);int ret gets_arr(arr);if (ret 1){printf(…

Linux文件编程(打开/创建写入读取移动光标)

目录 一、如何在Linux下做开发 1.vi编辑器 2.gcc编译工具 3.常用指令 二、文件打开及创建 三、写入文件 四、读取文件 五、文件“光标”位置 一、如何在Linux下做开发 所谓文件编程&#xff0c;就是对文件进行操作&#xff0c;Linux的文件和Windows系统的文件大差不差…