【QT】第一个 QT程序(对象树)

news2025/2/22 16:21:42

🌈 个人主页:Zfox_
🔥 系列专栏:Qt

目录

  • 一:🔥 QtHelloWorld程序
    • 🦋 使⽤"标签"实现
      • 纯代码⽅式实现
      • 可视化操作实现
    • 🦋 使⽤"按钮"实现
      • 可视化操作实现
      • 纯代码实现
    • 🦋 内存泄露
  • 二:🔥 认识对象模型(对象树)
    • 🦋 什么是对象树
    • 🦋 验证对象树
    • 🦋 utf8 和 gbk
  • 三:🔥 解决编码问题
  • 四:🔥 Qt 编程注意事项
    • 🦋 Qt 中的命名事项
    • 🦋 Qt 中的快捷键
  • 五:🔥 Qt 析构函数执行顺序
  • 六:🔥 共勉
    • 🦋 小结

一:🔥 QtHelloWorld程序

🦋 使⽤"标签"实现

纯代码⽅式实现

📚 我们点击 widget.cpp 里面,会有一个 widget 的构造函数和析构函数,我们一般使用代码进行编辑界面的时候,一般都是在 widget 的构造函数中实现,因为在 main 函数中调用了 widget 类之后就直接 show 了,所以卸载构造函数中的时候,一旦执行到了 show 就一定可以显示出设计的界面。

在这里插入图片描述

📚 实现效果:

在这里插入图片描述

void setText(const QString &);

🦁 这里我们会发下使用字符串的时候并不是我们 C++ 使用的标准库中的 string,而是 Qt 自己包装好的字符串 QString 。这个其实也是历史原因,Qt 诞生于 1991 年,那个时候 C++ 还没有定标准,而 Qt 为了更好的开发就自己包装了一些容器。但是我们也还是可以使用 C++ 的标准库中的容器来使用

  • QString 用起来其实也要比 std::string 好一点,因为 QString 内部已经对于字符编码做了处理,而不像 std::string 啥都没做

可视化操作实现

  1. 双击:"widget.ui"⽂件;
    在这里插入图片描述
  2. 拖拽"标签"⾄UI设计界⾯中,并双击修改标签内容;
    在这里插入图片描述

🔥 此时 ui 界面就会生成对应的 XML 格式代码,这个时候 qmake 就会根据这个 XML 代码生成对应的 C++ 代码,我们也可以在同目录下找到这个 C++ 代码
在这里插入图片描述

  1. 实现效果如下图所⽰:
    在这里插入图片描述

🦋 使⽤"按钮"实现

可视化操作实现

  1. 双击:"widget.ui"⽂件;
    在这里插入图片描述

  2. 拖拽控件⾄ui界⾯窗⼝并修改内容;
    在这里插入图片描述

  3. 构建并运⾏,效果如下所⽰:
    在这里插入图片描述

  • 这里的按钮的确可以点击,但是却没有任何反应,这个就设计到我们后面学的信号槽知识,后面会说的
  • QT 的信号槽机制:本质上就是给按钮的点击操作,关联上一个处理函数,当用户点击的时候,就会执行这个处理函数

这里我们的按钮没有任何功能,假如我们要实现一定的功能,那该怎么做呢?

打开 widget.ui 文件,查看设计的右下角,则有

在这里插入图片描述
📚 如下代码:

在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 按钮的点击操作 -- 信号槽
    // 在 Linux 网络编程那也有个connect 函数,那里用来给 TCP socket 建立连接的,写 TCP 客户端的时候,就需要先建立连接才能读写数据
 
    // ui->pushButton:谁发出的信号
    // &QPushButton::clicked:发出了啥信号,点击按钮的时候自动触发该信号
    // this: 谁来处理这个信号
    // Widget::handle:具体怎么处理
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick);  // 访问到 form file(ui 文件)中创建的控件
 
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::handleClick()
{
    if(ui->pushButton->text() == "Hello World"){
        ui->pushButton->setText("Hello IsLand");
    }else{
        ui->pushButton->setText("Hello World");
    }
}
 

在这里插入图片描述

📚 返回上级目录查看 ui_widget.h 文件:

在这里插入图片描述

📚 因此我们也可以把 PushButton 改成其他的,如下:

在这里插入图片描述
🧑‍💻 再次查看 ui_widget.h 文件,如下:
在这里插入图片描述
结论:在 objectName 中,设置成什么值,生成的变量名就叫啥名字就可以根据这个名字来获取到对应的控件的变量了

纯代码实现

widget.h

#ifndef WIDGET_H
#define WIDGET_H
 
#include <QWidget>
#include <QPushButton>
 
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
 
class Widget : public QWidget
{
    Q_OBJECT
 
public:
    Widget(QWidget *parent = nullptr);
    void handleClick();
    ~Widget();
 
private:
    Ui::Widget *ui;
 
    QPushButton* myButton;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    myButton = new QPushButton(this);
    myButton->setText("Hello World");
    connect(myButton, &QPushButton::clicked, this, &Widget::handleClick);  // 访问到 form file(ui 文件)中创建的控件
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::handleClick()
{
    if(myButton->text() == QString("Hello World")){
        myButton->setText("Hello IsLand");
    }else{
        myButton->setText("Hello World");
    }
}

💻 实现效果如下图:
在这里插入图片描述
🆚 两个版本比较:

  • 图形化实现:此时按钮对象不需要咱们自己 new。new 对象的操作已经是被 Qt 自动生成了而且这个按钮对象,已经作为 ui 对象里的一个成员变量了,也无需作为 Widget 的成员
  • 纯代码实现:按钮对象是咱们自己 new 的,为了保证其他函数中能够访问到这个变量,就需要把按钮对象,设定为 Widget 类的成员变量

🦋 内存泄露

📚 在上面的代码实现中,我们使用 new 创建了对象,在栈上开辟了一块空间之后,但是我们没有使用 delete 进行释放控件,这样不就会导致内存泄漏啊

  • 其实上述代码在 Qt 不会产生内存泄露, label 对象会在合适的时候被析构释放,之所以能够把对象释放掉,主要是因为把这个对象挂到了 对象树 上
  • 前端开发 (网页开发) 也涉及到 类似的 对象树 (DOM),本质上也是一个树形结构 (N 又树), 通过树形结构把界面上的各种元素组织起来
  • Qt 中也是类似,也是搞了一个对象树,也是 N 又树,把界面上的各种元素组织起来了

在这里插入图片描述

  • 用对象树把这些内容组织起来,最主要的目的:就是为了能够在合适的时机(窗口关闭和销毁),把这些对象统一进行释放。通过这个树形结构,就把界面上要显示的这些控件对象都组织起来了
  • 这里的树上的这些对象,统一销毁是最好不过的,如果某个对象提前销毁,此时就会导致对应的控件就在界面上不存在了。

二:🔥 认识对象模型(对象树)

🦋 什么是对象树

在 Qt 中创建很多对象的时候会提供一个 Parent 对象指针,下面来解释这个 parent 到底是干什么的。

  • QObject 是以对象树的形式组织起来的。
    • 当创建一个 QObject 对象时,会看到 QObject 的构造函数接收一个 QObject 指针作为参数,这个参数就是 parent,也就是父对象指针。
    • 这相当于,在创建 QObject 对象时,可以提供一个其父对象,我们创建的这个 QObject 对象会自动添加到其父对象的 children()列表。
    • 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)

这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个 QShortcut(快捷键)对象作为其子对象。当删除按钮的时候,这个快捷键理应被删除。这是合理的。

在这里插入图片描述
在这里插入图片描述

🦁 作为⽗组件的window和作为⼦组件的quit都是QObject的⼦类(事实上,它们都是 QWidget 的⼦类,⽽ QWidget是QObject的⼦类)。这段代码是正确的,quit的析构函数不会被调⽤两次,因为标准C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作⽤域时,会先调⽤quit的析构函数,将其从⽗对象window的⼦对象列表中删除,然后才会再调⽤window的析构函数。

  • 但是一旦我们的代码稍微修改一点就会出错
{
	QLabel label("hello"); // 指定父类是widow
	QWidget window;
	label.setParent(&window);
}

情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说,quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++不允许调用两次析构函数,因此,程序崩溃了。

在这里插入图片描述
🍉 由此我们看到,Qt 的对象树机制虽然在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,即 在 Qt 中,尽量将其开辟在堆上,并指定好其 parent 父类对象

