Qt绘制动态仪表

news2024/12/28 19:29:06

背景:

项目需要,可能需要做一些仪表显示。此篇除了介绍实现方法,还要说明心路历程。对我而言,重要的是心理,而不是技术。写下来也是自勉。

本人起初心里是比较抵触的,从业20多年了,深知所谓界面,实际上最根本还是代码实现的,操作系统层面多少年来一步步发展更新,是靠不断封装演变来的。所谓的可视化开发,主要还是靠ide提供的插件控件,通常只是应用。如果要按照自己的想法做个什么,那就是绘图机制,把代码变成图不用想就知道麻烦。基于这种心理,就是再说qt可以做得很炫,个人表示一看就够那种,根本看不进去。

终究为了活下去,所以要保住工作,也是对技术的不服输,所以不忙时打开qt示例,看它的文档。其实看过很多遍了,主要是看不进去。终于要善于提炼代码,看它的主要部分,缩小规模,然后研读手册,必要时看看别人怎么说的,逐个尝试,最后形成自己的总结,就学会了。
下面只介绍干货。

用法总结:

所谓绘图机制,肯定用到paintEvent函数,每一个能看到的窗体都是使用操作系统的重绘机制显示的。说人话就是,绘图的过程要写在paintEvent函数里,只要做了更新显示操作,记着调用update函数,它会调用paintEvent函数重绘界面。

qt绘图就一个QPainter,所有事都是它干的。QPen用于设置边框边线,QBrush用于设置填充。
painter画任何东西都是有个draw***函数。

画任何东西都基于坐标系,所以要先操作坐标系,再画。比如想画仪表盘刻度,设定好间隔(循环),转一次坐标系,画一个短线,最后转完一圈,也就画了一圈刻度。

painter就是画画,肯定后画的覆盖先画的,跟图层似的。

因为能旋转坐标系,所以要记住前后的坐标系状态,所以前面调用QPainter::save(),最后调用QPainter::restore()。比如画仪表,开始画背景是正常坐标系,接着通过旋转坐标系画刻度,最后要在正常坐标系下画表轴或者其它东西。这中间,旋转坐标系那部分,就需要暂存painter状态,画完这部分再恢复。

具体下面例子说明。

下面无论我画什么,都不再贴mainwindow代码了,没有必要,每个仪表都是一个类,使用时,在界面画一个label,提升为这个类即可。

手绘仪表盘:

思路是:

先画个黑色矩形当背景;

再旋转坐标系画刻度;

通过一个全局的成员变量值,作为指针旋转角度值,通过旋转坐标系把指针画到相应位置;

最后画一个转轴盖住指针(其实也可以把指针别画那么长,留出圆心位置即可)。

我鸟语不行,注释部分,看官您忽略就好。至于表芯转轴,你们说叫hand heart还是center?要不咱别较真的了,主要看代码。

guage_watertemp.h

/**************************************************************************************************
 ** File name:      guage_watertemp.h (Guage_WaterTemp)
 ** Created by:     Henrick.Nie at 2024-9-8
 ** Used for:       The car water temprature guage.
 **************************************************************************************************/
#ifndef GUAGE_WATERTEMP_H
#define GUAGE_WATERTEMP_H

#include <QLabel>
#include <QPainter>
#include <QDebug>

class Guage_WaterTemp : public QLabel
{
    Q_OBJECT

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

    void f_SetTemp(const qreal &realTemp);

private:
    qreal m_realTemp, m_realMin = 25, m_realMax = 155;

    void paintEvent(QPaintEvent *event);

    void f_Draw(QPainter *p);
};


#endif // GUAGE_WATERTEMP_H

guage_watertemp.cpp

#include "guage_watertemp.h"

