Qt实现流动的管道效果代码示例

news2024/11/25 10:37:36

在现代图形用户界面(GUI)应用程序中,动态效果可以显著增强用户体验。本文将介绍如何使用Qt框架实现一个流动的管道效果。我们将通过自定义QWidget来绘制管道,并使用定时器来实现流动效果。

1. 准备工作

首先,确保你已经安装了Qt开发环境。如果没有,可以从Qt官方网站下载并安装。

2. 创建项目

打开Qt Creator,创建一个新的Qt Widgets应用程序项目。我们将在这个项目中实现流动的管道效果。

3. 自定义QWidget

我们将创建一个自定义的QWidget类来绘制管道并实现流动效果。以下是类的定义和实现:

#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QPainterPath>

class PipeWidget : public QWidget {
    Q_OBJECT

public:
    PipeWidget(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;

private slots:
    void updateOffset();

private:
    int m_offset;
    void drawPipe(QPainter &painter);
};

PipeWidget::PipeWidget(QWidget *parent)
    : QWidget(parent), m_offset(0)
{
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &PipeWidget::updateOffset);
    timer->start(50); // 每50毫秒更新一次
}

void PipeWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);

    drawPipe(painter);
}

void PipeWidget::updateOffset()
{
    m_offset += 5;
    if (m_offset > width()) {
        m_offset = 0;
    }
    update(); // 重绘窗口
}

void PipeWidget::drawPipe(QPainter &painter)
{
    QPainterPath path;
    path.moveTo(0, height() / 2);
    path.cubicTo(width() / 3, height() / 2 - 50, 2 * width() / 3, height() / 2 + 50, width(), height() / 2);

    QLinearGradient gradient(0, 0, width(), 0);
    gradient.setColorAt(0, Qt::blue);
    gradient.setColorAt(1, Qt::green);

    painter.setBrush(gradient);
    painter.setPen(Qt::NoPen);

    for (int i = 0; i < 10; ++i) {
        painter.save();
        painter.translate(m_offset - i * width() / 10, 0);
        painter.drawPath(path);
        painter.restore();
    }
}

4. 将自定义QWidget添加到主窗口

在主窗口的UI文件中,添加一个QWidget,并将其提升为我们的自定义QWidget类。具体步骤如下:

  1. 打开主窗口的UI文件。
  2. 添加一个QWidget到主窗口。
  3. 右键点击QWidget,选择“提升为...”。
  4. 在弹出的对话框中,填写提升的类名为PipeWidget,并添加头文件路径。
  5. 点击“添加”,然后点击“提升”。

5. 运行项目

现在,你可以运行项目,看到流动的管道效果。管道会从左到右流动,并且循环往复。

#include <QApplication>

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

    HorizontalFlowPipeWidget widget;
    widget.resize(800, 400);
    widget.show();

    return app.exec();
}

上述实现的原理介绍:

translate 方法是 QPainter 类中的一个方法,用于平移(移动)绘图坐标系。通过调用 translate 方法,可以改变绘图的原点位置,从而实现图形的平移效果。

translate 方法的签名如下:

void QPainter::translate(const QPointF &offset);
void QPainter::translate(const QPoint &offset);
void QPainter::translate(qreal dx, qreal dy);

其中,offset 是一个 QPointF 或 QPoint 类型的点,表示平移的偏移量;dx 和 dy 分别表示在 x 轴和 y 轴方向上的平移量。

QPainterPath 是 Qt 框架中的一个类,用于创建和操作复杂的 2D 图形路径。它提供了一种方便的方式来定义和操作各种形状,如线条、曲线、矩形、椭圆等。QPainterPath 可以包含多个子路径,每个子路径可以是一个简单的形状或一个复杂的图形。

QPainterPath 的主要用途包括:

  1. 绘制复杂图形:通过组合多个基本形状和路径,可以创建复杂的图形。
  2. 路径操作:可以对路径进行平移、旋转、缩放等变换操作。
  3. 填充和描边:可以对路径进行填充和描边,使用不同的画笔和画刷样式。
  4. 剪裁:可以将路径用作剪裁区域,限制绘图操作的范围。

以下是一些 QPainterPath 的常见用法示例:

QPainterPath path;

// 添加一个矩形
path.addRect(10, 10, 80, 50);

