qt开关控件设计(手把手从零开始)

news2024/9/23 9:32:00

从零开始手把手教你设计自己的qt控件

  • 1 说明
    • 1.1 显示效果
    • 1.2 控件特性
    • 1.3 设计方法
  • 2 控件需求分析
      • 2.1 必要需求
      • 2.1 顺带需求(锦上添花)
  • 3 功能设计
    • 3.1 设计思路(重点内容)
    • 3.2 自适应大小
    • 3.3 开关动画
    • 3.4 控件绘制
  • 4 总体代码

1 说明

1.1 显示效果

显示效果看下面的动图:
(1)普通模式,一般都是用这种模式就行
在这里插入图片描述
(2)等待模式,有的比较耗时且不确定是否能够开启的操作使用该模式
在这里插入图片描述

1.2 控件特性

具体特性如下图所示:
在这里插入图片描述

1.3 设计方法

实现自定义控件的方法有很多,除了自己绘制外,还可以使用qt的控件+样式表堆一个新的控件。
我现在只推荐继承qwidget类进行绘制,虽然前期可能麻烦点,原因如下:

  • 需要高度自定义,很多细节把控;
  • 后续功能和样式拓展方便灵活;
  • 执行效率;
  • 通用性和独立性;
  • 项目如果非常复杂庞大的话,样式表将会是个非常忌讳的东西。

在后文提供一种控件的通用设计思路,可供参考。

2 控件需求分析

需求分析主要是要明确控件做成什么样子,从而在开发中避免做无用功,最主要的是防止设计方向出现偏差导致漏掉的需求很难添加上去。
这个步骤还是非常有必要的,在我们的工作项目中开发做需求分析后,一个是可以对工作上做风险把控,确定工作难度和影响,另外一个就是可以做好时间规划,防止规划工作的时间过长或过短,最后就是这个过程可以顺便把概要设计完成,总之益处多多。

2.1 必要需求

(1)控件开、关状态;
(2)开、关、圆形按钮颜色自定义;
(3)圆形按钮带移动动画;
(4)添加等待模式;
(5)圆形按钮阴影;
(6)可获取开关状态;
(7)状态切换后发出信号。

2.1 顺带需求(锦上添花)

(1)自适应缩放,就是可大可小;
(2)边缘颜色自定义(一般为透明就行);
(3)颜色渐变动画;
(4)添加使、失能状态。

3 功能设计

3.1 设计思路(重点内容)

对于自定义控件实现中,每个功能的支持,在代码上我们一般可总结为接口、操作、数据、绘制几个部分,关系如下图:
在这里插入图片描述
使用失能状态为例子:
(1)首先是数据支持,每个功能第一步应该是需要构建好支持的数据,这样就有个中心,接下来围绕这个中心进行开发,从上图可以看出来,最后数据最好有个默认初始状态。

bool mEnable{1};    //使能状态

(2)、读取、写入接口,接口定义可以优于开发的,一般在项目需求分析的时候定义好了。

    void setEnabled(bool enable);    /// 设置使能状态,default:1
    bool getEnabled();    /// 获取使能状态

(3)操作,由于这个功能非常简单,所以基本没有什么操作,所以直接写在接口函数内部,如下:

void WBSwitchButton::setEnabled(bool enable){
    QWidget::setEnabled(enable);
    mEnable = enable;
    emit sigEnableChanged(mEnable);
    update();
}

(3-2)操作相关影响,失能状态下是不可点击切换开关状态的,所以在点击事件中进行过滤,代码如下:

    if(!mEnable)    return ;

注:这段代码需要写在事件函数最前面。
(4)绘制,这步主要是要在显示上区分使能和失能,我的思路是直接在按钮上面盖上一层暗色的蒙层,表示处于失能状态,代码如下所示:

    /// 失能显示,添加一层暗色的蒙层
    if(!mEnable){
        QColor disable(Qt::black);
        disable.setAlphaF(0.5);
        painter.setBrush(disable);
        painter.drawRoundedRect(this->rect(),mRadius,mRadius);
    }

注:这部分代码是写在绘制函数最后面。
失能效果如下,可对比前面的使能状态。
在这里插入图片描述

3.2 自适应大小

因为控件按照上面的思路设计进行设计,所以在自适应大小的时候,不涉及到接口和操作,直接调整(·计算)背后支持的数据即可,然后直接update()重新绘制一下。

