嵌入式养成计划-46----QT--简易版网络聊天室实现--QT如何连接数据库

news2024/11/17 7:33:27

一百一十九、简易版网络聊天室实现

119.1 QT实现连接TCP协议

119.1.1 基于TCP的通信流程

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

119.1.2 QT中实现服务器过程

  1. 使用QTcpServer实例化一个服务器对象
  2. 设置监听状态,通过listen()函数,可以监听特定的主机,也可以监听所有客户端,端口号可以是系统自动分配的,也可以是指定端口号。
  3. 如果有客户端发来连接请求,那么服务器就自动发射一个newConnection信号,我们就可以将该信号连接到自定义的槽函数中处理该客户端的操作。
  4. 此时服务器和客户端已经建起了连接,可以调用nextPandingConnection获取最新连接的客户端套接字,可以将该套接字存放在服务器的客户端容器中。
  5. 当客户端发来数据时,该客户端就会自动发射一个readyRead信号,我们可以将该信号连接到自定义的槽函数中读取客户端数据。
  6. 通过read(),readLine(),readAll()读取套接字里的数据,可以通过write()往套接字中写入数据
  7. 关闭服务器使用close即可

119.1.3 QT中实现客户端过程

  1. 使用QTcpSocket实例化一个客户端对象
  2. 将客户端连接到服务器,使用connectToHost, 给定主机地址,端口号
  3. 如果连接成功,该客户端会自动发射connected信号,我们可以将该信号连接到自定义的槽函数中处理相关逻辑代码。
  4. 此时,客户端和服务器已经建立了连接,如果服务端发来数据,那么该客户端会自定发射readyRead信号,可以将该信号连接到自定义的槽函数中读取服务端中数据
  5. 可以使用read() readLine() readAll()读取套接字中数据,使用write往套接字中写入数据
  6. 客户端断开与服务器的连接,使用disConnectFromHost, 如果断开成功,客户端会自动发射disconnected信号,我们可以将该信号连接到自定义的槽函数中处理相关逻辑代码。

119.2 服务器端

119.2.1 UI 界面

在这里插入图片描述

119.2.2 qt_server.h

#ifndef QT_SERVER_H
#define QT_SERVER_H

#include <QWidget>
#include <QTcpServer>
#include <QList>
#include <QTcpSocket>
#include <QMessageBox>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class Qt_Server; }
QT_END_NAMESPACE

class Qt_Server : public QWidget
{
    Q_OBJECT

public:
    Qt_Server(QWidget *parent = nullptr);
    ~Qt_Server();

private slots:
    void on_startBtn_clicked();

public slots:
    //  自定义的 处理连接的槽函数声明
    void newConnection_slot();
    //  自定义的 处理接收数据的槽函数声明
    void readyRead_slot();

private:
    Ui::Qt_Server *ui;

    QTcpServer *server;

    QList<QTcpSocket *> socketList;
};
#endif // QT_SERVER_H

119.2.3 qt_server.cpp

#include "qt_server.h"
#include "ui_qt_server.h"

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

    //  给服务器指针创建空间
    server = new QTcpServer(this);
//    socketList = new QList<>();
}

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

//  点击启动服务器按钮对应的槽函数实现
void Qt_Server::on_startBtn_clicked()
{
    if(ui->startBtn->text() == "启动服务器"){
        //  获取UI界面上输入的端口号
        quint16 port = ui->portEdit->text().toUInt();
        //  使服务器进入监听状态,返回值是bool类型
        if(server->listen(QHostAddress::Any,port)){
            QMessageBox::information(this,"提示","服务器启动成功");
        }else{
            QMessageBox::critical(this,"错误","服务器启动失败");
        }
        //  此时服务器已经进入监听状态,如果有客户端发来链接请求,
        //  则服务器会自动发射一个newConnection信号,需要将该信号连接到自定义的槽函数中处理连接的套接字
        connect(server, &QTcpServer::newConnection, this, &Qt_Server::newConnection_slot);
        ui->startBtn->setText("已启动服务器");
        ui->startBtn->setStyleSheet("background-color:green");
    }else{
        server->close();
        disconnect(server, &QTcpServer::newConnection, this, &Qt_Server::newConnection_slot);
        ui->startBtn->setStyleSheet("background-color:white");
        ui->startBtn->setText("启动服务器");
    }

}

