【QT】编写第一个 QT 程序 对象树 Qt 编程事项 内存泄露问题

目录

1. 编写第一个 QT 程序

1.1 使用 标签 实现  

🐇 图形化界面实现

🐇 纯代码形式实现

1.2 使用 按钮 实现 

🐋 图形化界面实现 

🐋 纯代码形式实现

1.3 使用 编辑框 实现

🥝 图形化界面实现

🥝 纯代码形式实现 

2. 认识对象模型(对象树)

2.1 什么是对象树

2.2 验证对象树

3. 解决编码问题

4. Qt 编程注意事项

4.1 Qt 中的命名事项

4.2 Qt Creator 中的快捷键

4.3 使用帮助文档

5. 补充 -- 析构函数执行顺序

6. 小结


1. 编写第一个 QT 程序

1.1 使用 标签 实现  

🐇 图形化界面实现

①  创建好一个项目后,我们可以点击 widget.ui 进入图形化界面设计,可以直接通过拖拽的方式进行添加

  拖拽 "标签" 至 UI 设计界面中,并双击修改标签内容

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

🐇 纯代码形式实现

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

#include "widget.h" // 创建生成时的文件
#include "ui_widget.h"
#include <QLabel>  // 包含标签的头文件Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this); // 将form file生成的界面和我们当前的widget进行关联起来// 创建对象的两种方法// QLabel label; // 在栈上创建QLabel* label = new QLabel(this); // 在堆上创建,推荐这种方法,还要传递 一个 this,给当前这个 lable 对象指定 父对象// 1. 设置标签内容label->setText((QString)("显式 Hello world"));label->setText("隐式 Hello World"); // QString 也提供了 C 风格字符串作为参数的构造函数来不显示构造 QString// 注意:由于QString 对应的头文件,已经被很多 Qt 内置的其他类给间接包含了.因此一般不需要显式包含 QString 头文件// 这里虽然有两次 setText,但是下面内容会覆盖上面内容// 2. 设置窗口大小setFixedSize(500, 400);// 3. 设置字体大小QFont font("楷体", 16);label->setFont(font);// 4. 设置标签内容显式位置label->move(200, 150);// 5. 设计标签字体颜色label->setStyleSheet("color:blue");
}Widget::~Widget()
{delete ui;
}

 

void setText(const QString &);

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

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

1.2 使用 按钮 实现 

🐋 图形化界面实现 

双击:"widget.ui" 文件

② 拖拽控件至 ui 界面窗口并修改内容

  • 虽然那里有好几个按钮,但是我们这里用 Push Button(普通按钮)

② 构建并运行,效果如下所示

  • 这里的按钮的确可以点击,但是却没有任何反应,这个就设计到我们后面学的信号槽知识,后面会说的

  • 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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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 类的成员变量

实际开发中,是通过代码的方式构造界面为主,还是通过图形化界面的方式构造界面为主??

  • 这两种都很主要,难分主次!!
  • 如果你当前程序界面,界面内容是比较固定的,此时就会以 图形化 的方式来构造界面
  • 但是如果你的程序界面,经常要动态变化,此时就会以 代码 的方式来构造界面
  • 反正这两种方式哪种方便用哪个,也可以配合来使用


1.3 使用 编辑框 实现

  • 单行编辑框: QLineEdit
  • 多行编辑框: QTextEdit
🥝 图形化界面实现

 

  • 当然输出的文本框,我们也可以在输出里面进行修改啥的,但是不会影响代码里面的文本数据

🥝 纯代码形式实现 

内存泄露 

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

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

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

2. 认识对象模型(对象树)