🌰 比如:

🧑‍💻 如果我们把最初的代码改成在栈上开辟的话我们运行程序会发现什么都没有

在这里插入图片描述

💻 Qt对象树如图:
在这里插入图片描述

🦋 验证对象树

🤔 首先我们自定义一个 label 类,并在析构部分打上日志,如下步骤:

  1. 选中工程名,鼠标右键 -------> “add new…”(或 “添加新文件” )… 建立出一个新的项目

在这里插入图片描述

  1. 点击"完成"之后,⼿动创建类的头⽂件以及源⽂件

在这里插入图片描述

  • 此时我们可以按 F4 来进行 .h 文件 和 .cpp 文件 来回切换
    在这里插入图片描述
  • 使用快捷键 Alt + Enter 可以实现快速定义

🦭 此时我们的 mylabel.cpp 中就是

#include "mylabel.h"
#include <iostream>
 
MyLabel::MyLabel(QWidget* parent) : QLabel(parent)
{
}
 
MyLabel::~MyLabel()
{
    std::cout << "MyLabel 被销毁" << std::endl;
}

🦭 midget.cpp 中的代码就是

#include "widget.h"
#include "ui_widget.h"
#include "mylabel.h"
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this); // 将form file生成的界面和我们当前的widget进行关联起来
    // 使用自己定义的 MyLabel 代替原来的 QLabel,所谓的 “继承” 本质上是扩展,保持原有功能不变的基础上
    // 给对象扩展出一个析构函数,通过这个析构函数,打印一个自定义日志,方便观察程序运行结果
    MyLabel *label = new MyLabel(this);
    label->setText("Hello World");
 
}
 
Widget::~Widget()
{
    delete ui;
}
 

🔥 此时我们运行代码,就可以看到窗口上有 Hello World 的字样,只要我们关闭窗口,就会输出我们的日志
在这里插入图片描述

这里也是验证了对象树自动释放对象的能力

  • 这里日志是有的,说明析构函数是执行了,虽然没有 手动 delete,但是由于把 MyLabel 挂到了对象树上,此时窗口被销毁的时候,就会自动销毁对象树中的所有对象!!所以 MyLabel 的析构是执行到了

但是这里似乎出现了乱码的情况

  • 乱码问题出现的原因有且仅有一个 编码方式不匹配(不仅仅局限于 C++)
  • 比如字符串本身是 utf8 编码的,但是终端(控制台)是按照 gbk 的方式来解析显示的,就会出现乱码(相当于拿着 utf8 的数值 去查询 gbk 的 码表)

🦋 utf8 和 gbk

目前,表示汉字字符集, 主要是两种方式

  1. GBK(中国大陆) 使用 2 个字节表示一个汉字!Windows 简体中文版,默认的字符集就是 GBK
  2. UTF-8 / utf8 变长编码,表示一个符号,使用的字节数有变化,2-4但是在 utf8 中。一个汉字一般是 3 个字节
  3. Linux 默认就是 utf8

三:🔥 解决编码问题

💻 我们用文本文件打开 mylabel.cpp 文件,可以看到这个文件的编码方式

在这里插入图片描述

🦁 可看到这个文件的编码方式是 utf8,但是 Qt 的这个终端的编码方式肯定不是 utf8 ,但是 Qt 不支持修改编码方式,所以这里我们就需要借助 Qt 自己提供的打印日志的功能 qDebug,或者使用 QString 来处理编码方式。

#include "mylabel.h"
#include <iostream>
 
#include <QtDebug> // 头文件
 
MyLabel::MyLabel(QWidget* parent) : QLabel(parent)
{
 
}
 
MyLabel::~MyLabel()
{
    // std::cout << "MyLabel 被销毁" << std::endl;
    qDebug() << "MyLabel 被销毁"; // qDebug 这个宏封装了 QDebug 对象,使用 qDebug 相当于使用 cout
}
#define qDebug QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug

📚 此时中文就不会出现乱码情况,如下:

在这里插入图片描述

  • 后续在 Qt 中,如果想通过打印日志的方式,输出一些调试信息,都优先使用 qDebug。虽然使用 cout 也行,但是 cout 对于编码的处理不太好,在 windows 上容易出现乱码(如果是 Linux 使用 Qt Creator, 一般就没事了,Linux 默认的编码一般都是 utf8)
  • 使用 qDebug, 还有一个好处:打印的调试日志是可以统一进行关闭的!!
  • 输出的日志,是开发阶段、调试程序的时候使用的。如果你的程序发布给用户,不希望用户看到这些日志的!!
  • qDebug 可以通过编译开关,来实现一键式关闭

🧑‍💻 之前调试程序, 都是用调试器 .VS/gdb,这里为啥要打印日志调试呢??

  • 调试器很多时候是有局限性的,是无法使用的,
  • 假设当前 bug 是一个概率性的 bug。出现的概率是 1% 甚至更小要想调试,无法使用调试器了
  • 使用 日志 就可以很好的解决这种问题
  • 无论是哪种方式本质上都是观察程序执行的中间过程和中间结果~

四:🔥 Qt 编程注意事项

🦋 Qt 中的命名事项

  • 类名:首字母大写,单词和单词之间首字母大写;
  • 函数名及变量名:首字母小写,单词和单词之间首字母大写
  • 起的名字要有描述性,不要使用 abc, xyz 这种比较无规律的名字来描述
  • 如果名字比较长,由多个单词构成的,就需要使用适当的方式来进行区分不同单词
  • 一般可以采用 蛇形命名法 或者 驼峰命名法

🦋 Qt 中的快捷键

在这里插入图片描述
🔧 其里面内置 Vim 插件,因此我们也可以按照使用 Vim 操作来使用

  • 注释:Ctrl + /
  • 运行:Ctrl + R
  • 编译:Ctrl + B
  • 字体缩放:Ctrl + 鼠标滑轮
  • 查找:Ctrl + F
  • 整行移动:Ctrl + Shift + ↑/↓
  • 帮助文档:F1
  • 自动对齐: Ctrl + i
  • 同名之间的.h和.cpp 的切换:F4
  • 生成函数声明的对应定义:Alt + Enter
  • 跳转到控件定义: 鼠标左键 + Ctrl,返回就是:Alt + <-

五:🔥 Qt 析构函数执行顺序

mylabel.cpp

#include "mylabel.h"
#include <iostream>
 
#include <QtDebug>
 
MyLabel::MyLabel(QWidget* parent) : QLabel(parent)
{
    qDebug() << "mylabel.cpp 构造函数被调用";
}
 
MyLabel::~MyLabel()
{
    qDebug() << "mylabel.cpp 析构函数被调用";
}

widget.cpp

#include "widget.h" // 创建生成时的文件
#include "ui_widget.h"
#include <QLabel>  // 包含标签的头文件
#include "mylabel.h"
#include <QDebug>
 
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this); // 将form file生成的界面和我们当前的widget进行关联起来
    // 使用自己定义的 MyLabel 代替原来的 QLabel,所谓的 “继承” 本质上是扩展,保持原有功能不变的基础上
    // 给对象扩展出一个析构函数,通过这个析构函数,打印一个自定义日志,方便观察程序运行结果
    MyLabel *label = new MyLabel(this);
    label->setText("Hello World");
 
}
 
Widget::~Widget()
{
    qDebug() << "midget 析构函数被调用";
    delete ui;
}
 

🔧 运行结果如下:
在这里插入图片描述

  • 对象树确保的是先释放子节点的内存,后释放父节点的内存
  • 而析构函数的调用顺序则不一定遵守上述要求,因此看到子节点的析构执行顺序反而在父节点析构顺序之后

注意:调用析构函数和释放内存并非是同一件事情。

六:🔥 共勉