//  自定义的 处理连接的槽函数声明
void Qt_Server::newConnection_slot()
{
    qDebug() << "有新客户的连接";
    //  获取最新连接的客户端套接字,并放入容器中,此时客户端与服务器已经建立起连接
    QTcpSocket *s = server->nextPendingConnection();
    socketList.push_back(s);

    //  如果有客户端发送数据,那么此客户端就会自动发射readyRead信号
    //  需要将该信号与自定义的处理接收数据的槽函数连接
    connect(s, &QTcpSocket::readyRead, this, &Qt_Server::readyRead_slot);
}

//  自定义的 处理接收数据的槽函数实现
void Qt_Server::readyRead_slot()
{
    //  移除无效的客户端
    //  遍历所有的客户端
    for(int i=0; i<socketList.count(); i++){
        //  判断每个客户端的状态,返回值是枚举类型
        //  socketList.at(i)->state();
        if(0 == socketList.at(i)->state()){
            //  移除当前客户端,通过下标删除
            socketList.removeAt(i);
        }
    }
    //  遍历容器,找到有需要读取数据的客户端
    for(int i=0; i<socketList.count(); i++){
        //  如果当前客户端的 有效字节数 不为0,代表当前客户端有需要读取的数据
        if(0 != socketList.at(i)->bytesAvailable()){
            //  读取客户端中的数据
            QByteArray msg = socketList.at(i)->readAll();
            //  将读取的数据放到UI界面上
            ui->listWidget->addItem(QString::fromLocal8Bit(msg));

            //  将数据发送给所有客户端
            for (int j=0; j<socketList.count(); j++) {
                socketList.at(i)->write(msg);
            }
        }
    }
}

119.2.4 main.cpp

#include "qt_server.h"

#include <QApplication>

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

119.3 客户端

119.3.1 UI 界面

在这里插入图片描述

119.3.2 qt_client.h

#ifndef QT_CLIENT_H
#define QT_CLIENT_H

#include <QWidget>
#include <QTcpServer>
#include <QList>
#include <QTcpSocket>
#include <QMessageBox>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class Qt_Client; }
QT_END_NAMESPACE

class Qt_Client : public QWidget
{
    Q_OBJECT

public:
    Qt_Client(QWidget *parent = nullptr);
    ~Qt_Client();

public slots:
    void connected_slot();
    void readyRead_slot();

private slots:
    void on_connectBtn_clicked();

    void on_msgBtn_clicked();

    void on_disconnectBtn_clicked();

private:
    Ui::Qt_Client *ui;

    //  定一个客户端对象
    QTcpSocket *socket;
    //  定义一个用户名变量
    QString uname;
};
#endif // QT_CLIENT_H

119.3.3 qt_client.cpp

#include "qt_client.h"
#include "ui_qt_client.h"

Qt_Client::Qt_Client(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Qt_Client)
{
    ui->setupUi(this);
    //  给客户端对象创建空间
    socket = new QTcpSocket(this);

    //  初始化UI界面的组件状态
    ui->msgEdit->setEnabled(false);
    ui->msgBtn->setEnabled(false);
    ui->disconnectBtn->setEnabled(false);

}

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

void Qt_Client::connected_slot()
{
    //  告诉服务器我进来了
    QString msg = uname + " 来了,快接驾";
    //  将这个消息发送给服务器
    socket->write(msg.toLocal8Bit());
    //  此时说明服务器与客户端已经建立连接

    //  现在将UI界面的组件状态修改一下
    ui->msgEdit->setEnabled(true);
    ui->msgBtn->setEnabled(true);
    ui->disconnectBtn->setEnabled(true);
    ui->unameEdit->setEnabled(false);
    ui->ipEdit->setEnabled(false);
    ui->portEdit->setEnabled(false);
    ui->connectBtn->setEnabled(false);

    //  如果服务器发来数据,客户端会自动发射readyRead信号
    //  因此需要将readyRead信号连接到自定义的槽函数
    //  因为只需要连接一次,所以也是该在构造函数中写连接函数
    connect(socket, &QTcpSocket::readyRead, this, &Qt_Client::readyRead_slot);
}