2.1 什么是对象树

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

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

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

  • Qwidget 是能够在屏幕上显示的一切组件的父类。
    • Qwidget 继承自 QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的-个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该(-起被删除。事实就是如此,因为这些都是对话框的子组件。
    • 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。

Qt 引入对象树的概念,在一定程度上解决了内存问题。

  • 当一个 QObject 对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
  • 任何对象树中的 QObject 对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent的children() 列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有 QObject 会被delete 两次,这是由析构顺序决定的。

如果 QObject 在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下面的代码片段:

{QWidget window;QLabel label("hello", &window); // 指定父类是widow
}

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

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

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

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

比如:

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

Qt 对象树如下:

2.2 验证对象树

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

① 选中工程名,鼠标右键------->"add new..."(或 "添加新文件" )

结果图如下: 

  • 上面 Qt Creator 是帮我们生成了一些代码,但是没完全生成,头文件没有给我们主动包含,上面的头文件也是我自己手动包含的 
  • 此时我们可以按F4来进行 .h文件.cpp文件 来回切换

此时我们的 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

3. 解决编码问题

我们用文本文件打开 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% 甚至更小要想调试,无法使用调试器了
  • 使用 日志  就可以很好的解决这种问题
  • 无论是哪种方式本质上都是观察程序执行的中间过程和中间结果~

4. Qt 编程注意事项

4.1 Qt 中的命名事项

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

4.2 Qt Creator 中的快捷键

  • 注释:ctrl+/
  • 运行:ctrl+R
  • 编译:ctrl+B
  • 字体缩放:ctrl+鼠标滑轮
  • 查找:ctrl+F
  • 整行移动:ctrl+shift+↑/↓
  • 帮助文档:F1
  • 自动对齐:ctrl+i
  • 同名之间的.h和.cpp 的切换:F4
  • 生成函数声明的对应定义:alt+enter

4.3 使用帮助文档

打开帮助文档有三种方式,实际编程中使用哪种都可以

  1. 光标放到要查询的类名/方法名上,直接按 F1
  2. Qt Creator 左侧边栏中直接用鼠标单击"帮助"按钮

点击 "帮助" 之后,出现如下图:

3、找到 Qt Creator 的安装路径,在 "bin" 文件夹下找到 assistant.exe,双击打开

使用示例

  1. 新建项目,在新建的项目中使用 Qt 中的"QpushButton" 控件。
  2. 打开帮助手册,在"索引"里面输入"QpushButton":

注意:一定不要使用中文文档!!!

  • 阅读英文文档是每个程序员必备的专业技能,必须要练,不能退缩
  • Qt的文档从通俗易懂的角度来说,是技术类文档中非常出类拔萃的,只要大家稍微有点耐心,基本都能读懂个八九不十

5. 补充 -- 析构函数执行顺序

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;
}

运行结果如下: 

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

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


6. 小结

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

【*★,°*:.☆( ̄▽ ̄)/$:*.°★* 】那么  本篇到此就结束啦,如果我的这篇博客可以给你提供有益的参考和启示,可以三连支持一下 !!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pytest入门一:用例的执行范围

从一个或多个目录开始查找&#xff0c;可以在命令行指定文件名或目录名。如果未指定&#xff0c;则使用当前目录。 测试文件以 test_ 开头或以 _test 结尾 测试类以 Test 开头 &#xff0c;并且不能带有 init 方法 测试函数以 test_ 开头 断言使用基本的 assert 即可 所有的…

科研绘图系列:R语言绘制热图和散点图以及箱线图(pheatmap, scatterplot boxplot)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载图1图2图3系统信息参考介绍 R语言绘制热图和散点图以及箱线图(pheatmap, scatterplot & boxplot) 加载R包 library(magrittr) library(dplyr) library(ve…

ArcGIS MultiPatch数据转换Obj数据

文章目录 ArcGIS MultiPatch数据转换Obj数据1 效果2 技术路线2.1 Multipatch To Collada2.2 Collada To Obj3 代码实现4 附录4.1 环境4.2 一些坑ArcGIS MultiPatch数据转换Obj数据 1 效果 2 技术路线 MultiPatch --MultipatchToCollada–> Collada --Assimp–> Obj 2.…

(5)4T刷题-逻辑代数基础

&#xff08;1&#xff09;逻辑函数的常用表示方法有&#xff1a;真值表、逻辑图、卡诺图、函数表达式 逻辑函数的表达方法中具有唯一性的是&#xff1a;真值表和卡诺图 &#xff08;2&#xff09;异或运算&#xff08;题干意思不明确&#xff0c;应该是按位异或&#xff09; …

Linux(网络基础和网络标准OSI七层结构)

后面也会持续更新&#xff0c;学到新东西会在其中补充。 建议按顺序食用&#xff0c;欢迎批评或者交流&#xff01; 缺什么东西欢迎评论&#xff01;我都会及时修改的&#xff01; 在这里真的很感谢这位老师的教学视频让迷茫的我找到了很好的学习视频 王晓春老师的个人空间…

向达梦告警日志说声hello

为了调试和跟踪一些业务功能&#xff0c;通常会创建一个日志表&#xff0c;写入每个关键步骤的信息。也可以向达梦数据库的告警日志输出信息&#xff0c;然后通过查看告警日志即可。 在达梦的告警日志中输出一个信息可以这样 SQL> DBMS_SYSTEM.KSDWRT(2,hi dm);

详解 ES6 Reflect

一. 概念 Reflect 是 ES6 中新增的一个内置对象&#xff0c;它提供了一组静态方法&#xff0c;用于操作对象。这些方法与 Object 上的方法具有相同的功能。在这些方法中会调用对应 Object 上的方法&#xff0c;并且返回对应结果。Reflect 的出现主要是为了将一些 Object 对象上…

图像分割数据集海洋水体船只分割数据集labelme格式6123张3类别

数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数)&#xff1a;6123 标注数量(json文件个数)&#xff1a;6123 标注类别数&#xff1a;3 标注类别名称:["water","sea_obstacle",&…