void WBSwitchButton::resizeEvent(QResizeEvent *event){
    Q_UNUSED(event)
    /// 更新按钮大小、圆角大小、动画两个位置
    int size = qMin(this->width(),this->height());
    mRadius = size/2;
    float width = size * 3 / 4;
    float border = (size - width) / 2;
    mLeftPos = QPoint(border,border);
    mRightPos = QPoint(this->width() - border - width,border);
    mButtonRect.setWidth(width);
    mButtonRect.setHeight(width);
    mButtonRect.moveTo(mOnOff ? mRightPos : mLeftPos);
    mBackColor = mOnOff ? mBackOnColor : mBackOffColor ;
    update();
}

3.3 开关动画

对于动画,其实都是属于设计思路中的操作部分,此部分只要使用qt的动画对象动态修改数据即可,位置动画就修改按钮位置,颜色动画就修改颜色位置,具体代码如下:
开关按钮位置移动动画

    /// 动画-开关按钮位置
    QVariantAnimation* posAnimation = new QVariantAnimation(this);
    posAnimation->setDuration(mAnimationPeriod);
    posAnimation->setStartValue(mButtonRect.topLeft());
    posAnimation->setEndValue(mOnOff ?  mRightPos : mLeftPos);
    connect(posAnimation,&QPropertyAnimation::valueChanged,[=](const QVariant &value){
        mButtonRect.moveTo(value.toPointF());
        update();
    });
    posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);   //停止后删除

开关状态颜色渐变动画

    /// 动画-背景颜色
    QPropertyAnimation * colorAnimation = new QPropertyAnimation(this,"pBackColor");
    colorAnimation->setDuration(mAnimationPeriod);
    colorAnimation->setStartValue(mBackColor);
    colorAnimation->setEndValue(mOnOff ?  mBackOnColor: mBackOffColor);
    colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);   //停止后删除

3.4 控件绘制

从设计思路中可以看出,我们的绘制主要按照外观,读取支持的数据直接绘制即可,这样的好处就是绘制中不带复杂的操作逻辑(比如动画)和计算操作,防止每次绘制都会进行没必要的重复计算。

绘制函数只拿数据在必要的时候进行绘制,这样大大提高控件的执行效率和流畅度(本控件可能看不出来,大的复杂控件就能体验出来)。

void WBSwitchButton::paintEvent(QPaintEvent *event){
    Q_UNUSED(event)
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);	//抗锯齿
    painter.setPen(Qt::NoPen);

    /// 绘制边缘颜色
    QPainterPath path;
    path.addRect(this->rect());
    path.addRoundedRect(this->rect(),mRadius,mRadius);
    path.setFillRule(Qt::OddEvenFill);
    painter.setBrush(mEdgeColor);
    painter.drawPath(path);

    /// 绘制背景颜色
    painter.setBrush(mBackColor);
    painter.drawRoundedRect(this->rect(),mRadius,mRadius);

    /// 绘制圆形按钮
    painter.setBrush(mButtonColor);
    painter.drawEllipse(mButtonRect);

    /// 绘制按钮阴影
    painter.setBrush(Qt::NoBrush);
    QColor color(Qt::black);
    int count = (this->height() - mButtonRect.height())/2;
    float stepColor = (0.15-0.0)/count;
    for (int i = mButtonRect.height()/2 + 1; i < this->height()/2; i++){
        color.setAlphaF(0.15 - stepColor*(i - mButtonRect.height()/2));
        painter.setPen(color);
        painter.drawEllipse(mButtonRect.center(),i,i);
    }

    /// 失能显示,添加一层蒙层
    if(!mEnable){
        QColor disable(Qt::black);
        disable.setAlphaF(0.5);
        painter.setBrush(disable);
        painter.drawRoundedRect(this->rect(),mRadius,mRadius);
    }
}

注:可以重点看一下上面的绘制阴影实现思路。

4 总体代码

(1)h文件

#ifndef WBSWITCHBUTTON_H
#define WBSWITCHBUTTON_H

#include <QWidget>
#include <QPropertyAnimation>
#include <QPainterPath>
#include <QPainter>
#include <QRadialGradient>
#include <QMouseEvent>

///
/// \brief 基础控件-Switch开关按钮
///
class WBSwitchButton : public QWidget
{
    Q_OBJECT
public:
    Q_PROPERTY(QColor pBackColor MEMBER mBackColor)  //新增背景颜色属性,用于动画