Guage_WaterTemp::Guage_WaterTemp(QWidget *parent) :
    QLabel(parent)
{
}
void Guage_WaterTemp::paintEvent(QPaintEvent *event)
{
    QLabel::paintEvent(event);
    QPainter painter(this);
    f_Draw(&painter);
}
void Guage_WaterTemp::f_Draw(QPainter *p)
{
    QColor colorHand = QColor(Qt::red);

    //Init the size data.
    int iHeartSize = width() / 5;
    int iX0 = width() / 2, iY0 = height() - iHeartSize;
    int iBackWidth = width(), iBackHeight = width() / 2 + iHeartSize * 1.5 ;
    int iBackLeft = 0 - iBackWidth / 2, iBackTop = 0 - iBackWidth / 2 - iHeartSize / 2;
    int iMarkSize = iBackWidth / 40, iMarklength = iBackWidth / 20;
    int iHandSize = iBackWidth / 25, iHandLeft = iBackLeft + width() / 20, iHandLength = iBackWidth / 2 - iBackWidth / 20;
    int iText30Left = iBackLeft + width() / 28, iText30Top = 0 - width() / 15;
    int iText90Left = 0 - width() / 18, iText90Top = 0 - width() / 4;
    int iText150Left = width() / 3.5, iText150Top = iText30Top;

    //Init the origin.
    p->setRenderHint(QPainter::Antialiasing);
    p->translate(iX0, iY0);

    //Draw the black rect as the background.
    p->setPen(Qt::NoPen);
    p->setBrush(QBrush(Qt::black));
    p->drawRect(iBackLeft, iBackTop, iBackWidth, iBackHeight);

    //Draw the hand heart on the origin.
    p->setPen(QPen(QBrush(colorHand), iHeartSize, Qt::SolidLine, Qt::RoundCap));
    p->drawPoint(0, 0);

    //Draw the mark. (From 30 degrees to 150 degrees, 150-30=120, 120/10=12)
    p->save();
    p->rotate(30);
    for(int i = 30; i <= 150; ++i)
    {
        QBrush brush = (i < 140) ? QBrush(Qt::white) : QBrush(Qt::red);
        p->setPen(QPen(brush, iMarkSize));
        p->drawPoint(iBackLeft, 0);
        p->rotate(1);
    }
    p->restore();

    QList<int> iList_Big = { 30, 90, 150 };
    p->save();
    p->rotate(30);
    for (int i = 30; i <= 150; i += 12)
    {
        int iSize = iList_Big.contains(i) ? iMarkSize * 2 : iMarkSize;
        int iLeft = iList_Big.contains(i) ? iBackLeft + iMarkSize / 2 : iBackLeft;
        int iLength = iList_Big.contains(i) ? iMarklength + 10 : iMarklength;
        QBrush brush = (i < 130) ? QBrush(Qt::white) : QBrush(Qt::red);
        p->setPen(QPen(brush, iSize));
        p->drawLine(iLeft, 0, iLeft + iLength, 0);
        p->rotate(12);
    }
    p->restore();

    //Draw the mark text.
    p->setFont(QFont("", width() / 10));
    p->setPen(QPen(QBrush(Qt::white), width() / 18));
    p->drawText(iText30Left, iText30Top, "30");
    p->drawText(iText90Left, iText90Top, "90");
    p->drawText(iText150Left, iText150Top, "150");

    //Draw hands.
    p->save();
    p->setPen(QPen(QBrush(colorHand), iHandSize, Qt::SolidLine, Qt::RoundCap));
    if (m_realTemp < m_realMin)
    {
        m_realTemp = m_realMin;
    }
    if (m_realTemp > m_realMax)
    {
        m_realTemp = m_realMax;
    }
    p->rotate(m_realTemp);
    p->drawLine(iHandLeft, 0, iHandLeft + iHandLength, 0);
    p->restore();

    p->end();
}
void Guage_WaterTemp::f_SetTemp(const qreal &realTemp)
{
    m_realTemp = realTemp;
    update();
}

效果:

a962b05de3a946b48078be5bef15a3be.jpeg

其实我是仿照大众polo劲情的水温表画的。至于指针,既然是旋转肯定是角度。看到表盘的样式时,要大致估计一下,最左侧和左右侧的刻度大概是坐标轴上的多少度。算一下它的跨度,和实际需要的水温数值要有个转换。比方说,这个水温表的刻度刚好是水温和角度一一对应,所以不用转换。

图片仪表盘:

上面看到效果后会很有成就感,但要把仪表盘画得逼真可不容易,还要考虑缩放和字体,细节太多。因此有了这种方法,从网上找图片素材,自己加工仪表盘。直接先把图片画上去当背景,然后代码只控制表针即可。以后修改背景表盘,photoshop修图即可。

先看我找的素材:

d814b1bb900543c890953131dc6a1ac0.jpeg

再看我修好的效果:

f45cc417af744fc0a094cea34aceea04.jpeg4b797a398adc43fe92b78c8c647bf3b3.jpeg5931d8ffe67c407ba81f61899e8e73b8.jpegd7a770143027477db7d06663705ecf2e.jpeg

上代码:

polo_base.h

/**************************************************************************************************
 ** File name:      polo_baes.h (Polo_Base)
 ** Created by:     Henrick.Nie at 2024-9-9
 ** Used for:       The base class of the polo guage.
 **************************************************************************************************/

#ifndef POLO_BASE_H
#define POLO_BASE_H

#include <QLabel>
#include <QPainter>
#include <QDebug>

class Polo_Base : public QLabel
{
    Q_OBJECT
public:
    explicit Polo_Base(QWidget *parent = nullptr);

protected:
    QString m_sPicPath;

    virtual void f_SetValue(const qreal &realValue);

private:
    qreal m_realDegree, m_realMin = 40, m_realMax = 140;

    void paintEvent(QPaintEvent *event);

    void f_Draw(QPainter *p);
};

#endif // POLO_BASE_H

polo_base.cpp

#include "polo_base.h"

Polo_Base::Polo_Base(QWidget *parent) :
    QLabel(parent)
{

}
void Polo_Base::paintEvent(QPaintEvent *event)
{
    QLabel::paintEvent(event);
    QPainter painter(this);
    f_Draw(&painter);
}
void Polo_Base::f_Draw(QPainter *p)
{
    QColor colorHand = QColor(Qt::red);

    //Init the origin.
    int iX0 = width() / 2, iY0 = height() - width() / 4;
    p->setRenderHint(QPainter::Antialiasing);
    p->translate(iX0, iY0);

    //Draw the background picture.
    QPixmap pic(m_sPicPath);
    int iPicWidth = width(), iPicHeight = width() * pic.height() / pic.width();
    int iLeft = 0 - iPicWidth / 2, iTop = 0 - iPicHeight + width() / 4;
    p->drawPixmap(iLeft, iTop, width(), width() * pic.height() / pic.width(), pic);

    //Draw hands.
    int iHandSize = width() / 25, iHandLeft = iLeft - width() / 15, iHandLength = width() / 2 - width() / 20;
    p->save();
    p->setPen(QPen(QBrush(colorHand), iHandSize, Qt::SolidLine, Qt::RoundCap));
    if (m_realDegree < m_realMin)
    {
        m_realDegree = m_realMin;
    }
    if (m_realDegree > m_realMax)
    {
        m_realDegree = m_realMax;
    }
    p->rotate(m_realDegree);
    p->drawLine(iHandLeft, 0, iHandLeft + iHandLength, 0);
    p->restore();

    //Draw the hand heart on the origin.
    int iHeartSize = width() / 4;
    p->setPen(QPen(QBrush(QColor(Qt::darkGray)), iHeartSize, Qt::SolidLine, Qt::RoundCap));
    p->drawPoint(0, 0);

    p->end();
}
void Polo_Base::f_SetValue(const qreal &realValue)
{
    m_realDegree = realValue;
    update();
}


 polo_watertemp.h

/**************************************************************************************************
 ** File name:      polo_watertemp.h (Polo_WaterTemp)
 ** Created by:     Henrick.Nie at 2024-9-9
 ** Used for:       The water temprature guage of polo.
 **************************************************************************************************/

#ifndef POLO_WATERTEMP_H
#define POLO_WATERTEMP_H

#include "polo_base.h"

class Polo_WaterTemp : public Polo_Base
{
    Q_OBJECT
public:
    explicit Polo_WaterTemp(QWidget *parent = nullptr);

    void f_SetValue(const qreal &realValue) override;
};

#endif // POLO_WATERTEMP_H

polo_watertemp.cpp

#include "polo_watertemp.h"