void Qt_Client::readyRead_slot()
{
    //  走到了这一步,说明服务器给客户端发送了消息,现在需要进行读取
    QByteArray msg = socket->readAll();
    //  将这个数据放到UI界面的消息显示框中
    ui->listWidget->addItem(QString::fromLocal8Bit(msg));
}

//  连接服务器按钮 对应的槽函数
void Qt_Client::on_connectBtn_clicked()
{
    //  获取UI界面的IP和PORT,还有uname
    QString ip = ui->ipEdit->text();
    quint16 port = ui->portEdit->text().toUInt();
    uname = ui->unameEdit->text();
    //  使客户端连接服务器
    socket->connectToHost(ip, port);

    //  判断客户端是否成功连接服务器,成功则客户端会自动发射connected信号
    //  将该信号连接到自定义的槽函数中
    //  因为只需要连接一次,所以连接函数应该写在构造函数中
    connect(socket, &QTcpSocket::connected, this, &Qt_Client::connected_slot);
}

void Qt_Client::on_msgBtn_clicked()
{
    //  获取UI界面上输入的内容
    QString msg = uname + " : " + ui->msgEdit->toPlainText();
    //  将消息发送给服务器
    socket->write(msg.toLocal8Bit());

}

void Qt_Client::on_disconnectBtn_clicked()
{
    QString msg = uname + " 走咯,我还会再回来的";
    //  将消息发送给服务器
    socket->write(msg.toLocal8Bit());
    //  断开链接
    socket->close();
    disconnect(socket, &QTcpSocket::readyRead, this, &Qt_Client::readyRead_slot);
    disconnect(socket, &QTcpSocket::connected, this, &Qt_Client::connected_slot);

    //  更改UI界面的组件状态
    ui->msgEdit->setEnabled(false);
    ui->msgBtn->setEnabled(false);
    ui->disconnectBtn->setEnabled(false);
    ui->unameEdit->setEnabled(true);
    ui->ipEdit->setEnabled(true);
    ui->portEdit->setEnabled(true);
    ui->connectBtn->setEnabled(true);
}

119.3.4 main.cpp

#include "qt_client.h"

#include <QApplication>

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

一百二十、QT连接数据库

120.1 QT将数据库分为三个层次

  1. 数据库驱动层:QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorBase、QSqlDriverPlugin
  2. sql接口层:QSqlDatabase、QSqlQuery、QSqlRecord、QSqlError
  3. 用户接口层:提供一些模型QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel

120.2 实现数据库操作的相关方法

1.	添加数据库:[static] QSqlDatabase QSqlDatabase::addDatabase(QSqlDriver *driver, const QString &connectionName = QLatin1String(defaultConnection))                                                                
		QSQLITE SQLite version 3 or above
2.	设置数据库名称:void QSqlDatabase::setDatabaseName(const QString &name)
3.	包含数据库:bool QSqlDatabase::contains(const QString &connectionName = QLatin1String(defaultConnection))
4.	打开数据库:bool QSqlDriver::open(const QString &db)
5.	关闭数据库:void QSqlDatabase::close()
6.	错误信息:QSqlError QSqlDatabase::lastError()

7.	sql语句执行:构造一个QSqlQuery类对象,调用其成员函数exec,执行sql语句:bool QSqlQuery::exec(const QString &query)
8.	bool QSqlQuery::next():遍历查询结果的函数

120.3 示例 :

120.3.1 UI 界面

120.3.2 database.h

#ifndef DATABASE_H
#define DATABASE_H

#include <QWidget>
#include <QSqlDatabase> //  数据库管理类
#include <QSqlQuery>    //  数据库操作类
#include <QSqlRecord>   //  数据库记录类
#include <QSqlError>    //  数据库错误类
#include <QMessageBox>  //  消息对话框类
#include <QtDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class DataBase; }
QT_END_NAMESPACE

class DataBase : public QWidget
{
    Q_OBJECT

public:
    DataBase(QWidget *parent = nullptr);
    ~DataBase();

private slots:
    void on_addBtn_clicked();

    void on_showAllEdit_clicked();

    void on_showEdit_clicked();

    void on_deleteBtn_clicked();

    void on_deleteAllBtn_clicked();

private:
    Ui::DataBase *ui;

    //  实例化一个数据库对象
    QSqlDatabase db;
};
#endif // DATABASE_H