// 添加一个椭圆
path.addEllipse(100, 10, 80, 50);

// 添加一个贝塞尔曲线
path.moveTo(200, 30);
path.cubicTo(250, 10, 300, 50, 350, 30);

// 使用 QPainter 绘制路径
QPainter painter(this);
painter.setPen(Qt::blue);
painter.setBrush(Qt::green);
painter.drawPath(path);

通过这些方法,你可以创建复杂的图形路径,并在 Qt 应用程序中进行绘制和操作。

使用 QLinearGradient 绘制了一个从左上角到右下角的线性渐变效果。渐变的颜色从红色过渡到绿色,再过渡到蓝色。通过调整 setColorAt 方法的参数,可以控制渐变过程中不同位置的颜色。

QLinearGradient 是 Qt 框架中的一个类,用于创建线性渐变效果。线性渐变是指颜色从一个点线性地过渡到另一个点。QLinearGradient 可以用于填充图形、控件背景等,以实现平滑的颜色过渡效果。

QLinearGradient 的主要构造函数如下:

QLinearGradient(const QPointF &start, const QPointF &finalStop);

其中,start 是渐变的起始点,finalStop 是渐变的结束点。你可以通过调用 setColorAt 方法来设置渐变过程中不同位置的颜色。

使用 QPainterPath 绘制了一条水平曲线。通过使用 QTimer 定时更新偏移量 m_offset,我们可以实现水平流动的效果。每次定时器触发时,偏移量会增加,然后调用 update() 函数重绘窗口,从而实现水平流动的视觉效果。

cubicTo 方法用于绘制三次贝塞尔曲线。三次贝塞尔曲线由四个点定义:起始点、两个控制点和结束点。cubicTo 方法的签名如下:

void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint);

其中,c1 和 c2 是两个控制点,endPoint 是结束点。通过调整这些点的位置,可以控制曲线的形状。

6. 进一步完善

上述代码示例中,显示的动态曲线为贝塞尔曲线,在流动管道中,我们可能想要的是如三角形状的箭头符号。且管道的方向可能有上下左右四个方向,实现的方法是不一样的。可以自定义实现个Widget,继承自QLabel。

在界面上使用时,可以将 QLabel 提升为自定义的 QWidget。在 Qt 中,提升(Promotion)是一种机制,允许你将一个标准的 Qt 控件(如 QLabel)提升为一个自定义的控件(如自定义的 QWidget)。这样,你可以在设计器中使用标准的控件,但在运行时使用自定义的控件。

以上效果的代码实现:

#ifndef PIPEWIDGET_H
#define PIPEWIDGET_H

#include <QLabel>
#include <QPainter>
#include <QTimer>
#include <QPainterPath>

class PipeWidget : public QLabel
{
public:
    PipeWidget(QWidget *parent=0);

    enum Direction {
            Up,
            Down,
            Left,
            Right
        };

    void setDirection(Direction newDirect);

    void setStart(bool newStart);

protected:
    void paintEvent(QPaintEvent *event) override;

private slots:
    void updateOffset();

private:
    int  m_offset;
    bool m_start = false;
    Direction m_direct = Up; // 默认向上
    void upDirect(QPainter &painter);    //向上
    void downDirect(QPainter &painter);  //向下
    void leftDirect(QPainter &painter);  //左向
    void rightDirect(QPainter &painter); //右向
};

#endif // PIPEWIDGET_H

 

#include "pipewidget.h"


PipeWidget::PipeWidget(QWidget *parent):QLabel(parent), m_offset(0) {
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &PipeWidget::updateOffset);
    timer->start(50); // 每50毫秒更新一次
}


void PipeWidget::paintEvent(QPaintEvent *event) {
    Q_UNUSED(event);
    if(m_start){
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing, true);
        //m_direct = 1;
        switch (m_direct) {
            case Up: upDirect(painter);break;
            case Down: downDirect(painter);break;
            case Left: leftDirect(painter);break;
            case Right: rightDirect(painter);break;
            default:break;
        }
    }
}

