C++ Qt 学习(一):Qt 入门

  • Qt6 安装教程

0. 基础知识

0.1 qmake 和 cmake 对比

  • qmake:qt 独有的代码构建工具
  • cmake:C++ 通用的代码构建工具,绝大部分 C++ 开源项目都使用 cmake 管理代码
  • qt 项目,没有特殊要求,使用 qmake 即可

0.2 Qt 3 个窗口类的区别

  • QMainWindow
    • 包含菜单栏、工具栏、状态栏
    • QMainWindow 使用的场景不多
  • QWidget
    • 一个普通的窗口,不包含菜单栏、状态栏,除了登录界面
    • 新建项目时建议使用 Qwidget,因为大部分的窗口可能都要做成无边框窗口,需要自定义标题栏,实现拉伸等
  • QDialog
    • 对话框,常用来做登录窗口、弹出窗口 (例如设置界面)

1. 图片查看软件

在这里插入图片描述

1.1 main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

1.2 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();

private:
    void open1();  // 基础版本
    void open2();  // 记住上次打开的路径,并指定默认的路径为 文档/图片
    void open3();  // 图片自适应显示

private slots:
    void on_btnOpen_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

1.3 widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QSettings>  // 用于读取和写入应用程序的设置和配置信息
#include <QDebug>
#include <QStandardPaths>
#include <memory>     // 智能指针

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);

    ui->label_image->clear();
}

Widget::~Widget() {
    delete ui;
}

void Widget::open1() {
    // QFileDialog::getOpenFileName() 用于显示一个打开文件对话框,并返回用户选择的文件路径
    // 参数:指向当前窗口的指针 this,打开的窗口标题,默认打开路径,文件类型过滤器
    QString filename = QFileDialog::getOpenFileName(this, "请选择图片", "D:/", "图片(*.png *jpg);");

    if(filename.isEmpty()) {
        return;
    }

    ui->lineEdit_path->setText(filename);  // 显示图片路径
    ui->label_image->setPixmap((QPixmap(filename)));  // 显示图片
}

// 记住上次打开的路径,并指定默认的路径为 文档/图片
void Widget::open2() {
    // qApp->applicationDirPath() 用于获取当前应用程序所在的目录路径
    // "/config/Setting.ini" 是一个固定的字符串表示配置文件的路径
    QString config_path = qApp->applicationDirPath() + "/config/Setting.ini";
    qDebug() << config_path;

    // 使用智能指针创建了一个 QSettings 对象,并使用指定的配置文件路径和格式进行初始化
    // QSettings::IniFormat 是一个枚举值,用于指定配置文件的格式,此处采用的是 ini 格式的配置文件
    // 通过 pIniSet 指针调用 value() 函数来获取指定键的值,并将其转换为 QString 类型
    std::unique_ptr<QSettings> pIniSet(new QSettings(config_path, QSettings::IniFormat));
    QString lastPath = pIniSet->value("/LastPath/path").toString();

    // 设置默认读取路径为 windows 下 图片 目录
    if(lastPath.isEmpty()) {
        lastPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
    }

    QString filename = QFileDialog::getOpenFileName(this, "请选择图片", lastPath, "图片(*.png *jpg);");

    if(filename.isEmpty()) {
        return;
    }

    ui->lineEdit_path->setText(filename);
    ui->label_image->setPixmap((QPixmap(filename)));

    // 找到给定文件名(filename)中最后一个斜杠"/"的位置
    int end = filename.lastIndexOf("/");
    // 提取文件名中最后一个斜杠"/"之前的部分,即路径部分
    QString _path = filename.left(end);
    // 将键 "/LastPath/path" 的值设置为 _path
    pIniSet->setValue("/LastPath/path", _path);

    qDebug() << _path;
}