120.3.3 database.cpp

#include "database.h"
#include "ui_database.h"

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

    //  判断数据库是否存在
    if(!db.contains())
    {
        //  不存在,则创建数据库
        db = QSqlDatabase::addDatabase("QSQLITE");   //  表示数据库驱动为sqlite3
        //  给刚创建的数据库起名
        db.setDatabaseName("stuInfo.db");

        //  提示用户:数据库创建成功
        QMessageBox::information(this,"提示","数据库创建成功");
    }
    //  打开数据库
    if(!db.open()){
        QMessageBox::critical(this,"错误","数据库无法打开");
        return;
    }
    //  创建数据库表
    QSqlQuery table;
    //  Sql语句
    QString sql = "create table if not exists stu_info_table("
                  "id integer primary key autoincrement,"
                  "numb integer,"
                  "name varchar(20),"
                  "sex varchar(4),"
                  "score integer);";
    if(table.exec(sql)){
        QMessageBox::information(this,"提示","数据库学生表创建成功");
    }else{
        QMessageBox::critical(this,"错误","数据库学生表创建失败");
    }
}

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

//  添加
void DataBase::on_addBtn_clicked()
{
    //  获取UI界面上的信息
    int num = ui->numEdit->text().toUInt();
    QString name = ui->nameEdit->text();
    QString sex = ui->sexEdit->text();
    int score = ui->scoreEdit->text().toUInt();

    //  保证用户输入完整信息
    if(num == 0 || name.isEmpty() || sex.isEmpty() || score == 0){
        QMessageBox::warning(this,"警告","请完善信息");
        return;
    }

    //  将信息存放到数据库学生表中
    QSqlQuery query;
    //  SQL语句
    QString sql = QString("insert into stu_info_table (numb, name, sex, score) "
                          "values(%1,'%2','%3',%4)")
            .arg(num).arg(name).arg(sex).arg(score);

    //  执行SQL语句
    if(query.exec(sql)){
        QMessageBox::information(this,"提示","数据添加成功");
    }else{
        QMessageBox::critical(this,"错误","数据添加失败");
    }
    DataBase::on_showAllEdit_clicked();
}

//  查看所有
void DataBase::on_showAllEdit_clicked()
{
    ui->tableWidget->clear();
    //  将信息存放到数据库学生表中
    QSqlQuery query;
    //  SQL语句
    QString sql = QString("select * from stu_info_table");
    //  执行SQL语句
    if(query.exec(sql)){
        //  将数据库的内容放到UI界面上
        int i = 0;  //  记录行号
        //  用next()遍历
        while (query.next()) {
            for(int j=0; j<query.record().count(); j++){
                ui->tableWidget->setItem(i,j,new QTableWidgetItem(query.value(j+1).toString()));
            }
            i++;
        }
    }
}

//  查询某个学生
void DataBase::on_showEdit_clicked()
{
    QString name = ui->nameEdit_2->text();
    if(name.isEmpty()){
        QMessageBox::information(this, "提示", "请输入要查询的学生姓名");
        return;
    }
    ui->tableWidget->clear();
    QSqlQuery query;
    QString sql = QString("select * from stu_info_table where name='%1';").arg(name);
    if(query.exec(sql)){
        int i=0;
        while (query.next()) {
            //            qDebug() << i;
            for(int j=0; j<query.record().count(); j++){
                ui->tableWidget->setItem(i, j, new QTableWidgetItem(query.value(j+1).toString()));
            }
            i++;
        }
    }else{
        QMessageBox::warning(this, "警告", "查询错误");
        return;
    }
}

void DataBase::on_deleteBtn_clicked()
{
    QString name = ui->nameEdit_2->text();
    if(name.isEmpty()){
        QMessageBox::information(this, "提示", "请输入要删除的学生姓名");
        return ;
    }
    QString sql = QString("delete from stu_info_table where name='%1';").arg(name);
    QSqlQuery query;
    if(query.exec(sql)){
        QMessageBox::information(this, "提示", "删除成功");
        DataBase::on_showAllEdit_clicked();
    }else{
        QMessageBox::warning(this, "警告", "删除失败");
        DataBase::on_showAllEdit_clicked();
    }
    return ;
}