    explicit WBSwitchButton(QWidget *parent = nullptr);
    bool getSwitch();   /// 获取开关状态

public slots:
    void setSwitch(bool onoff);    /// 设置开关状态,default:0
    void setEnabled(bool enable);    /// 设置使能状态,default:1
    bool getEnabled();    /// 获取使能状态
    void setAnimationPeriod(int period);    /// 设置切换状态周期
    void setPrecisionClick(bool flag);    /// 设置精确点击,即只有点中按钮的时候才开关
    void setWaitModel(bool flag);    /// 设置等待模式,点击后不会主动切换开关,需要setSwitch
    void setSwitchForWaitModel(bool onoff);    /// 设置开关状态,default:0

    void setButtonColor(QColor color);    /// 设置开关(圆形按钮)颜色
    void setBackOnColor(QColor color);    /// 设置背景颜色-开
    void setBackOffColor(QColor color);    /// 设置背景颜色-关
    void setEdgeColor(QColor color);    /// 设置边缘颜色,默认透明

signals:
    void sigEnableChanged(bool enable);    /// 使能状态变化信号
    void sigSwitchChanged(bool onoff);    /// 开关状态变化信号

protected:
    void paintEvent(QPaintEvent *event);
    void resizeEvent(QResizeEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void enterEvent(QEvent *event);
    void leaveEvent(QEvent *event);

private:
    bool mOnOff{0};     //开关状态
    bool mEnable{1};    //使能状态
    bool mPrecisionClickFlagh{0};   //精确点击标志位
    bool mWaitSigModel{1};  //等待模式,点击后按钮位置会进行切换,但是颜色需要等待外部信号变动
    bool mAnimationOnOff{1};    //动画开关,default:1
    bool mHover{0};

    QColor mButtonColor{Qt::white};    //开关(圆形按钮)颜色
    QColor mBackColor{Qt::red};
    QColor mEdgeColor{Qt::transparent};  //边缘颜色
    QRectF mButtonRect; //开关按钮rect
    int mRadius{8}; // 开关外观边缘圆角

    int mAnimationPeriod{200};  //动画周期
    QPointF mRightPos;       // 动画位置-开
    QPointF mLeftPos;        // 动画位置-关
    QColor mBackOnColor{Qt::green};  //背景颜色-开
    QColor mBackOffColor{Qt::darkGray};  //背景颜色-关
};

#endif // WBSWITCHBUTTON_H

(2)cpp文件

#include "wbswitchbutton.h"
#include <QDebug>

WBSwitchButton::WBSwitchButton(QWidget *parent)
    : QWidget{parent}
{

}

bool WBSwitchButton::getSwitch(){
    return mOnOff;
}

void WBSwitchButton::setSwitch(bool onoff){
    if(mWaitSigModel) return ;
    /// 状态切换
    mOnOff = onoff;
    /// 发送信号
    sigSwitchChanged(mOnOff);
    /// 动画-背景颜色
    QPropertyAnimation * colorAnimation = new QPropertyAnimation(this,"pBackColor");
    colorAnimation->setDuration(mAnimationPeriod);
    colorAnimation->setStartValue(mBackColor);
    colorAnimation->setEndValue(mOnOff ?  mBackOnColor: mBackOffColor);
    colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);   //停止后删除
    /// 动画-开关按钮位置
    QVariantAnimation* posAnimation = new QVariantAnimation(this);
    posAnimation->setDuration(mAnimationPeriod);
    posAnimation->setStartValue(mButtonRect.topLeft());
    posAnimation->setEndValue(mOnOff ?  mRightPos : mLeftPos);
    connect(posAnimation,&QPropertyAnimation::valueChanged,[=](const QVariant &value){
        mButtonRect.moveTo(value.toPointF());
        update();
    });
    posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);   //停止后删除
}