// 图片自适应显示
void Widget::open3() {
    QString config_path = qApp->applicationDirPath() + "/config/Setting.ini";
    qDebug() << config_path;

    std::unique_ptr<QSettings> pIniSet(new QSettings(config_path, QSettings::IniFormat));
    QString lastPath = pIniSet->value("/LastPath/path").toString();

    if(lastPath.isEmpty()) {
        lastPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
    }

    QString filename = QFileDialog::getOpenFileName(this, "请选择图片", lastPath, "图片(*.png *jpg);");

    if(filename.isEmpty()) {
        return;
    }

    ui->lineEdit_path->setText(filename);

    // 图片自适应显示
    std::unique_ptr<QPixmap> pix(new QPixmap(filename));  // 通过给定的文件名(filename)加载图像数据
    // 将加载的图像按照 ui->label_image 控件的大小进行缩放
    // Qt::KeepAspectRatio:保持图像的纵横比例不变
    pix->scaled(ui->label_image->size(), Qt::KeepAspectRatio);
    ui->label_image->setScaledContents(true);  // 当图像大于控件大小时,将自动缩放以适应控件的大小
    ui->label_image->setPixmap(*pix);  // 设置 ui->label_image 控件的图像为加载并缩放后的图像

    int end = filename.lastIndexOf("/");
    QString _path = filename.left(end);
    pIniSet->setValue("/LastPath/path", _path);
    qDebug() << _path;
}

void Widget::on_btnOpen_clicked() {
    //open1();
    //open2();
    open3();
}

1.4 widget.ui

在这里插入图片描述

2. C++ lambda 函数详解

  • C++ lambda 表达式的本质就是重载了 operator(),lambda 是一个类,在调用时会进行编译展开,因此 lambda 表达式对象其实就是一个匿名的 functor,所以 lambda 表达式也叫匿名函数对象
    • Qt 槽函数可以使用 lambda 函数来写
  • C++ 中 lambda 表达式的构成
    [捕获列表](形参列表) mutable 异常列表->返回类型  {
        函数体
    }
    
    • 捕获列表:捕获外部变量,捕获的变量可以在函数体中使用,可以省略,即不捕获外部变量
    • 形参列表:和普通函数的形参列表一样。可省略,即无参数列表
    • mutable:如果有,则表示在函数体中可以修改捕获变量,根据具体需求决定是否需要省略
    • 异常列表:noexcept /throw(…),和普通函数的异常列表一样,可省略,即代表可能抛出任何类型的异常
    • 返回类型:和函数的返回类型一样。可省略,如省略,编译器将自动推导返回类型
    • 函数体:代码实现,可省略,但是没意义
  • 捕获方式
    • 值捕获:不能在 lambda 表达式中修改捕获变量的值
    • 引用捕获:使用引用捕获一个外部变量,需在捕获列表变量前面加上一个引用说明符 &
    • 隐式捕获
    #include <iostream>
    
    using namespace std;
    
    int main() {
        // 1、值捕获
        int value = 100;
        
        auto f = [value](int a, int b)->int {
            //value++;  // 不能在 lambda 表达式中修改捕获变量的值
            return a + b + value;
        };
        
        cout << f(1, 2) << endl;
        
        // 2、引用捕获
        auto f2 = [&value](int a, int b)->int {
            value++;
            return a + b;
        };
        
        cout << f2(1, 3) << endl;
        cout << "value = " << value << endl;
        
        // 3、隐式捕获
            // = 值捕获
            // & 引用捕获
        int age = 123;
        auto f3 = [&](int a, int b)->int {
            value++;
            age++;
            return a + b;
        };
        
        return 0;
    }
    

3. 槽函数的常见写法

  • Qt 4 写法

    connect(ui->btnOpen, SIGNAL(clicked), this, SLOT(open()));
    
    • 不推荐这种写法,如果 SIGNAL写错了,或者信号名字、槽函数名字写错了编译器检查不出来,导致程序无响应,引起不必要的误解
  • Qt 5 写法

    connect(ui.btnOpen, QPushButton::clicked, this, &Widget::open);
    
    • 推荐使用这种写法
  • lambda 函数表达式写法

    connect(ui.btnOpen, &QPushButton::clicked, [=](){
        // 具体代码实现
    });
    
    • 适用于 slot 代码比较少的逻辑
  • 直接法

    void on_控件名_信号名();
    
    • 这种不用 connect,Qt 自动连接

4. 自定义信号及参数注册

4.1 跨 UI 发送自定义信号

