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

news2024/9/19 10:36:38

文章目录

  • 一、基本绘图技术介绍
  • 二、常见的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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

tableau饼图与环形图绘制 - 5

饼图与环形图绘制 1. 饼图绘制1.1 饼图绘制1.2 显示标签1.3 显示合计百分比1.4 设置百分比格式1.5 添加说明1.6 图像导出 2. 环形图绘制2.1 选择标记为饼图2.1 饼图绘制2.2 双饼图绘制2.3 设置大饼图大小2.4 设置双轴2.5 设置小饼图属性 1. 饼图绘制 酒店价格等级饼图 1.1 饼…

初识数组(二)

目录 1. 二维数组的初始化 1&#xff09; 不完全初始化 2&#xff09; 完全初始化 3&#xff09; 按照行初始化 4&#xff09; 初始化时省略行&#xff0c;但是不能省略列 2.二维数组的使用 1&#xff09; 二维数组的下标 2&#xff09;二维数组的输入和输出 3. 二维数…

Python实现吃豆人游戏详解(内附完整代码)

一、吃豆人游戏背景 吃豆人是一款由Namco公司在1980年推出的经典街机游戏。游戏的主角是一个黄色的小圆点&#xff0c;它必须在迷宫中吃掉所有的点数&#xff0c;同时避免被四处游荡的幽灵捉到。如果玩家能够吃掉所有的点数&#xff0c;并且成功避开幽灵&#xff0c;就可以进入…

小试牛刀--对称矩阵压缩存储

学习贺利坚老师对称矩阵压缩存储 数据结构实践——压缩存储的对称矩阵的运算_计算压缩存储对称矩阵 a 与向量 b 的乘积-CSDN博客 本人解析博客 矩阵存储和特殊矩阵的压缩存储_n阶对称矩阵压缩-CSDN博客 版本更新日志 V1.0: 对老师代码进行模仿 , 我进行名字优化, 思路代码注释 …

VMware Workstation 和 Fusion 现对个人用户宣布免费使用

这周笔记本坏了&#xff0c;重装了系统&#xff0c;部分软件也得重装&#xff0c;其中就有 VMware Workstation&#xff0c; 当我还在上网苦苦找破解包时&#xff08;个人学习使用&#xff09;&#xff0c;发现现在居然免费了…… 原来 VMware 换了新东家&#xff0c;简单回顾下…

DMA方式的知识点笔记

苏泽 “弃工从研”的路上很孤独&#xff0c;于是我记下了些许笔记相伴&#xff0c;希望能够帮助到大家 目录 1. DMA基本概念 2. DMA传送过程 易错点 DMA控制器操作流程 3. DMA传送方式 这是单总线的结果 &#xff08;CPU说了算 所以不会产生于CPU的冲突&#xff09; 这…

谷歌摸鱼神器来了:推出AI会议替身,一键总结提问发言_会议预约 ai对话

饱受会议折磨的打工人&#xff0c;终于可以解放了&#xff01; 就在刚刚举办的Google Cloud Next’23大会上&#xff0c;谷歌宣布了一系列科技新进展&#xff0c;最瞩目的要属其中的“开会AI替身”了。 只需要一句“帮我参加”&#xff0c;AI就能替你开会&#xff0c;并在合适…

SCI二区TOP|蜘蛛黄蜂优化算法(SWO)原理及实现【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2023年&#xff0c;M Abdel-Basset受到蜘蛛黄蜂优化社会行为启发&#xff0c;提出了蜘蛛黄蜂优化算法&#xff08;Spider Wasp Optimizer, SWO&#xff09;。 2.算法原理 2.1算法思想 S…

python04——类(基础new)

类其实也是一种封装的思想&#xff0c;类就是把变量、方法等封装在一起&#xff0c;然后可以通过不同的实例化对其进行调用操作。 1.类的定义 class 类名&#xff1a; 变量a def __init__ (self,参数2&#xff0c;参数2...)&#xff1a;初始化函数&#xff01;&#xff01;&…

【LeetCode】生命游戏

目录 一、题目二、解法完整代码 一、题目 根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一…

Qt 音频编程实战项目

一Qt 音频基础知识 QT multimediaQMediaPlayer 类&#xff1a;媒体播放器&#xff0c;主要用于播放歌曲、网络收音 机等功能。QMediaPlaylist 类&#xff1a;专用于播放媒体内容的列表。 二 音频项目实战程序 //版本5.12.8 .proQT core gui QT multimedia greate…

C++入门基础(2)

目录 一、引用: 1、定义&#xff1a; 2、特性&#xff1a; 3、引用的使用&#xff1a; 4、const引用&#xff1a;控制权限 const引用定义: const引用可以接收3种对象&#xff1a; 1、正常对象&#xff1a; 2、临时对象&#xff1a; 3、const对象&#xff1a; 总结&…

Sorted Set 类型命令(命令语法、操作演示、命令返回值、时间复杂度、注意事项)

Sorted Set 类型 文章目录 Sorted Set 类型zadd 命令zrange 命令zcard 命令zcount 命令zrevrange 命令zrangebyscore 命令zpopmax 命令bzpopmax 命令zpopmin 命令bzpopmin 命令zrank 命令zscore 命令zrem 命令zremrangebyrank 命令zremrangebyscore 命令zincrby 命令zinterstor…

vue子组件调用父组件方法

父组件 页面<popoverss ref"pop" :goodspop"goodspop"></popoverss>子组件components: {"popoverss": () > import(../comm/popover.vue)},方法goodspop(e){console.log(e"----")return 9999;},子组件 方法props:[go…

【转载】目标检测mAP的含义

转载自三叔家的猫 https://blog.csdn.net/qq_39056987 https://blog.csdn.net/qq_39056987/article/details/104348493 <div id"content_views" class"markdown_views prism-atom-one-light"><svg xmlns"http://www.w3.org/2000/svg" s…

不用服务器 | 我搭建了一个属于自己的GPT聊天应用!!!

原文地址&#xff1a;aiutools.fun/archives/5118 平台限制部分内容未显示&#xff0c;详情请访问原文。 展示 不废话&#xff0c;直接上干货&#xff01; 我这里搭建的Lobe Chat 支持 聊天TTS & STT 语音会话文生图各种优秀的插件 下面搭建好的样子 前期准备 需要…

(2)滑动窗口算法练习:无重复字符的最长子串

无重复字符的最长子串 题目链接&#xff1a;3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的最长子串的长度。 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是"a…

Java字符串(String、字符串拼接、原理)

文章目录 一、String字符串1.1创建方式【直接赋值、new一个对象】1.1.1 使用字符串字面值直接赋值&#xff1a;&#xff08;1&#xff09;字符串字面量创建String对象的转换过程&#xff08;2&#xff09;一些方法&#xff08;3&#xff09;说明 1.1.2 使用new关键字创建字符串…

Java如何自定义注解及在SpringBoot中的应用

注解 注解&#xff08;Annotation&#xff09;&#xff0c;也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性&#xff0c;与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面&#xff0c;用来对这些元素进行说…

最快视频转绘-AnimateDiff-Lightning

最快视频转绘-AnimateDiff-Lightning Video-to-Video Generation AnimateDiff-Lightning 非常适合视频到视频的生成。使用 ControlNet 提供最简单的 comfyui 工作流程。 「ComfyUI-aki-v1.3.7z」链接&#xff1a;https://pan.quark.cn/s/199a753292d8 下载导入工作流 animat…