🦋 小结

  1. 认识 QLabel 类,能够在界面上显示字符串。通过 setText 来设置的,参数 QString(Qt 中把 C++ 里的很多容器类,进行了重新封装,历史原因)
  2. 内存泄露 / 文件资源泄露
  3. 对象树Qt 中通过对象树,来统一的释放界面的控件对象,Qt 还是推荐使用 new 的方式在堆上创建对象,通过对象树, 统一释放对象创建对象的时候,在构造函数中, 指定父对象 (此时才会挂到对象树上),如果你的对象没有挂到对象树上,就必须要记得手动释放 !!
  4. 通过继承自 Qt 内置的类,就可以达到对现有控件进行功能扩展效果 Qt 内置的 QLabel,没法看到销毁过程的。为了看清楚,就创建类 MyLabel, 继承自 QLabel 重写析构函数。在析构函数中加上日志,直观的观察到对象释放的过程了,也可以重写控件中的任何功能。不仅仅是析构函数, 达到功能扩展目的
  5. 乱码问题 和 字符集 ~ MySQL (很多地方都涉及到)
  6. 如何在 Qt 中打印日志,作为调试信息使用 cout 固然可以, 但是并不是上策(字符编码处理的不好,也不方便统一进行关闭)Qt 中推荐使用 qDebug() 完成日志的打印

😋 以上就是我对 【Qt】新世界大门:概述 的理解, 觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~ 😉
在这里插入图片描述

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

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

相关文章

游戏引擎学习第113天

仓库:https://gitee.com/mrxiao_com/2d_game_2 黑板&#xff1a;优化的基本过程 在游戏编程中&#xff0c;优化是一个非常重要的学习内容&#xff0c;尤其是想要成为专业开发者时。优化的核心是理解代码的执行速度&#xff0c;以及如何提升其性能。在这个阶段&#xff0c;已经…

Linux 本地部署 Deepseek-R1 大模型!

DeepSeek-R1 的发布&#xff0c;掀起了一场风暴&#xff01; 开源、强大、本地可部署&#xff0c;真正私有的 AI 助手&#xff0c;不受网络、隐私等限制&#xff0c;数据安全感直接拉满&#xff01; 今天&#xff0c;手把手带你在 Linux 上本地部署 DeepSeek-R1&#xff0c;关…

【深度学习】Pytorch的深入理解和研究

一、Pytorch核心理解 PyTorch 是一个灵活且强大的深度学习框架&#xff0c;广泛应用于研究和工业领域。要深入理解和研究 PyTorch&#xff0c;需要从其核心概念、底层机制以及高级功能入手。以下是对 PyTorch 的深入理解与研究的详细说明。 1. 概念 动态计算图&#xff08;D…

IDEA + 通义灵码AI程序员:快速构建DDD后端工程模板

作者&#xff1a;陈荣健 IDEA 通义灵码AI程序员&#xff1a;快速构建DDD后端工程模板 在软件开发过程中&#xff0c;一个清晰、可维护、可扩展的架构至关重要。领域驱动设计 (DDD) 是一种软件开发方法&#xff0c;它强调将软件模型与业务领域紧密结合&#xff0c;从而构建更…

内容中台重构企业内容管理的价值维度与实施路径

内容概要 在数字化转型进程中&#xff0c;企业内容管理&#xff08;ECM&#xff09;与内容中台的差异性体现在价值维度的重构与能力边界的突破。传统ECM系统通常聚焦于文档存储、权限控制等基础功能&#xff0c;而内容中台通过标准化流程引擎与智能工具链&#xff0c;构建起覆…

CPU封装形式解析:从传统到先进封装的技术演进

中央处理器&#xff08;CPU&#xff09;的封装技术是半导体制造的关键环节&#xff0c;直接影响芯片的电气性能、散热效率和物理可靠性。随着半导体工艺的不断进步&#xff0c;封装形式从早期的简单结构演变为复杂的多维集成方案。本文将系统解析CPU的主流封装形式及其技术特点…

Spring Boot 应用(官网文档解读)

Spring Boot 启动方式 SpringApplication.run(MyApplication.class, args); Spring Boot 故障分析器 在Spring Boot 项目启动发生错误的时候&#xff0c;我们通常可以看到上面的内容&#xff0c;即 APPLICATION FAILED TO START&#xff0c;以及后面的错误描述。这个功能是通过…

【智能客服】ChatGPT大模型话术优化落地方案

本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 一、项目背景 1.1 行业背景 1.2 业务现…

1.22作业

1 Web-php-unserialize __construct()与$file、__destruct() __wakeup()检查 先绕过wakeup函数&#xff1a; O:4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}1.PHP序列化的时候对public protected private变量的处理方式是不同的 public无标…

学习aigc