在这里插入图片描述

  • 如何自定义信号

    • 使用signals声明
    • 返回值是void
    • 在需要发送的地方使用下述方法进行发送
      • emit 信号名字(参数);
    • 在需要链接的地方使用connect进行链
  • 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();
    
    private slots:
        void on_btnOpen_clicked();
    
    private:
        Ui::Widget *ui;
    };
    #endif // WIDGET_H
    
  • dialog.h

    • 跨 UI 发送:New File --> Qt Designer Form Class --> Dialog without Buttons
    #ifndef DIALOG_H
    #define DIALOG_H
    
    #include <QDialog>
    
    namespace Ui { class Dialog; }
    
    class Dialog : public QDialog {
        Q_OBJECT
    
    public:
        explicit Dialog(QWidget *parent = nullptr);
        ~Dialog();
    
    private slots:
        void on_btnAdd_clicked();
    
    signals:
        void sig_addOne(int value);
    
    private:
        Ui::Dialog *ui;
    };
    
    #endif // DIALOG_H
    
  • widget.cpp

    #include "widget.h"
    #include "ui_widget.h"
    #include "dialog.h" // 跨 UI 头文件
    
    Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
        ui->setupUi(this);
    }
    
    Widget::~Widget() {
        delete ui;
    }
    
    void Widget::on_btnOpen_clicked() {
        Dialog dlg;
    
        // 使用 lambda 函数编写槽函数
        connect(&dlg, &Dialog::sig_addOne, [=](int value) {
            ui->lineEdit->setText(QString::number(value));
        });
    
        dlg.exec();  // 需放在 connect 后,因为此行为事件循环会阻塞 UI
    }
    
  • dialog.cpp

    #include "dialog.h"
    #include "ui_dialog.h"
    
    Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) {
        ui->setupUi(this);
    }
    
    Dialog::~Dialog() {
        delete ui;
    }
    
    void Dialog::on_btnAdd_clicked() {
        static int value = 100;
        emit sig_addOne(value++); // 实现跨 UI 自加操作
    }
    

4.2 跨线程发送自定义信号

Qt 的子线程无法直接修改 ui,需要发送信号到 ui 线程进行修改

  • widget.h

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    #include "childthread.h"
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    class Widget : public QWidget {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    private slots:
        void on_btnUpdate_clicked();
        void showInfo(Score s);
    
    private:
        Ui::Widget *ui;
    };
    #endif // WIDGET_H
    
  • widget.cpp

    #include "widget.h"
    #include "ui_widget.h"
    #include <QDebug>
    
    Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
        ui->setupUi(this);
    
        qDebug() << "ui thread id = " << QThread::currentThreadId();
    }
    
    Widget::~Widget() {
        delete ui;
    }
    
    void Widget::on_btnUpdate_clicked() {
        ChildThread *ch = new ChildThread();
    
        // 以下实现还是在子线程中(不在 ui 线程中),无法直接修改 ui
    //    connect(ch, &ChildThread::sig_SendToUI, [=](Score s) {
    //        string info = s.name + "id = " + to_string(s.id) + " age = " + to_string(s.age);
    //        ui->lineEdit->setText(QString::fromStdString(info));
    //        // 用于验证 slot 与 ui 下是否同属一个线程(id)
    //        // 结果表明此处的 slot 与子线程的 run() 同属一个线程(id)
    //        qDebug() << "slot thread id = " << QThread::currentThreadId();
    //    });
    
        // 以下实现在 ui 线程 (主线程) 中,可以直接修改 ui
        connect(ch, &ChildThread::sig_SendToUI, this, &Widget::showInfo);
    
        ch->start();
    }
    
    void Widget::showInfo(Score s) {
        qDebug() << "ui thread id2 = " << QThread::currentThreadId();
    
        string info = s.name + "id = " + to_string(s.id) + " age = " + to_string(s.age);
        ui->lineEdit->setText(QString::fromStdString(info));
    }
    
  • childthread.h

    #ifndef CHILDTHREAD_H
    #define CHILDTHREAD_H
    
    #include <QThread>
    #include <string>
    
    using namespace std;
    
    struct Score {
        string name;
        int id;
        int age;
    };
    
    class ChildThread : public QThread {
        Q_OBJECT
    
    public:
        ChildThread();
    
    protected:
        void run() override;
    
    signals:
        void sig_SendToUI(Score score);
    };
    
    #endif // CHILDTHREAD_H
    
  • childthread.cpp

    #include "childthread.h"
    #include <QDebug>
    
    ChildThread::ChildThread() {
        // 非基础类型参数需要注册
        qRegisterMetaType<Score>("Score");
        qRegisterMetaType<string>("string");
    }
    
    void ChildThread::run() {
        qDebug() << "run thread id = " << QThread::currentThreadId();
    
        Score s;
        s.name = "jack";
        s.id = 1001;
        s.age = 26;
    
        emit sig_SendToUI(s);  // 发送信号
    }
    