void WBSwitchButton::setSwitchForWaitModel(bool onoff)
{
    if(!mWaitSigModel) return ;
    if(mOnOff == onoff){
        /// 表示值未改变先运行按钮位置动画
        QVariantAnimation* posAnimation = new QVariantAnimation(this);
        posAnimation->setDuration(mAnimationPeriod);
        posAnimation->setStartValue(mOnOff ? mLeftPos : mRightPos);
        posAnimation->setEndValue(mOnOff ?  mRightPos : mLeftPos);
        connect(posAnimation,&QVariantAnimation::valueChanged,[=](const QVariant &value){
            mButtonRect.moveTo(value.toPointF());
            update();
        });
        posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);   //停止后删除
        return ;
    }
    /// 状态切换
    mOnOff = onoff;
    /// 发送信号
    sigSwitchChanged(mOnOff);
    /// 后运行背景颜色动画
    QPropertyAnimation * colorAnimation = new QPropertyAnimation(this,"pBackColor");
    colorAnimation->setDuration(mAnimationPeriod);
    colorAnimation->setStartValue(mBackColor);
    colorAnimation->setEndValue(mOnOff ?  mBackOnColor: mBackOffColor);
    colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);   //停止后删除
    connect(colorAnimation,&QPropertyAnimation::valueChanged,[=](const QVariant &value){
        update();
    });
}

void WBSwitchButton::setEnabled(bool enable){
    QWidget::setEnabled(enable);
    mEnable = enable;
    emit sigEnableChanged(mEnable);
    update();
}

void WBSwitchButton::setAnimationPeriod(int period){
    mAnimationPeriod = period;
}

void WBSwitchButton::setPrecisionClick(bool flag){
    mPrecisionClickFlagh = flag;
}

void WBSwitchButton::setWaitModel(bool flag)
{
    mWaitSigModel = flag;
}

void WBSwitchButton::setButtonColor(QColor color){
    mButtonColor = color;
    update();
}

void WBSwitchButton::setBackOnColor(QColor color){
    mBackOnColor = color;
    update();
}

void WBSwitchButton::setBackOffColor(QColor color){
    mBackOffColor = color;
    update();
}

void WBSwitchButton::setEdgeColor(QColor color){
    mEdgeColor = color;
    update();
}

void WBSwitchButton::paintEvent(QPaintEvent *event){
    Q_UNUSED(event)
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setPen(Qt::NoPen);

    /// 绘制边缘颜色
    QPainterPath path;
    path.addRect(this->rect());
    path.addRoundedRect(this->rect(),mRadius,mRadius);
    path.setFillRule(Qt::OddEvenFill);
    painter.setBrush(mEdgeColor);
    painter.drawPath(path);

    /// 绘制背景颜色
    painter.setBrush(mBackColor);
    painter.drawRoundedRect(this->rect(),mRadius,mRadius);

    /// 绘制圆形按钮
    painter.setBrush(mButtonColor);
    painter.drawEllipse(mButtonRect);

    /// 绘制按钮阴影
    painter.setBrush(Qt::NoBrush);
    QColor color(Qt::black);
    int count = (this->height() - mButtonRect.height())/2;
    float stepColor = (0.15-0.0)/count;
    for (int i = mButtonRect.height()/2 + 1; i < this->height()/2; i++){
        color.setAlphaF(0.15 - stepColor*(i - mButtonRect.height()/2));
        painter.setPen(color);
        painter.drawEllipse(mButtonRect.center(),i,i);
    }

    /// 失能显示,添加一层蒙层
    if(!mEnable){
        QColor disable(Qt::black);
        disable.setAlphaF(0.5);
        painter.setBrush(disable);
        painter.drawRoundedRect(this->rect(),mRadius,mRadius);
    }
}

void WBSwitchButton::resizeEvent(QResizeEvent *event){
    Q_UNUSED(event)
    /// 更新按钮大小、圆角大小、动画两个位置
    int size = qMin(this->width(),this->height());
    mRadius = size/2;
    float width = size * 3 / 4;
    float border = (size - width) / 2;
    mLeftPos = QPoint(border,border);
    mRightPos = QPoint(this->width() - border - width,border);
    mButtonRect.setWidth(width);
    mButtonRect.setHeight(width);
    mButtonRect.moveTo(mOnOff ? mRightPos : mLeftPos);
    mBackColor = mOnOff ? mBackOnColor : mBackOffColor ;
    update();
}