Docker Compose--安装本地maven

原文网址&#xff1a;Docker Compose--安装本地maven-CSDN博客 简介 本文介绍如何使用Docker Compose安装maven。 脚本及配置 路径&#xff1a;/work/env/maven ├── app ├── config │ └── settings.xml ├── docker-compose.yml ├── repository └── t…

EDA - Spring Boot构建基于事件驱动的消息系统

文章目录 概述事件驱动架构的基本概念工程结构Code创建事件和事件处理器创建事件总线创建消息通道和发送逻辑创建事件处理器消息持久化创建消息发送事件配置 Spring Boot 启动类测试消息消费运行项目 概述 在微服务架构和大规模分布式系统中&#xff0c;事件驱动架构&#xff…

数据链路层(Java)(MAC与IP的区别)

以太网协议&#xff1a; "以太⽹" 不是⼀种具体的⽹络, ⽽是⼀种技术标准; 既包含了数据链路层的内容, 也包含了⼀些物理 层的内容. 例如: 规定了⽹络拓扑结构, 访问控制⽅式, 传输速率等; 例如以太⽹中的⽹线必须使⽤双绞线; 传输速率有10M, 100M, 1000M等; 以太…

UNIX数据恢复—UNIX系统常见故障问题和数据恢复方案

UNIX系统常见故障表现&#xff1a; 1、存储结构出错&#xff1b; 2、数据删除&#xff1b; 3、文件系统格式化&#xff1b; 4、其他原因数据丢失。 UNIX系统常见故障解决方案&#xff1a; 1、检测UNIX系统故障涉及的设备是否存在硬件故障&#xff0c;如果存在硬件故障&#xf…

黑马程序员Java项目实战《苍穹外卖》Day12

苍穹外卖-day12 课程内容 工作台Apache POI导出运营数据Excel报表 功能实现&#xff1a;工作台、数据导出 工作台效果图&#xff1a; 数据导出效果图&#xff1a; 在数据统计页面点击数据导出&#xff1a;生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原…

【竞技宝】LOL:JDG官宣yagao离队

北京时间2024年12月13日,在英雄联盟S14全球总决赛结束之后,各大赛区都已经进入了休赛期,目前休赛期也快进入尾声,LPL大部分队伍都开始陆续官宣转会期的动向,其中JDG就在近期正式官宣中单选手yagao离队,而后者大概率将直接选择退役。 近日,JDG战队在官方微博上连续发布阵容变动消…

谷歌浏览器的多账户设置与管理

在数字化时代&#xff0c;我们常常需要在不同的网站和服务上使用多个账户。为了方便管理和保护隐私&#xff0c;谷歌浏览器提供了多账户设置功能。本文将详细介绍如何在Chrome中进行多账户设置与管理&#xff0c;并涵盖一些相关的安全配置和问题解决方法。&#xff08;本文由ht…

科研绘图系列:R语言绘制网络图和密度分布图(network density plot)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载图1图2图3图4图5图6图7图8系统信息参考介绍 R语言绘制网络图和密度分布图(network & density plot) 加载R包 library(magrittr) library(dplyr) library(…

VRRP的知识点总结及实验

1、VRRP VRRP(Virtual Router Redundancy Protocol&#xff0c;虚拟路由器冗余协议)既能够实现网关的备份&#xff0c;又能解决多个网关之间互相冲突的问题&#xff0c;从而提高网络可靠性。 2、VRRP技术概述&#xff1a; 通过把几台路由设备联合组成一台虚拟的“路由设备”…

数智读书笔记系列002 埃隆·马斯克传

书名&#xff1a;埃隆马斯克传 作者&#xff1a;【美】沃尔特艾萨克森 译者&#xff1a;孙思远&#xff1b;刘家琦 出版社&#xff1a;中信出版集团 出版时间&#xff1a;2023年9月 ISBN&#xff1a;9787521758399 这本书是关于特斯拉CEO埃隆马斯克的传记&#xff0c;作者…

2024年12月13日Github流行趋势

项目名称&#xff1a;nexus-xyz / nexus-zkvm 项目维护者&#xff1a;govereau slumber danielmarinq sjudson yoichi-nexus项目介绍&#xff1a;Nexus zkVM 是一个零知识虚拟机。项目star数&#xff1a;1,948项目fork数&#xff1a;343 项目名称&#xff1a;soxoj / maigret …

016 在路由器上配置 DHCP

配置路由器端口IP地址 将路由器的端口地址配置好&#xff0c; 左边的网络地址是 192.168.1.0 右边的网络地址是 192.168.2.0 配置路由器的DHCP服务 打开命令窗口&#xff0c;进入特权模式 进入全局配置 conf t创建一个DHCP地址池&#xff1b; po1 是地址池的名称&#xf…