4.3 处理信号重名问题

  • 例如 QComboBox 的信号

    Q_SIGNALS:
        void currentIndexChanged(int index);
        void currentIndexChanged(const QString &);
    
  • 解决方案

    // 错误写法
    connect(ui->comboBox, &QComboBox::currentIndexChanged, this, &Widget::onIndex);
    
    // 解决方案一
    connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onIndex(int)));
    
    // 解决方案二
    connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Widget::onIndex);
    

5. connect 函数详解

template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
                                              Func1 signal,
                                              const typename QtPrivate::FunctionPointer<Func2>::Object *receiver,
                                              Func2 slot,
                                              Qt::ConnectionType type = Qt::AutoConnection)
enum ConnectionType {
    AutoConnection,
    DirectConnection,
    QueuedConnection,
    BlockingQueuedConnection,
    UniqueConnection =  0x80,
    SingleShotConnection = 0x100,
};
  • AutoConnection

    • 默认连接方式,如果接收方在发出信号的线程中,使用 Qt::DirectConnection
    • 否则使用 Qt::QueuedConnection,在发出信号时确定连接类型
    • Qt 中默认使用 AutoConnection,所以平时写信号槽时都是 4 个参数
  • DirectConnection

    • 当发出信号时,插槽立即被调用,槽在发送信号的线程中执行
  • QueuedConnection

    • 当控制返回到接收方线程的事件循环时调用槽,槽在接收方的线程中执行
  • BlockingQueuedConnection

    • 与 Qt::QueuedConnection 相同,只是发送信号的线程会阻塞,直到槽返回
    • 如果接收方存在于发送信号的线程中,则不能使用此连接,否则应用程序将产生死锁
  • UniqueConnection

    • 这是一个可以使用按位 OR 与上述任何一种连接类型组合的标志,当 Qt::UniqueConnection 被设置时,如果连接已经存在,QObject::connect() 将失败 (例如,如果相同的信号已经连接到相同的对象对的插槽)

6. Qt 信号槽与 MOC

  • moc 全称是 Meta-Object Compiler,也就是 “元对象编译器”
    • Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件
    • 如果发现在一个头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件,这个源文件中包含了 Q_OBJECT 宏的实现代码,这个新的文件名是原文件名前面加上 moc_ 构成,这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此,这个新的文件不是 “替换” 掉旧的文件,而是与原文件一起参与编译
    • 另外,还可看出:moc 的执行是在预处理器之前,因为预处理器执行之后,Q_OBJECT 宏就不存在了

    可以这么理解,moc 把 Qt 中一些不是 C++ 的关键字做了解析,让 C++ 编译器认识,例如:slots, signals,emit 等,moc 会把这些重新编译解析

7. Qt 内存管理机制

  • C++ 派生类

    • 构造顺序:先执行基类的构造函数,再执行派生类的构造函数
    • 析构顺序:先执行派生类的析构函数,再执行基类的析构函数
  • Qt 半内存管理机制

    • QObject 及其派生类的对象,如果其 parent 非 0,那么其 parent 析构时会析构该对象
    • QWidget 及其派生类的对象,可以设置 Qt::WA_DeleteOnClose 标志位,当 close 时会调用 QWidgetPrivate::close_helper,进而调用 deleteLater 析构该对象