void WBSwitchButton::mouseReleaseEvent(QMouseEvent *event){
    if(mWaitSigModel){
        /// 先运行按钮位置动画
        QVariantAnimation* posAnimation = new QVariantAnimation(this);
        posAnimation->setDuration(mAnimationPeriod);
        posAnimation->setStartValue(mOnOff ? mRightPos : mLeftPos);
        posAnimation->setEndValue(mOnOff ?  mLeftPos : mRightPos);
        connect(posAnimation,&QVariantAnimation::valueChanged,[=](const QVariant &value){
            mButtonRect.moveTo(value.toPointF());
            update();
        });
        posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);   //停止后删除
        return ;
    }
    if(!mEnable)    return ;
    if(mButtonRect.contains(event->pos()) || !mPrecisionClickFlagh){
        setSwitch(!mOnOff);
    }
}

void WBSwitchButton::enterEvent(QEvent *event){
    Q_UNUSED(event)
    mHover = true;
}

void WBSwitchButton::leaveEvent(QEvent *event){
    Q_UNUSED(event)
    mHover = false;
}

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

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

相关文章

推动新能源越野场景革命 坦克品牌开创越野新生态

近日&#xff0c;坦克品牌以“创领越野新生态”为主题&#xff0c;携多款车型登陆第二十届广州国际汽车展览会。秉持“以用户为中心”&#xff0c;坦克品牌围绕技术与生态双线出击&#xff0c;正式亮相坦克500 PHEV长续航版、生活方式共创平台TANK Life。技术创领&#xff0c;打…

docker-ui创建使用

首先需要安装docker: apt install docker.io composer也需要安装&#xff1a; apt install composer docker查找docker-ui镜像&#xff1a; docker search docker-ui 在列表里选一个镜这里就选第一个。 docker pull builtdock/docker-ui 然后直接docker run 使用的时候&#x…

DSL操作ElasticSearch基础命令

文章目录一、DSL操作ES-RESTful风格二、DSL操作索引库2.1 PUT 添加索引2.2 GET 查询索引2.3 DELETE 删除索引2.4 POST 打开/关闭索引库三、DSL操作映射3.1 数据类型3.1.1 简单数据类型3.1.2 复杂数据类型3.2 创建索引库并设置映射3.2.1 语法格式3.2.2 举例3.3 查询索引库映射3.…

python中的类型注解

目录 一.类型注解 变量的类型注解 类型注解的语法 类型注解主要功能在于: 函数方法的类型注解 函数&#xff08;方法&#xff09;形参进行类型注解 函数&#xff08;方法&#xff09;返回值进行类型注解 小结 Union类型 小结 一.类型注解 变量的类型注解 思考 为什么…

Android美团多渠道打包Walle集成

一、为什么使用美团多渠道打包的方式&#xff1f; 打包更加快速 传统的通过productFlavors渠道包的方式&#xff0c;渠道10个以内还可以接受&#xff0c;如果100个渠道包&#xff0c;每个包需要打5Min,就是将近10个小时的打包&#xff0c;而采用美团Walle多渠道打包的方式只需…

PyTorch 2.0 推理速度测试:与 TensorRT 、ONNX Runtime 进行对比

PyTorch 2.0 于 2022 年 12 月上旬在 NeurIPS 2022 上发布&#xff0c;它新增的 torch.compile 组件引起了广泛关注&#xff0c;因为该组件声称比 PyTorch 的先前版本带来更大的计算速度提升。 这对我们来说是一个好消息&#xff0c;训练时间改进的结果令人印象深刻。PyTorch 团…

JavaScript 入门基础 - 流程控制(四)

JavaScript 流程控制 - 分支和循环 文章目录JavaScript 流程控制 - 分支和循环1. 什么是流程控制2. 顺序流程控制3. 分支流程控制 之 if语句3.1 什么是分支结构3.2 if 语句3.2.1 if 语句基本理解3.2.2 if 语句执行流程3.2.3 if 语句案例3.3 if else语句&#xff08;双分支语句&…

Threejs实现鼠标点击人物行走/镜头跟随人物移动/鼠标点击动画/游戏第三人称/行走动作

1&#xff0c;功能介绍 Threejs获取鼠标点击位置、实现鼠标点击人物行走、人物头顶显示名称标签、镜头跟随人物移动并且镜头围绕人物旋转&#xff0c;类似游戏中第三人称、鼠标点击位置有动画效果&#xff0c;如下效果图 2&#xff0c;功能实现 获取鼠标点击位置&#xff0c;…

【Linux】进程间通信 - 匿名/命名管道与System V共享内存

目录 前言 一.管道 0.什么是管道 1).管道的概念 2).管道的本质 3).管道指令: "|" 1.匿名管道 1).如何创建匿名管道 2).如何使用匿名管道进行通信 3).匿名管道的特点总结 2.命名管道 0).指令级的命名管道的通信 1).如何在编程时创建命名管道 2).如何在…