Polo_WaterTemp::Polo_WaterTemp(QWidget *parent) :
    Polo_Base(parent)
{
    m_sPicPath = ":/images/polo_watertemp.jpg";
}
void Polo_WaterTemp::f_SetValue(const qreal &realValue)
{
    //The given water temprature value, it equals the degrees.
    Polo_Base::f_SetValue(realValue);
}

polo_fuel.h

/**************************************************************************************************
 ** File name:      polo_fuel.h (Polo_Fuel)
 ** Created by:     Henrick.Nie at 2024-9-9
 ** Used for:       The fuel guage of polo.
 **************************************************************************************************/

#ifndef POLO_FUEL_H
#define POLO_FUEL_H

#include "polo_base.h"

class Polo_Fuel : public Polo_Base
{
    Q_OBJECT
public:
    explicit Polo_Fuel(QWidget *parent = nullptr);

    void f_SetValue(const qreal &realValue) override;
};

#endif // POLO_FUEL_H

polo_fuel.cpp

#include "polo_fuel.h"

Polo_Fuel::Polo_Fuel(QWidget *parent) :
    Polo_Base(parent)
{
    m_sPicPath = ":/images/polo_fuel.jpg";
}
void Polo_Fuel::f_SetValue(const qreal &realValue)
{
    Polo_Base::f_SetValue(40 + realValue * 2.2);
}

燃料表这个要注意数值换算了。下面转速表也是。

tachometer.h

/**************************************************************************************************
 ** File name:      tachometer.h (TachoMeter)
 ** Created by:     Henrick.Nie at 2024-9-9
 **************************************************************************************************/

#ifndef TACHOMETER_H
#define TACHOMETER_H

#include <QLabel>
#include <QPainter>
#include <QDebug>

class TachoMeter : public QLabel
{
    Q_OBJECT
public:
    explicit TachoMeter(QWidget *parent = nullptr);

    void f_SetValue(const qreal &realValue);

private:
    qreal m_realValue = -45, m_realMin = -45, m_realMax = 225;

    void paintEvent(QPaintEvent *event);
    void f_Draw(QPainter* p);
};

#endif // TACHOMETER_H

tachometer.cpp

#include "tachometer.h"

TachoMeter::TachoMeter(QWidget *parent) :
    QLabel(parent)
{

}
void TachoMeter::paintEvent(QPaintEvent *event)
{
    QLabel::paintEvent(event);
    QPainter painter(this);
    f_Draw(&painter);
}
void TachoMeter::f_Draw(QPainter *p)
{
    //Init the size data.
    int iSideSize = qMin(width(), height());
    int iX0 = width() / 2, iY0 = height() / 2;

    //Init the origin.
    p->setRenderHint(QPainter::Antialiasing);
    p->translate(iX0, iY0);

    //Draw the background.
    p->setPen(Qt::NoPen);
    p->setBrush(QBrush(Qt::black));
    p->drawRect(0 - width() / 2, 0 - height() / 2, width(), height());

    QPixmap pic(":/images/tacho1.jpg");
    int iLeft = 0 - iSideSize / 2, iTop = 0 - iSideSize / 2;
    p->drawPixmap(iLeft, iTop, iSideSize, iSideSize, pic);

    //Draw the hand.
    p->save();
    p->setPen(QPen(QBrush(Qt::red), iSideSize / 40, Qt::SolidLine, Qt::RoundCap));
    if (m_realValue < m_realMin)
    {
        m_realValue = m_realMin;
    }
    if (m_realValue > m_realMax)
    {
        m_realValue = m_realMax;
    }
    p->rotate(m_realValue);
    p->drawLine(0 - iSideSize / 2 + iSideSize / 20, 0, 0, 0);
    p->restore();

    //center
    p->setPen(Qt::NoPen);
    p->setBrush(QBrush(QColor(Qt::darkGray)));
    p->drawEllipse(QPoint(0, 0), iSideSize / 20, iSideSize / 20);

    p->end();
}
void TachoMeter::f_SetValue(const qreal &realValue)
{
    //value: 0 to 800
    //degree: -45 to 225 (225-(-45))=270
    qreal realScale = 270.0 / 800.0; //per 10 rpm
    m_realValue = 0 - 45 + realValue * realScale;
    update();
}

