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

news2025/2/25 12:39:18
  • 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.coloradmin.cn/o/1158887.html

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

相关文章

代码随想录Day34 LeetCode T343整数拆分 T96 不同的二叉搜索树

目录 前言 LeetCode T343 整数拆分 题目思路: 第一步:确定递归数组含义 第三步:初始化dp数组(其实也是为接下来的递推做准备) 第四步:确定遍历顺序(很多情况下是对遍历顺序有要求的) 第五步:打印dp数组(如果遇到错误可以打印一下dp数组看看和我们推理的dp数组有啥不同,错…

0基础学习PyFlink——使用DataStream进行字数统计

大纲 sourceMapSplittingMapping ReduceKeyingReducing 完整代码结构参考资料 在《0基础学习PyFlink——模拟Hadoop流程》一文中&#xff0c;我们看到Hadoop在处理大数据时的MapReduce过程。 本节介绍的DataStream API&#xff0c;则使用了类似的结构。 source 为了方便&…

OSPF高级特性

OSPF高级特性(1) 一、OSPF不规则区域类型 产生原因&#xff1a;区域划分不合理&#xff0c;导致的问题 1、非骨干区域无法和骨干区域保持连通 2、骨干区域被分割 造成后果&#xff1a;非骨干区域没和骨干区域相连&#xff0c;导致ABR将不会帮忙转发区域间的路由信息。非骨干区…

MS3142电机驱动器可兼容LV8548M

MS3142/MS3142S 是一个双全桥电机驱动。可兼容LV8548M&#xff08;功能基本一致&#xff0c;管脚不兼容&#xff09;。电源电压供电范围 4V 到 18V&#xff0c;平均电流 1.1A&#xff0c;电流峰值 1.54A。如果需要更高的电流能力&#xff0c;可以将双全桥并联使用。 四个输入脚…

数据结构预算法--顺序表

1.顺序表 1.1概念及结构 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改。 顺序表一般可以分为&#xff1a; 1. 静态顺序表&#xff1a;使用定长数组存储元素。 2. 动态顺序表&#xff1a;使…

Shiro 身份验证绕过 (CVE-2020-13933)

一、漏洞描述 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。 Apache Shiro身份验证绕过漏洞CVE-2020-11989的修复补丁存在缺陷&#xff0c;在1.5.3及其之前的版本&#xff0c;由于shiro在处理url时与spring仍然存在差异&#xff0c;依然存…

Day42 力扣动态规划 :123.买卖股票的最佳时机III |188.买卖股票的最佳时机IV

Day42 力扣动态规划 :123.买卖股票的最佳时机III &#xff5c;188.买卖股票的最佳时机IV 123.买卖股票的最佳时机III第一印象看完题解的思路dp数组&#xff1a;递推公式&#xff1a;初始化遍历顺序 实现中的困难感悟代码 188.买卖股票的最佳时机IV第一印象初始化递推公式看完题…

黄金矿工小游戏

欢迎来到程序小院 黄金矿工 玩法&#xff1a;点击开始游戏&#xff0c;黄金和钩子&#xff0c;钩子会左右摆动&#xff0c;对准黄金位置点击鼠标左键钓起黄金加对应时间&#xff0c;钓起黑色四块减去响应时间&#xff0c;快去挖矿吧^^。开始游戏https://www.ormcc.com/play/ga…

【错误解决方案】Error: module ‘cv2‘ has no attribute ‘SURF‘

1. 错误提示 python-opencv高版本中&#xff0c;AttributeError: module cv2 has no attribute SURF问题&#xff1b; 错误提示&#xff1a;Error: module ‘cv2‘ has no attribute ‘SURF‘ 2. 解决方案 解决&#xff1a;将sift cv2.SIFT()替换为&#xff1a;sift cv2.x…

windows内存取证-中等难度-下篇

上文我们对第一台Target机器进行内存取证&#xff0c;今天我们继续往下学习&#xff0c;内存镜像请从上篇获取&#xff0c;这里不再进行赘述​ Gideon 攻击者访问了“Gideon”&#xff0c;他们向AllSafeCyberSec域控制器窃取文件,他们使用的密码是什么&#xff1f; 攻击者执…

day57--动态规划15

392.判断子序列 115.不同的子序列 第一题&#xff1a;判断子序列 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&…

大模型冷思考:企业“可控”价值创造空间还有多少?

文 | 智能相对论 作者 | 叶远风 毫无疑问&#xff0c;大模型热潮正一浪高过一浪。 在发展进程上&#xff0c;从最开始的技术比拼到现在已开始全面强调商业价值变现&#xff0c;百度、科大讯飞等厂商都喊出类似“不能落地的大模型没有意义”等口号。 在模型类型上&#xff0…

2023年【高压电工】考试及高压电工找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 高压电工考试参考答案及高压电工考试试题解析是安全生产模拟考试一点通题库老师及高压电工操作证已考过的学员汇总&#xff0c;相对有效帮助高压电工找解析学员顺利通过考试。 1、【单选题】 额定容量是指:在变压器铭…

Spring源码分析篇:@Autowired 是怎样完成注入的?究竟是byType还是byName亦两者皆有

1. 五种不同场景下 Autowired 的使用 第一种情况 上下文中只有一个同类型的bean 配置类 package org.example.bean; ​ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; ​ Configuration public class…

docker-compose 简单部署MySQL Database

docker-compose 简单部署MySQL Database 本博文部署MySQL 并与上篇部署的 Flask进行关联 主博客目录&#xff1a;《从零开始学习搭建量化平台笔记》 文章目录 docker-compose 简单部署MySQL Database部署 MySQLMySQL 开放端口与权限 主项目计划需要搭建一个MySQL 数据库为其他部…

“零基础”PHP代码审计入门

目录 一、代码审计目的 二、代码审计基础 三、 代码审计思路 四、PHP核心配置 五、 代码审计环境 六、手动调试代码 七、PHP的弱类型 八、学习漏洞函数 九、审计入门总结 推荐一些demo&#xff1a; 一、代码审计目的 代码审计指的是对源代码进行检查&#xff0c;寻找…

智慧校园地下管线三维可视化管控平台减少人力和物力资源的浪费

随着科技的不断发展&#xff0c;三维可视化管理平台在各个领域得到了广泛的应用。三维可视化管理平台通过将数据以三维形式呈现&#xff0c;使得用户能够更直观地理解和分析数据&#xff0c;从而提高工作效率和决策质量。 VR数字孪生园区系统是通过将实际园区的各种数据和信息进…

开关电源老化试验和性能检测系统软件

开关电源自动测试系统由PC(工控机)、测试工装、可编程直流电源、数字示波器、可编程直流电子负载、继电器模块等部分组成&#xff0c;并通过RS232/LAN通讯总线、测试夹具以及其它线缆等进行连接&#xff0c;为系统组成结构。PC与可编程直流电源、数字示波器、可编程直流电子负载…

c++装饰器模式

前言 装饰器模式&#xff0c;就是可以对一个对象无限装饰一些东西&#xff0c;而且可以没有顺序。比如一个人可能只会说出他的名字&#xff0c;但是可以让他再说哈哈&#xff0c;可以说完哈哈之后再说哇哇。如何后面又不想装饰了&#xff0c;不需要改类原来的代码&#xff0c;…

什么是数字展览馆,数字展览馆有什么应用前景

引言&#xff1a; 数字展览馆作为一种新兴的文化艺术展示形式&#xff0c;以数字化技术和虚拟现实为基础&#xff0c;正在逐渐改变传统展览的方式。 一、什么是数字展览馆&#xff1f; 1.定义 数字展览馆是利用数字技术和虚拟现实技术打造的一种线上文化艺术展示平台。通过虚…