你好2023-使用msys64 openssl 制作QSslSocket实验所需证书

2023年开始了&#xff0c;第一篇&#xff0c;记录最近帮朋友制作QSslSocket所需证书的过程。 使用传统的TCP连接依旧是很多工业软件的常见通信方法。但如果恰好不希望别人通过抓包等方法研究上位机和控制器模块之间的协议格式&#xff0c;那使用SSL连接是一种掩耳盗铃的好办法&…

Pyinstaller - 你的“神”队友

哈哈&#xff01;今天是我在2023年发布的第一篇文章呀&#xff01; 这两天&#xff0c;我在做一个爬虫项目。因为我做好后准备给我的朋友看看&#xff0c;但我朋友没有 Python 环境。所以&#xff0c;只好想办法把 .py 打包成 .exe 。 在网上搜了一下&#xff0c;发现目前相对…

设计模式 ——工厂模式

前言 有一些重要的设计原则在开篇和大家分享下&#xff0c;这些原则将贯通全文&#xff1a; 面向接口编程&#xff0c;而不是面向实现。这个很重要&#xff0c;也是优雅的、可扩展的代码的第一步&#xff0c;这就不需要多说了吧。 职责单一原则。每个类都应该只有一个单一的功…

第三十一讲:神州路由器策略路由的配置

从局域网去往广域网的流量有时需要进行分流&#xff0c;即区别了不同用户又进行了负载分担&#xff0c;有时这种目标是通过对不同的源地址进行区别对待完成的&#xff0c;通过策略路由的方法可以解决此问题。 实验拓扑图如下所示 R1 R2 R3 F0/0 1.1.3.1/24 F0/0 1.1.3.2…

【AcWing每日一题】4261. 孤独的照片

Farmer John 最近购入了 N 头新的奶牛&#xff0c;每头奶牛的品种是更赛牛&#xff08;Guernsey&#xff09;或荷斯坦牛&#xff08;Holstein&#xff09;之一。 奶牛目前排成一排&#xff0c;Farmer John 想要为每个连续不少于三头奶牛的序列拍摄一张照片。 然而&#xff0c…

java多线程(11):线程协作

1 线程通信 应用场景 : 生产者和消费者问题 假设仓库中只能存放一件产品 , 生产者将生产出来的产品放入仓库 , 消费者将仓库中产品取走消费 如果仓库中没有产品 , 则生产者将产品放入仓库 , 否则停止生产并等待 , 直到仓库中的产品被消费者取走为止 如果仓库中放有产品 ,…

Chrome Extension 基础篇

Extensions are software programs, built on web technologies (such as HTML, CSS, and JavaScript) that enable users to customize the Chrome browsing experience. 扩展程序是基于 Web 技术&#xff08;例如 HTML、CSS 和 JavaScript&#xff09;构建的软件程序&#xf…

C语言递归

递归指的是在函数的定义中使用函数自身的方法。 举个例子&#xff1a; 从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&#xff0c;正在给小和尚讲故事呢&#xff01;故事是什么呢&#xff1f;"从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚…

redis的安装

1.Redis是基于C语言编写的&#xff0c;因此首先需要安装Redis所需要的gcc依赖&#xff1a; yum install -y gcc tcl2.上传安装包并解压 tar -xzf redis-6.2.6.tar.gz3.解压后&#xff0c;进入redis目录 cd redis-6.2.64.运行编译命令 make && make install如果没有…

3_运行时数据区概述及线程

前言 本节主要讲的是运行时数据区&#xff0c;也就是下图这部分&#xff0c;它是在类加载完成后的阶段 当我们通过前面的&#xff1a;类的加载-> 验证 -> 准备 -> 解析 -> 初始化 这几个阶段完成后&#xff0c;就会用到执行引擎对我们的类进行使用&#xff0c;同时…

56. 数据增广 / 图像增广

1. CES上的真实故事 2. 数据增强 增加一个已有数据集&#xff0c;使得有更多的多样性 在语言里加入各种不同的背景噪音改变图片的颜色和形状 例如&#xff0c;我们可以以不同的方式裁剪图像&#xff0c;使感兴趣的对象出现在不同的位置&#xff0c;减少模型对于对象出现位置…