void DataBase::on_deleteAllBtn_clicked()
{
    QString sql = QString("delete from stu_info_table");
    QSqlQuery query;
    if(query.exec(sql)){
        QMessageBox::information(this, "提示", "删除成功");
        DataBase::on_showAllEdit_clicked();
    }else{
        QMessageBox::warning(this, "警告", "删除失败");
        DataBase::on_showAllEdit_clicked();
    }
    ui->tableWidget->clear();
    return ;
}

120.3.4 main.cpp

#include "database.h"

#include <QApplication>

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

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

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

相关文章

Unity读取写入Excel

1.在Plugins中放入dll&#xff0c;118开头的dll在Unity安装目录下&#xff08;C:\Program Files\Unity\Editor\Data\Mono\lib\mono\unity&#xff09; 2.写Excel public void WriteExcel(){//文件地址FileInfo newFile new FileInfo(Application.dataPath "/test.xlsx…

v-on 可以监听多个方法吗?

目录 ​编辑 前言&#xff1a;Vue 3 中的 v-on 指令 详解&#xff1a;v-on 指令的基本概念 用法&#xff1a;v-on 指令监听多个方法 解析&#xff1a;v-on 指令的优势和局限性 优势 局限性 **v-on 指令的最佳实践** - **适度监听**&#xff1a; - **方法抽离**&#x…