效果:

06057548e3274a0897c4917e90f396a0.jpeg

效果解析:

上面效果图中,我在界面上放置了滑动条,实际操作时,可以改变滑块位置,直接看到仪表动作。

本文完。

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

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

相关文章

Linux 中 Tail 命令的 9 个实用示例

引言 我们作为 Linux 用户&#xff0c;经常会操作那些在后台长时间运行的进程&#xff0c;这些进程被称作守护进程或服务。例如 Secure Shell (sshd)、Network Manager (networkd)、Volume Manager (LVM)、Cron 等都是服务的典型例子&#xff0c;这样的服务还有很多。 在许多情…

1.10 DFT示例1

1.10 DFT示例1 Tips&#xff1a;离散傅里叶的不同角度的解释。 参考&#xff1a;https://mp.weixin.qq.com/s/TrRmqkc34Zqw9pgaITqlZg?poc_tokenHF5h1WajXiXCmFpwIbv1HaHN52KsET1UE29CM561 摘取部分核心观点&#xff1a; 站在高观点下看问题&#xff0c;傅里叶变换本质上是…

MySQL表的操作与数据类型

目录 前言 一、表的操作 1.创建一个表 2.查看表的结构 3.修改表 4.删除一个表 二、 MySQL的数据类型 0.数据类型一览&#xff1a; 1.整数类型 2.位类型 3.小数类型 4.字符类型 前言 在MySQL库的操作一文中介绍了有关MySQL库的操作&#xff0c;本节要讲解的是由库管理的结构——…

PointNet++改进策略 :模块改进 | x-Conv | PointCNN, 结合局部结构与全局排列提升模型性能

目录 前言PointCNN实现细节1. X X X-Conv 操作输入输出步骤 2. PointCNN 网络架构层级卷积分类与分割任务 3. 数据增强4. 效率优化 前言 这篇论文介绍了一种名为 PointCNN 的方法&#xff0c;旨在从点云&#xff08;point cloud&#xff09;数据中学习特征。传统卷积神经网络…

华为 HCIP-Datacom H12-821 题库 (9)

有需要题库的可以看主页置顶 V群进行学习交流 1.以下关于 RSTP 保护功能的描述&#xff0c;错误的是哪一选项&#xff1f; A、环路保护可以部署在根端口上&#xff0c;以防网络中形成环路 B、环路保护可以部署在Alternate 端口上&#xff0c;以防网络中形成环路 C、BPDU 保…

“短视频 + ”新业态下的高职院校数字媒体技术专业群建设方案

一、引言 短视频&#xff0c;亦称短片视频&#xff0c;是在移动互联网快速普及背景下兴起的一种新兴媒体形态&#xff0c;它继承了传统影视行业的一些特点并加以创新。相较于传统的文字和图片传播方式&#xff0c;短视频集声音与影像于一体&#xff0c;能够承载更多的信息量&a…

【JAVA开源】基于Vue和SpringBoot的网上订餐系统

本文项目编号 T 018 &#xff0c;文末自助获取源码 \color{red}{T018&#xff0c;文末自助获取源码} T018&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

缓存对象反序列化失败

未定义serialVersionUID&#xff0c;会自动生成序列化号 新增了属性&#xff0c;序列号就变了&#xff0c;导致缓存对象反序列化失败。 所有缓存对象必须指定序列化id&#xff01; 那我如何找到未添加字段前 对象的序列化号呢&#xff1f;默认的序列化号是如何生成的呢&#…

11、Hive+Spark数仓环境准备

1、 Hive安装部署 1&#xff09;把hive-3.1.3.tar.gz上传到linux的/opt/software目录下 2&#xff09;解压hive-3.1.3.tar.gz到/opt/module/目录下面 [shuidihadoop102 module]$ tar -zxvf /opt/software/hive-3.1.3.tar.gz -C /opt/module/ 3&#xff09;修改hive-3.1.3-b…

车辆目标检测数据集 19000张 带标注 voc yolo