8. 解决 Qt 中文乱码问题

  • 粘贴别人的代码时,首先在记事本里复制一遍,再粘贴到 QtCreator

  • 使用 u8

    • ui.pushButton->setText (u8"你好")
  • 不使用 QtCreator开发,直接使用 vs2019

  • 其他设置

    • QtCreator — 选项 — 文本编辑器 — UTF8 BOM 总是删除
    • #pragma execution_character_set(“utf-8”)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB野外观测站生态气象数据处理分析实践应用

1.基于MATLAB语言 2.以实践案例为主&#xff0c;提供所有代码 3.原理与操作结合 4.布置作业&#xff0c;答疑与拓展 示意图&#xff1a; 以野外观测站高频时序生态气象数据为例&#xff0c;基于MATLAB开展上机操作&#xff1a; 1.不同生态气象要素文件的数据读写与批处理实现 …

微信小程序自定义弹窗阻止滑动冒泡catchtouchmove之后弹窗内部内容无法滑动

自定义弹窗 如图所示&#xff1a; 自定义弹窗内部有带滚动条的盒子区域 问题&#xff1a; 在盒子上滑动&#xff0c;页面如果超出一屏的话&#xff0c;也会跟着一起上下滚动 解决方案&#xff1a;给自定义弹窗 添加 catchtouchmove 事件&#xff0c;阻止冒泡即可 网上不少…

Linux 安装 Redis7.x

Linux 安装 Redis7.x 下载redis7检查linux版本检查是否有 gcc什么是 gcc查看 gcc 是否有安装 安装 redis7查看默认安装目录启动服务连接服务服务关闭Redis的删除卸载Redis数据类型 下载redis7 下载地址&#xff1a;https://download.redis.io/releases/ 检查linux版本 [root…

Oracle JDK 和OpenJDK两者有什么异同点

Oracle JDK 和 OpenJDK 是两种不同版本的 Java Development Kit&#xff08;Java 开发工具包&#xff09;&#xff0c;它们都提供了用于开发 Java 程序的一系列工具和库。以下是它们之间的一些主要异同点&#xff1a; 相同点&#xff1a; 功能&#xff1a;在大多数情况下&…

【C++入门 三】学习C++缺省参数 | 函数重载 | 引用

C入门 三 1.缺省参数1.1 缺省参数概念1.2 缺省参数分类 2. 函数重载2.1 函数重载概念2.2 C支持函数重载的原理--名字修饰(name Mangling) 3.引用3.1引用概念3.2引用特性3.3 常引用3.4 使用场景1. 做参数2. 做返回值 3.5 传值、传引用效率比较3.6引用和指针的区别 4.引用和指针的…

2023最新ChatGPT商业运营系统源码+支持GPT4/支持ai绘画+支持Midjourney绘画

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

Linux进程基础

文章目录 1.进程概念2.进程描述3.进程操作&#xff08;一&#xff09;3.1.进程查看3.2.进程获取3.3.进程终止3.4.进程创建 4.进程状态4.1.进程状态理论4.1.1.粗略理解4.1.2.深入理解 4.2.进程状态实现4.2.1.运行状态和浅度/深度睡眠4.2.2.暂停状态和停止并跟踪状态4.2.3.终止状…

19.4 Boost Asio 远程命令执行

命令执行机制的实现与原生套接字通信一致&#xff0c;仅仅只是在调用时采用了Boost通用接口&#xff0c;在服务端中我们通过封装实现一个run_command函数&#xff0c;该函数用于发送一个字符串命令&#xff0c;并循环等待接收客户端返回的字符串&#xff0c;当接收到结束标志go…

工作中的小tips:如何快速提取图片或者pdf上的文字,进行编辑?

工作中经常会碰到需要的材料是图片或者不能拷贝的pdf之类的情况&#xff0c;那么有没有办法快速从上面提取文字呢&#xff1f; 最近发现一个很好用的网站&#xff0c;百度翻译。首先说明一下&#xff0c;接下来的方法比较适合短一点的文字&#xff0c;像是大篇幅的那种不太适合…

Linux学习第30天:Linux 自带的 LED 灯驱动实验:驱动开发思维方式的转变势在必行

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 学习嵌入式Linux驱动开发整整30天了。今天简单做一个小结。因为之前的主要工作是做ARM的裸机开发&#xff0c;所以接触Linux以后感觉很多东西都变了。不仅仅包括…