void PipeWidget::updateOffset() {
    switch (m_direct) {
    case Up:{
        //上
        m_offset -= 5;
        if (m_offset < 0) {
            m_offset = height() / 2;
        }
    }break;
    case Down:{
        //下
        m_offset += 5;
        if (m_offset > (height() / 2)) {
            m_offset = 0;
        }
    }break;
    case Left:{
        //左
        m_offset -= 5;
        if (m_offset < 0) {
            m_offset = width() / 2;
        }
    }break;
    case Right:{
        //右
        m_offset += 5;
        if (m_offset > (width() / 2)) {
            m_offset = 0;
        }
    }break;
    default:break;
    }

    update();
}

void PipeWidget::setStart(bool newStart)
{
    m_start = newStart;
}

void PipeWidget::setDirection(Direction newDirect)
{
    m_direct = newDirect;
}

void PipeWidget::upDirect(QPainter &painter)
{
    QPainterPath path;
    //朝上的三角形
    path.moveTo(width() / 2 , height()/2-15); // 三角形顶点
    path.lineTo(0 , height()/2); // 三角形左下角
    path.lineTo(width(),height()/2); // 三角形右下角
    path.closeSubpath(); // 闭合路径

    QLinearGradient gradient(0, 0, 0, height());
    gradient.setColorAt(0, Qt::blue);
    gradient.setColorAt(0, Qt::green);

    painter.setBrush(gradient);
    painter.setPen(Qt::NoPen);

    for (int i = 0; i < 10; ++i) {
        painter.save();
        painter.translate(0, m_offset - i *  (height() / 4) );
        painter.drawPath(path);
        painter.restore();
    }
}

void PipeWidget::downDirect(QPainter &painter)
{
    QPainterPath path;
    //朝下的三角形
    path.moveTo(width() / 2 , height()/2+15); // 三角形顶点
    path.lineTo(0 , height()/2); // 三角形左下角
    path.lineTo(width(),height()/2); // 三角形右下角
    path.closeSubpath(); // 闭合路径

    QLinearGradient gradient(0, 0, 0, height());
    gradient.setColorAt(0, Qt::blue);
    gradient.setColorAt(0, Qt::green);

    painter.setBrush(gradient);
    painter.setPen(Qt::NoPen);

    for (int i = 0; i < 10; ++i) {
        painter.save();
        painter.translate(0, m_offset - i *  (height() / 4) );
        painter.drawPath(path);
        painter.restore();
    }
}

void PipeWidget::leftDirect(QPainter &painter)
{
    QPainterPath path;
    //贝塞尔曲线
    //path.moveTo(width() / 2, 0);
    //path.cubicTo(width() / 2 - 50, height() / 3, width() / 2 + 50, 2 * height() / 3, width() / 2, height());
    //朝左的三角形
    path.moveTo(width() / 2 , 0); // 三角形顶点
    path.lineTo(width() / 2 , height()); // 三角形左下角
    path.lineTo(width()/2-15,height()/2); // 三角形右下角
    path.closeSubpath(); // 闭合路径

    QLinearGradient gradient(0, 0, 0, height());
    gradient.setColorAt(0, Qt::blue);
    gradient.setColorAt(0, Qt::green);

    painter.setBrush(gradient);
    painter.setPen(Qt::NoPen);

    for (int i = 0; i < 10; ++i) {
        painter.save();
        painter.translate(m_offset - i *  (width() / 4) , 0);
        painter.drawPath(path);
        painter.restore();
    }
}

void PipeWidget::rightDirect(QPainter &painter)
{
    QPainterPath path;
    //贝塞尔曲线
    //path.moveTo(width() / 2, 0);
    //path.cubicTo(width() / 2 - 50, height() / 3, width() / 2 + 50, 2 * height() / 3, width() / 2, height());
    //朝右的三角形
    path.moveTo(width() / 2 , 0); // 三角形顶点
    path.lineTo(width() / 2 , height()); // 三角形左下角
    path.lineTo(width()/2+15,height()/2); // 三角形右下角
    path.closeSubpath(); // 闭合路径

    QLinearGradient gradient(0, 0, 0, height());
    gradient.setColorAt(0, Qt::blue);
    gradient.setColorAt(0, Qt::green);

    painter.setBrush(gradient);
    painter.setPen(Qt::NoPen);

    for (int i = 0; i < 10; ++i) {
        painter.save();
        //painter.translate(0, 0);
        //painter.translate(m_offset - i * width() / 2 , 0);
        painter.translate(m_offset - i *  (width() / 4) , 0);
        painter.drawPath(path);
        painter.restore();
    }
}