数据集名称&#xff1a; 车辆目标检测数据集 数据集规模&#xff1a; 图像数量&#xff1a;19000张标注类型&#xff1a;车辆检测&#xff08;包括但不限于轿车、卡车、公交车、摩托车等&#xff09;格式兼容性&#xff1a;支持VOC和YOLO标注格式 数据集内容&#xff1a; 该…

IP网络广播服务平台任意文件上传漏洞

文章目录 免责声明搜索语法漏洞描述漏洞复现修复建议 免责声明 本文章仅供学习与交流&#xff0c;请勿用于非法用途&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任 搜索语法 icon_hash"-568806419"漏洞描述 该系统在upload接口处可上传任…

SOMEIP_ETS_099: SD_ClientServiceActivate

测试目的&#xff1a; 验证DUT能够在不停止其已运行的服务器模式的情况下&#xff0c;被触发进入客户端模式。 描述 本测试用例旨在确保DUT能够在保持服务器模式运行的同时&#xff0c;启动客户端模式的启动程序&#xff0c;并准备好作为客户端进行操作。 测试拓扑&#xf…

机器人入门系列:《通过蓝牙模块通讯控制机器人电机转动》

一、引言 在现代科技高速发展的今天&#xff0c;机器人技术作为自动化和智能化领域的重要分支&#xff0c;正以前所未有的速度改变着我们的生活和生产方式。其中&#xff0c;无线通信技术作为机器人远程控制的关键技术之一&#xff0c;其重要性不言而喻。蓝牙技术&#xff0c;…

Deepl翻译VS行业TOP3,你的翻译神器是哪款?

现在世界变得越来越小&#xff0c;大家都在用不同的语言交流&#xff0c;这时候翻译工具就特别重要了。不管是搞研究、谈生意&#xff0c;还是平时聊天&#xff0c;翻译软件都超级有用。在这么多翻译工具里&#xff0c;Deepl翻译这样的软件特别受欢迎。咱们今天就来比一比&…

复盘:求职两个月还没找到工作,深刻反思

一、求职反思 复盘下&#xff0c;求职两个月还没找到工作&#xff0c;心态在快崩溃的边缘又被拉回来了。 历经两个月的征途&#xff0c;找工作打招呼和投简历不下百次&#xff0c;用了三个APP&#xff0c;猎聘、boss直聘、智联。真的感觉粥多僧少。从一开始挑公司、薪酬&…

运营商不给降套餐怎么办?建议你这么操作!

订套餐容易改套餐难&#xff0c;这种情况你遇到过没有&#xff1f; 有朋友反馈&#xff0c;因为自己的套餐资费太贵&#xff0c;打运营商客服电话降套餐&#xff0c;结果运营商不不能降套餐怎么办&#xff0c;这种情况会怎么办呢&#xff1f; 如果出现以下几种情况&#xff0…

初识C++|继承

&#x1f36c; mooridy-CSDN博客 &#x1f9c1;C专栏&#xff08;更新中&#xff01;&#xff09; 目录 1. 继承的概念及定义 1.1 继承的概念 1.2 继承定义 1.2.1 定义格式 1.2.2 继承父类成员访问方式的变化 1.3继承类模板 2. 父类和子类对象赋值兼容转换 3. 继承中的…

Excel单元格操作:读写单元格数据、格式设置与条件格式详解

目录 一、Excel单元格的基本操作 1.1 单元格的选取与编辑 案例一&#xff1a;基本数据录入 1.2 单元格的读取与写入 案例二&#xff1a;使用公式计算销售额 二、单元格格式设置 2.1 字体与颜色设置 案例三&#xff1a;设置标题格式 2.2 数字格式设置 案例四&#xff…

大数据-128 - Flink 并行度设置 细节详解 全局、作业、算子、Slot

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【观察】为天府之国数智化转型“架桥铺路”,华为云云商店释放创新“乘数效应”...

作为全国首批国家数字经济创新发展试验区&#xff0c;近年来四川在发展数字经济的道路上可谓“一马当先”。数据显示&#xff0c;2023年&#xff0c;四川省数字经济核心产业增加值达到4899.07亿元&#xff0c;占GDP比重8.1%&#xff0c;数字经济日益成为四川省经济高质量发展的…