【并发编程】多线程读写同一变量的并发问题(并发编程启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

《动手学深度学习 Pytorch版》 10.1 注意力提示

10.1.1 生物学中的注意力提示 “美国心理学之父” 威廉詹姆斯提出的双组件&#xff08;two-component&#xff09;框架&#xff1a; 非自主性提示&#xff1a;基于环境中物体的突出性和易见性 自主性提示&#xff1a;受到了认知和意识的控制 10.1.2 查询、键和值 注意力机制…

一款功能强大的音乐曲谱软件Guitar Pro 8 .1.1for Mac 中文破解版

Guitar Pro 8 .1.1for Mac 中文破解版是一款功能强大的音乐曲谱软件&#xff0c;非常适合学习如何玩&#xff0c;改进技巧&#xff0c;重现喜爱的歌曲或陪伴自己。可以帮助我们进行吉他的学习、绘谱与创作&#xff0c;它包含了几乎所有的吉他现有指法及音色&#xff0c;在做弹拨…

C语言求 3*3 矩阵对角线之和

完整代码&#xff1a; // 求 3*3 矩阵对角线之和 #include<stdio.h>int main() {int n3;int arr[3][3];// 输入矩阵printf("请输入矩阵的元素:\n");for (int i 0; i < n; i){for (int j 0; j < n; j){scanf("%d", &arr[i][j]);}}int su…

OpenCV官方教程中文版 —— 图像金字塔

OpenCV官方教程中文版 —— 图像金字塔 前言一、原理二、使用金字塔进行图像融合 前言 • 学习图像金字塔 • 使用图像创建一个新水果&#xff1a;“橘子苹果” • 将要学习的函数有&#xff1a;cv2.pyrUp()&#xff0c;cv2.pyrDown()。 一、原理 一般情况下&#xff0c;我…

【Ubuntu18.04】激光雷达与相机联合标定(Livox+HIKROBOT)(一)

LivoxHIKROBOT联合标定 引言1 海康机器人HIKROBOT SDK二次开发并封装ROS1.1 介绍1.2 安装MVS SDK1.3 封装ROS packge 2 览沃Livox SDK二次开发并封装ROS3 相机雷达联合标定3.1 环境配置3.1.1 安装依赖——PCL 安装3.1.2 安装依赖——Eigen 安装3.1.3 安装依赖——Ceres-solver …

计算机视觉-数学基础*变换域表示

被研究最多的图像&#xff08;或任何序列数据&#xff09;变换域表示是通过傅 里叶分析 。所谓的傅里叶表示就是使用 正弦函数的线性组合来表示信号。对于一个给定的图像I(n1,n2) &#xff0c;可以用如下方式分解它&#xff08;即逆傅里叶变换&#xff09;&#xff1a; 其中&a…

浅谈APP自动化测试工具主要优势

移动应用程序已经成为我们日常生活和商业活动的重要组成部分。随着用户对应用性能、稳定性和用户体验的需求不断增加&#xff0c;开发人员不得不寻找方法来确保他们的应用在各种情况下都能正常运行。这就引入了自动化测试工具&#xff0c;这些工具通过自动执行测试用例来检查应…

openGauss学习笔记-107 openGauss 数据库管理-管理用户及权限-三权分立

文章目录 openGauss学习笔记-107 openGauss 数据库管理-管理用户及权限-三权分立107.1 默认的用户权限107.2 三权分立较非三权分立权限变化说明 openGauss学习笔记-107 openGauss 数据库管理-管理用户及权限-三权分立 默认权限机制和管理员两节的描述基于的是openGauss创建之初…

2023-10-23 LeetCode每日一题(老人的数目)

2023-10-23每日一题 一、题目编号 2678. 老人的数目二、题目链接 点击跳转到题目位置 三、题目描述 给你一个下标从 0 开始的字符串 details 。details 中每个元素都是一位乘客的信息&#xff0c;信息用长度为 15 的字符串表示&#xff0c;表示方式如下&#xff1a; 前十…

Java,回形数

回形数基本思路&#xff1a; 用不同的四个分支分别表示向右向下向左向上&#xff0c;假如i表示数组的行数&#xff0c;j表示数组的列数&#xff0c;向右向左就是控制j的加减&#xff0c;向上向下就是控制i的加减。 class exercise {public static void main(String[] args){//回…

Jenkins 重新定义 pom 内容,打包

文章目录 源码管理构建 源码管理 添加仓库地址&#xff0c;拉取凭证&#xff0c;选择需要的分支 构建 勾选 构建环境 下删除原始 build 配置&#xff0c;防止文件错误 Pre Steps 构建前处理 pom.xml &#xff0c;例如我是需要删除该模块的所有子模块配置&#xff0c;我这里…

超声波清洗机频率如何选择?高频和低频有什么区别

超声波清洗原理就是在清洗液中产生“空化效应”&#xff0c;即清洗液产生拉伸和压缩现象&#xff0c;清洗液拉伸时会产生大量微小气泡&#xff0c;清洗液压缩时气泡会被压碎破裂。这些气泡产生和破裂的局部压强可达到上千个大气压的冲击力&#xff0c;这种极强大的压力足以使得…

切片不够技术来凑

概述 随着数据经度的提升&#xff0c;18级的切片有些场景已经不够用了&#xff0c;但是大部分在线的栅格切片最大级别还是18级&#xff0c;如果地图继续放大&#xff0c;有的框架&#xff08;leaflet会&#xff0c;openlayers和mapboxGL不会&#xff09;会存在没有底图的情况。…

Spring源码篇(十二)事件机制

文章目录 前言应用示例第一种&#xff1a;EventListener第二种&#xff1a;实现ApplicationListener第三种&#xff1a;TransactionalEventListener补充&#xff1a;筛选条件 源码初始化事件器注册监听器添加监听器添加1&#xff1a;应用启动前的监听器SpringApplication实例化…

Hadoop3教程(三十五):(生产调优篇)HDFS小文件优化与MR集群简单压测

文章目录 &#xff08;168&#xff09;HDFS小文件优化方法&#xff08;169&#xff09;MapReduce集群压测参考文献 &#xff08;168&#xff09;HDFS小文件优化方法 小文件的弊端&#xff0c;之前也讲过&#xff0c;一是大量占用NameNode的空间&#xff0c;二是会使得寻址速度…

【Linux】进程优先级|进程并发概念|在vim中批量化注释

文章目录 前言tips——如何在vim中批量化注释进程更深度理解一、什么是进程优先级二、 为什么要有优先级三、Linux怎么设置优先级查看进程优先级的命令PRI and NI用top命令更改已存在进程的nice&#xff1a; 如何根据优先级开展调度呢&#xff1f;五、其他概念并发&#xff08;…

使用Github.io创建自己的博客

文章目录 1.最终效果2.操作步骤2.1 前置操作2.2 按照自己需求修改内容2.2.1 基本修改2.2.2 额外添加知乎等社交网站链接 2.3 首页修改2.4 查看发布状态2.5 奇怪的错误(头像显示错误)2.6 本地调试2.7 后续修改 3. 项目设置为私密&#xff08;要付费升级账号才行❌&#xff09;3.…