如何选择安全又可靠的文件数据同步软件?

数据实时同步价值体现在它能够确保数据在多个设备或系统之间实时更新和保持一致。这种技术可以应用于许多领域&#xff0c;如电子商务、社交媒体、金融服务等。在这些领域中&#xff0c;数据实时同步可以带来很多好处&#xff0c;如提高工作效率、减少数据不一致、提高用户体验…

【Verilog 教程】7.3 Verilog 串行 FIR 滤波器设计

串行 FIR 滤波器设计 设计说明 设计参数不变&#xff0c;与并行 FIR 滤波器参数一致。即&#xff0c;输入频率为 7.5 MHz 和 250 KHz 的正弦波混合信号&#xff0c;经过 FIR 滤波器后&#xff0c;高频信号 7.5MHz 被滤除&#xff0c;只保留 250KMHz 的信号。 输入频率&#x…

uniapp 省市区三级联动选择器

还有半个小时下班&#xff0c;总想着发点光亮照耀他人。IT技术这东西&#xff0c;尤其是UI方面的东西&#xff0c;于用户体验至关重要&#xff0c;想想最近使用uni-data-picker的丑陋页面&#xff0c;自己重构了这个功能&#xff0c;新加实现&#xff0c;效果图如下&#xff0c…

使用 Curl 和 DomCrawler 下载抖音视频链接并存储到指定文件夹

项目需求 假设我们需要从抖音平台上下载一些特定的视频&#xff0c;以便进行分析、编辑或其他用途。为了实现这个目标&#xff0c;我们需要编写一个爬虫程序来获取抖音视频的链接&#xff0c;并将其保存到本地文件夹中。 目标分析 在开始编写爬虫之前&#xff0c;我们需要了…

【Linux】配置JDKTomcat开发环境及MySQL安装和后端项目部署

目录 一、jdk安装配置 1. 传入资源 2. 解压 3. 配置 二、Tomcat安装 1. 解压开启 2. 开放端口 三、MySQL安装 1. 解压安装 2. 登入配置 四、后端部署 1. 数据库 2. 导入.war包 3. 修改端口 4.开启访问 一、jdk安装配置 打开虚拟机 Centos 登入账号&#xff…

MySQL -- 内置函数

MySQL – 内置函数 文章目录 MySQL -- 内置函数一、日期函数1.current_date()获取年月日2.current_time()获取时分秒3.current_timestamp() / now()获得时间戳4.date_add()在日期的基础上加日期5.date_sub()在日期的基础上减去日期6. datediff()计算两个日期之间相差多少天7.案…

inquirer.js——交互式命令行用户界面

一、什么是inquirer.js 1、inquirer.js是一个开源的交互式命令行用户界面&#xff08;CLI&#xff09;库&#xff0c;可以让你轻松地与用户进行交互&#xff0c;获取用户输入并做出相应的处理。它的主要功能是提供了一系列常用的命令行交互界面组件&#xff0c;例如input、con…

【Redis】高并发分布式结构服务器

文章目录 服务端高并发分布式结构名词基本概念评价指标1.单机架构缺点 2.应用数据分离架构应用服务集群架构读写分离/主从分离架构引入缓存-冷热分离架构分库分表&#xff08;垂直分库&#xff09;业务拆分⸺微服务 总结 服务端高并发分布式结构 名词基本概念 应⽤&#xff0…

Docker的安装、基础命令与项目部署

文章目录 前言一、docker安装与MySQL部署1.Linux环境下docker的安装&#xff08;1&#xff09;基于CentOS7&#xff08;2&#xff09;基于Ubuntu 二、docker基础1.常见命令&#xff08;1&#xff09;快速创建一个mysql容器&#xff08;MySQL得一键安装&#xff09;。&#xff0…

centos7 部署 Flink

1. 准备 安装的前提是虚拟机里已安装了jdk 去官网下载 Flink 所有版本下载地址&#xff1a;https://archive.apache.org/dist/flink/ 找到下图的安装包&#xff0c;下载即可 下载完后&#xff0c;将其上传至虚拟机的某个地方&#xff0c;本人将其放在 /home/flink/ 下 解压…
最新文章