DALLE2 论文 Hierarchical Text-Conditional Image Generation with CLIP Latents [2204.06125] Hierarchical Text-Conditional Image Generation with CLIP LatentsAbstract page for arXiv paper 2204.06125: Hierarchical Text-Conditional Image Generation with CLIP L…

overflow-x: auto 使用鼠标实现横向滚动,区分触摸板和鼠标滚动事件的方法

假设一个 div 的滚动只设置了 overflow-x: auto 我们发现使用鼠标的滚轮是无法左右滚动的&#xff0c;但是使用笔记本电脑的触摸板&#xff0c;或者在移动设备上是可以滚动的。所以我们需要兼容一下鼠标的横向滚动功能。 我们可以监控 wheel 事件&#xff0c;然后根据位置来计…

模拟实现Java中的计时器

定时器是什么 定时器也是软件开发中的⼀个重要组件. 类似于⼀个 "闹钟". 达到⼀个设定的时间之后, 就执⾏某个指定好的代码. 前端/后端中都会用到计时器. 定时器是⼀种实际开发中⾮常常⽤的组件. ⽐如⽹络通信中, 如果对⽅ 500ms 内没有返回数据, 则断开连接尝试重…

Ubuntu 的RabbitMQ安装

目录 1.安装Erlang 查看erlang版本 退出命令 2. 安装 RabbitMQ 3.确认安装结果 4.安装RabbitMQ管理界面 5.启动服务并访问 1.启动服务 2.查看服务状态 3.通过IP:port 访问界面 4.添加管理员用户 a&#xff09;添加用户名&#xff1a;admin&#xff0c;密码&#xff1…

vim修改只读文件

现象 解决方案 对于有root权限的用户&#xff0c;在命令行输入 :wq! 即可强制保存退出

【DeepSeek】本地部署,保姆级教程

deepseek网站链接传送门&#xff1a;DeepSeek 在这里主要介绍DeepSeek的两种部署方法&#xff0c;一种是调用API&#xff0c;一种是本地部署。 一、API调用 1.进入网址Cherry Studio - 全能的AI助手选择立即下载 2.安装时位置建议放在其他盘&#xff0c;不要放c盘 3.进入软件后…

NCRE证书构成:全国计算机等级考试证书体系详解

全国计算机等级考试&#xff08;NCRE&#xff09;证书体系为中学生提供了一个系统学习和提升计算机能力的平台。本文将详细介绍 NCRE 证书的构成&#xff0c;帮助中学生了解 NCRE 证书的级别和内容&#xff0c;规划未来职业发展。 一、NCRE 证书体系概述 NCRE 证书共分为四个级…

如何在WPS打开的word、excel文件中,使用AI?

1、百度搜索&#xff1a;Office AI官方下载 或者直接打开网址&#xff1a;https://www.office-ai.cn/static/introductions/officeai/smartdownload.html 打开后会直接提示开始下载中&#xff0c;下载完成后会让其选择下载存放位置&#xff1a; 选择位置&#xff0c;然后命名文…

【设计模式】 代理模式(静态代理、动态代理{JDK动态代理、JDK动态代理与CGLIB动态代理的区别})

代理模式 代理模式是一种结构型设计模式&#xff0c;它提供了一种替代访问的方法&#xff0c;即通过代理对象来间接访问目标对象。代理模式可以在不改变原始类代码的情况下&#xff0c;增加额外的功能&#xff0c;如权限控制、日志记录等。 静态代理 静态代理是指创建的或特…

《A++ 敏捷开发》- 16 评审与结对编程

客户&#xff1a;我们的客户以银行为主&#xff0c;他们很注重质量&#xff0c;所以一直很注重评审。他们对需求评审、代码走查等也很赞同&#xff0c;也能找到缺陷&#xff0c;对提升质量有作用。但他们最困惑的是通过设计评审很难发现缺陷。 我&#xff1a;你听说过敏捷的结对…

NutUI内网离线部署

文章目录 官网拉取源代码到本地仓库修改源代码打包构建nginx反向代理部署访问内网离线地址 在网上找了一圈没有写NutUI内网离线部署的文档&#xff0c;花了1天时间研究下&#xff0c;终于解决了。 对于有在内网离线使用的小伙伴就可以参考使用了 如果还是不会联系UP主:QQ:10927…