使用:

void MainWindow::on_pushButton_clicked()
{
    //ui->pipe->show();
    //向左
    ui->lb_h->setDirection(PipeWidget::Right);
    ui->lb_h->setStart(true);
    //向上
    ui->lb_v->setDirection(PipeWidget::Down);
    ui->lb_v->setStart(true);
}

 

7. 总结

通过本文的介绍,我们学习了如何使用Qt框架实现一个流动的管道效果。我们创建了一个自定义的QWidget类,并使用定时器和绘图API来实现流动效果。这个示例展示了Qt强大的图形绘制和动画功能,可以用于创建各种动态效果的GUI应用程序。

希望这篇文章对你有所帮助,欢迎进一步探索Qt框架的其他功能和特性。

其他资源

qt 虚线流动效果实现_qt制作 流动 虚线-CSDN博客

 

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

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

相关文章

【Linux进阶】磁盘分区2——MBR和GPT

1.磁盘的分区 因为如果你的磁盘被划分成两个分区&#xff0c;那么每个分区的设备文件名是什么&#xff1f; 在了解这个问题之前&#xff0c;我们先来复习一下磁盘的组成&#xff0c;因为现今磁盘的划分与它物理的组成很有关系。 我们谈过磁盘主要由碟片、机械手臂、磁头与主轴马…

计算机视觉 图像融合技术概览

在许多计算机视觉应用中(例如机器人运动和医学成像),需要将来自多幅图像的相关信息集成到一幅图像中。这种图像融合将提供更高的可靠性、准确性和数据质量。 多视图融合可以提高图像的分辨率,同时恢复场景的 3D 表示。多模态融合结合了来自不同传感器的图像,称为多传感器融…

【一念发动便是行】念头,就是命运

一个个恶念累积就是负能量&#xff0c;念头就是命运&#xff0c;克除恶念&#xff0c;防范念头&#xff0c;念头都有能量&#xff0c;学圣学须内外庄严检肃&#xff0c;言语有灵 多数人的问题都是出在念头上&#xff0c;念头&#xff0c;就是自己的命运&#xff1b; 当我们对自…

windows电脑网络重置后wifi列表消失怎么办?

我们的电脑网络偶尔会出现异常&#xff0c;我们通常会下意识选择网络诊断&#xff0c;运行完诊断后一般会让我们选择重置网络&#xff0c;然而&#xff0c;重置后wifi列表突然消失&#xff0c;无法愉快地上网了&#xff0c;找了一圈&#xff0c;都说是更改适配器选项&#xff0…

船舶雷达与导航系统选择7/8防水插座的原因分析

概述 船舶雷达与导航系统在现代航海中扮演着至关重要的角色&#xff0c;它们为船舶提供准确的导航信息&#xff0c;确保航行的安全和效率。在这些系统中&#xff0c;7/8防水插座的使用尤为重要&#xff0c;因为它们能够在恶劣的海上环境中提供稳定的电力和信号连接。接下来&am…

CASS7.0按方向和距离绘制图形

1、绘制工具 2、按方向和距离绘制 &#xff08;1&#xff09;切换方向 &#xff08;2&#xff09;距离输入

欧科云链大咖对话:Web3原生创新静默期,科技巨头却在两极化发展

出品&#xff5c;OKG Research 作者&#xff5c;Hedy Bi 上周末&#xff0c;欧科云链研究院接受FT中文的邀请&#xff0c;作为圆桌嘉宾参与了由FT中文网与上海交通大学上海高级金融学院联合主办的金融大师课。在圆桌环节&#xff0c;笔者与各位教授和金融行业科技创新前沿实践…

【UE5.3】笔记8 添加碰撞,检测碰撞

添加碰撞 打开BP_Food,添加Box Collision组件&#xff0c;与unity类似&#xff1a; 调整Box Collision的大小到刚好包裹物体&#xff0c;通过调整缩放和盒体范围来控制大小&#xff0c;一般先调整缩放找个大概大小&#xff0c;然后调整盒体范围进行微调。 碰撞检测 添加好碰撞…

MySQL中的DDL语句

第一题 输入密码登录mysql&#xff0c;创建数据库zoo&#xff0c;转换到zoo数据库&#xff0c; mysql> create database zoo character set gbk; mysql> use zoo查看创建数据库zoo信息 mysql> show create database zoo;删除数据库zoo mysql> drop database zo…

用requirements.txt配置环境

1. 在anaconda创建环境 创建Python版本为3.8的环境&#xff0c;与yolov5所需的包适配。 2. 在Anaconda Prompt中激活环境 (base) C:\Users\吴伊晴>conda activate yolov5 3. 配置环境 用指定路径中的requirements.txt配置环境。 (yolov5) C:\Users\吴伊晴>pip insta…

如何使用Maxscript访问C#类库?

本教程帮助你一步一步集成maxscript文档和C#类库&#xff0c;以便你能够在maxscript提供的相当有限的环境中访问dotnet框架的强大功能。这让你可以使用maxscript做一些功能更强大的事情&#xff0c;比如访问数据库、获取web部署的内容等等。 还是直接上教程实惠&#xff1a; …

Linux:进程间通信(一.初识进程间通信、匿名管道与命名管道、共享内存)

上次结束了基础IO&#xff1a;Linux&#xff1a;基础IO&#xff08;三.软硬链接、动态库和静态库、动精态库的制作和加载&#xff09; 文章目录 1.认识进程间通信2.管道2.1匿名管道2.2pipe()函数 —创建匿名管道2.3匿名管道的四种情况2.4管道的特征 3.基于管道的进程池设计4.命…

一些linux指令(转:一次服务器被入侵的处理过程分享)

https://www.cnblogs.com/operationhome/p/16637763.html 1.查找详细的入侵痕迹 last grep Accepted /var/log/secure 2.查看最近新增的一些用户 cat /etc/passwd 3.查看定时任务的日志 vi /var/log/cron 4.查看开机启动脚本 cat /etc/rc.local

单元测试Spring 上下文加载过程中遇到的阻塞或死锁问题

IDEA单元测试一直转圈&#xff0c;阻塞&#xff0c;前置后置的方法都不执行&#xff0c;无任何输出 1.单元测试类 SpringBootTest(classes {BareMetalApplication.class}) RunWith(SpringRunner.class) public class K8sUserNfsStoreInitServiceImplTest {BeforeEachpublic…

如何使用 SwiftUI 构建 visionOS 应用

文章目录 前言WindowsVolumes沉浸式空间结论 前言 Apple Vision Pro 即将推出&#xff0c;现在是看看 SwiftUI API 的完美时机&#xff0c;这使我们能够将我们的应用程序适应 visionOS 提供的沉浸式世界。苹果表示&#xff0c;构建应用程序的最佳方式是使用 Swift 和 SwiftUI。…

【C++】 解决 C++ 语言报错:Undefined Reference

文章目录 引言 未定义引用&#xff08;Undefined Reference&#xff09;是 C 编程中常见的错误之一&#xff0c;通常在链接阶段出现。当编译器无法找到函数或变量的定义时&#xff0c;就会引发未定义引用错误。这种错误会阻止生成可执行文件&#xff0c;影响程序的正常构建。本…

气压传感器在自动驾驶汽车还有哪些应用场景

气压传感器在近年来被广泛应用于各种新兴领域&#xff0c;以下是其中几个最新的应用&#xff1a; 1、自动驾驶汽车&#xff1a;自动驾驶汽车需要精确的气压传感器来监测道路上的气压变化&#xff0c;帮助车辆进行准确的定位和导航。气压传感器可以提供高精度、可靠的气压数据&…

2.ast快速脚本熟悉ast类型-cnblog

观察ast ast更换结果使a3 // 引入Node.js的文件系统模块&#xff0c;用于读写文件 const fs require(fs); const types require("babel/types") // 引入babel/parser模块&#xff0c;用于将源代码转换成AST&#xff08;抽象语法树&#xff09; const parser requ…

【东奥会计-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

存算一体架构或成为AI处理器技术发展关键

©作者|坚果 来源|神州问学 引言 马斯克巨资60亿美元打造的“超级算力工场”&#xff0c;通过串联10万块顶级NVIDIA H100 GPU&#xff0c;不仅震撼了AI和半导体行业&#xff0c;促使英伟达股价应声上涨6%&#xff0c;还强烈暗示了AI大模型及芯片需求的急剧膨胀。这一行动…