<Qt> 初识Qt

目录

一、项目文件解析

widget.h

main.cpp

widget.cpp

widget.ui

.pro文件

二、QT 实现Hello World程序

(一)按钮控件

1. 纯代码

2. 图形化

(二)标签控件

1. 纯代码

2. 图形化

三、内存泄漏问题

四、qdebug()的使用

五、Qt 中的对象树

六、使用其他方式创建一个 Hello World 程序(编辑框和按钮方式)

(一)编辑框

1. 图形化

2. 纯代码

(二)按钮

1. 图形化

2. 纯代码

七、关于 Qt 中的命名规范 / 快捷键 / 查询文档

八、Qt 窗口坐标系


一、项目文件解析

创建一个新qt项目后,会生成几个默认对应的文件

widget.h

注意:这里的文件名在创建时默认为widget.h,我这里只是重命名了这个文件名

  • 在QT中如果某个类想要使用信号与槽signal slot的机制,那么必须引入Q_OBJECT这个宏,并且要将这个宏写在这个类的前面,这时候QT编译器就允许这个类自定义信号和槽函数;
  • Ui::Widget *ui这个指针是在 namespace Ui 里的 Widget 类里面定义的,并且这个ui 指针实际上是指向可视化设计的界面的,后面要访问这个可视化界面上的控件都是通过这个指针去访问的。

main.cpp

使用QT创建一个任意一个工程过后,main.cpp文件中都会自动生成以下代码:

  1. Qt系统提供的标准类名声明头⽂件没有 .h 后缀。
  2. Qt⼀个类对应⼀个头⽂件,类名就是头⽂件名。
  3. QApplication 为应⽤程序类;QApplication a;(a为应⽤程序对象,有且仅有⼀个。)
    3.1 QApplication管理图形⽤⼾界⾯应⽤程序的控制流和主要设置。
    3.2 QApplication是Qt的整个后台管理的命脉。它包含主事件循环,在其中来⾃窗⼝系统和其它资源的所有事件处理和调度。它也处理应⽤程序的初始化和结束,并且提供对话管理。
    3.3 对于任何⼀个使⽤Qt的图形⽤⼾界⾯应⽤程序,都正好存在⼀个QApplication对象,⽽不论这个应⽤程序在同⼀时间内是不是有0、1、2或更多个窗⼝。
  4. Widget w:实例化窗口对象。
  5. w.show():显示窗口对象,与之对应的 w.close() 隐藏窗口对象。
  6. a.exec():程序进⼊消息循环,等待对⽤⼾输⼊进⾏响应。这⾥main()把控制权转交给Qt,Qt完成事件处理⼯作,当应⽤程序退出的时候exec()的值就会返回。在 exec()中,Qt 接受并处理⽤⼾和系统的事件并且把它们传递给适当的窗⼝部件。

widget.cpp

注意:这里的文件名在创建时默认为widget.cpp,我这里只是重命名了这个文件名

widget.ui

widget.ui 是窗体界⾯定义⽂件,是⼀个XML⽂件,定义了窗⼝上的所有组件的属性设置、布局,及其信号与槽函数的关联等。⽤UI设计器可视化设计的界⾯都由Qt⾃动解析,并以XML⽂件的形式保存下来。在设计界⾯时,只需在UI设计器⾥进⾏可视化设计即可,⽽不⽤管widget.ui⽂件是怎么⽣成的。

.pro文件

⼯程新建好之后,在⼯程⽬录列表中有⼀个后缀为".pro"的⽂件,".pro"⽂件就是⼯程⽂件(project)
,它是 qmake ⾃动⽣成的⽤于⽣产 makefile 的配置⽂件。如图所示:

双击进⼊该⽂件,该⽂件的核⼼内容如下:

  • QT +=coregui:Qt 包含的模块
  • greaterThan(QT_MAJOR_VERSION, 4): QT+=widgets:⼤于Qt4版本才包含widget模块
  • TARGET=QtFirst:应⽤程序名⽣成的 .exe 程序名称
  • TEMPLATE=app:模板类型,应⽤程序模板
  • SOURCES+=main.cpp\ :源⽂件
  • widget.cpp:源⽂件
  • HEADERS+=widget.h:头⽂件

“.pro” ⽂件的写法如下:

  1. 注释:从"#"开始,到这⼀⾏结束。
  2. QT += coregui:Qt包含的模块 Qt5包含的模块如下图所⽰:

  3. greaterThan(QT_MAJOR_VERSION,4):QT+=widgets 这条语句的含义是,如果QT_MAJOR_VERSION ⼤于4也就是当前使⽤的Qt5及更⾼版本)需要增加widgets 模块。如果项⽬仅需⽀持Qt5,也可以直接添加"QT+=widgets"⼀句。不过为了保持代码兼容,最好还是按照QtCreator⽣成的语句编写。
  4. 指定⽣成的应⽤程序名:TARGET=QtDemo
  5. TEMPLATE=app:模板。告诉qmake为这个应⽤程序⽣成哪种makefile。下⾯是可供选择的模板:
    5.1 app:建⽴⼀个应⽤程序的makefile。这是默认值,所以如果模板没有被指定,这个将被使⽤。
    5.2 lib:建⽴⼀个库的makefile。
    5.3 vcapp:建⽴⼀个应⽤程序的VisualStudio项⽬⽂件。
    5.4 vclib: 建⽴⼀个库的VisualStudio项⽬⽂件。
    5.5 subdirs:这是⼀个特殊的模板,它可以创建⼀个能够进⼊特定⽬录的makefile并且为它调⽤make的makefile。
  6. ⼯程中包含的源⽂件:SOURCES+=main.cpp/widget.cpp
  7. ⼯程中包含的头⽂件:HEADERS+=widget.h
  8. ⼯程中包含的资源⽂件:RESOURCES+=painter.qrc
  9. ⼯程中包含的"ui"设计⽂件:FORMS+=widget.ui
  10. 配置信息:CONFIG+=c++11(使⽤c++11的特性) CONFIG⽤来告诉qmake关于应⽤程序的配置信息。

二、QT 实现Hello World程序

利用 Qt 创建一个Hello World 程序有两种方式。分别是图形化的方式和纯代码的方式。

(一)按钮控件

1. 纯代码

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QPushButton> // 添加按钮对应的头文件MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
{ui->setupUi(this);// 在这里编写我们的代码// 创建一个按钮控件QPushButton *pushButton = new QPushButton;// 在这个按钮写一个Hello WorldpushButton->setText("Hello World!");// 将这个按钮控件添加在窗口上pushButton->setParent(this);
}MyWidget::~MyWidget()
{delete ui;
}

效果图:

2. 图形化

将按钮控件拖至屏幕上,双击控件,输入 Hello World。效果图如下:

(二)标签控件

1. 纯代码

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QLabel> // 引入标签控件的头文件
#include <QFont>  // 引入字体头文件MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
{ui->setupUi(this);// 创建一个标签控件QLabel *label = new QLabel;// 在这个标签写上Hello Worldlabel->setText("Hello World");// 创建一个字体对象// 将标签上的文字的字体设置成指定格式QFont font("黑体", 64);label->setFont(font);// 将标签这个控件放在窗口上// 并将这个控件移动到像素坐标为(150, 150)的地方label->move(150, 150);label->setParent(this);
}MyWidget::~MyWidget()
{delete ui;
}

效果图如下: 

2. 图形化

效果图如下:

三、内存泄漏问题

在这段代码中,在 new 了一个对象之后,却没有 delete ,会不会造成内存泄漏?
答案是在 Qt 中不会造成内存泄漏。此处通过 new 的方式创建对象,就是为了把该对象的生命周期交给 Qt 的对象树统一管理。代码中的 label 对象会在合适的时机被析构释放,这个合适的时机是指在窗口关闭的时候。

四、qdebug()的使用

qDebug() 的使用效果和 std::cout << std::endl 的效果一样,但是比后者好。
原因是 qDebug() 会自动处理编码方式,在 Qt 中如果用 cout 打印文字日志,可能会出现乱码,因为编码不同。

如在析构函数中加入打印语句:

Widget::~Widget()
{std::cout << "析构函数\n";delete ui;
}

运行后,然后关闭窗口,自动调用析构函数,查看应用程序输出,会看到乱码: 

所以通常采用 qDebug() 的方式去打印日志:

#include <QDebug> // 包含这个头文件Widget::~Widget()
{qDebug() << "析构函数";delete ui;
}

qDebug() 还有一个优点就是可以统一打开和关闭,就能方便程序员调试。
.pro 文件加入这条代码即可:

DEFINES += QT_NO_DEBUG_OUTPUT

需要 清除 然后重新构建才有效

五、Qt 中的对象树

在Qt中创建很多对象的时候会提供一个parent对象指针,这个parent到底是干什么的呢?

QObject是以对象树的形式组织起来的。

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

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

QWidget是能够在屏幕上显示的一切组件的父类

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

对象树机制一定程度上解决了内存问题。

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

若QObject在栈上创建,Qt保持同样的行为。正常情况下也不会发生什么问题:

{QWidget window;QPushButton quit("Quit", &window);
}

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

{QPushButton quit("Quit");QWidget window;quit.setParent(&window);
}

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

自定义MyPushButton类观察释放过程

mypushbutton.h

#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H#include <QPushButton>
#include <QDebug>class MyPushButton : public QPushButton
{Q_OBJECT
public:explicit MyPushButton(QWidget *parent = nullptr);~MyPushButton();//添加MyPushButton类的析构函数signals:};#endif // MYPUSHBUTTON_H

 mypushbutton.cpp

#include "mypushbutton.h"MyPushButton::MyPushButton(QWidget *parent) : QPushButton(parent)
{qDebug() << "我的按钮的构造函数被调用";
}MyPushButton::~MyPushButton()
{qDebug() << "我的按钮的析构函数被调用";
}

widget.h

#include "widget.h"
#include "ui_widget.h"
#include "mypushbutton.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);MyPushButton* button = new MyPushButton(this);//传入this,将其设置到对象树中,当窗口关闭时就会自动调用其析构函数button->setText("我的按钮");
}Widget::~Widget()
{qDebug() << "Widget的析构函数被调用";delete ui;
}

运行结果如下:

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

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

总结:

Qt的对象树机制虽然在⼀定程度上解决了内存问题,但是也引入了⼀些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以最好从开始就养成良好习惯。

注意:在Qt中,尽量在构造的时候就指定parent对象,并且大胆在堆上创建

六、使用其他方式创建一个 Hello World 程序(编辑框和按钮方式)

除了上面使用 label 标签的形式,还可以采用编辑框按钮的方式。同样分为图形化界面方式和纯代码方式完成。

(一)编辑框

1. 图形化

如下图所示,编辑框分为单行编辑框和多行编辑框,这里选择单行编辑框即可。拖动到编辑界面,然后输入文字:

运行查看效果。编辑框的内容运行后可以自己修改: 

2. 纯代码

纯代码方式和之前介绍过的大同小异。首先需要创建一个 QLineEdit 对象,然后设置文字:

效果图如下: 

(二)按钮

1. 图形化

在 ui 界面找到 Push Button 然后拖动到图形化编辑界面输入文字:

运行查看效果,可以点击这个按钮:

这个按钮是可以点击的,但是点击了没有反应。如果想要点击了有反应,需要用到信号和槽机制,后面会讲。这里先简单说一下,本质就是给按钮的点击操作,关联一个处理函数,当用户点击的时候,就会执行这个处理函数。

这里需要用到 connect 函数:

  • 第一个参数需要访问到 ui 文件中创建的控件,表示哪个控件发出的信号,在 Qt Designer 中创建一个控件的时候,此时就会给这个控件分配一个 objectName 属性,这个属性的值,要求在界面中是唯一的。qmake 在预处理.ui文件的时候,就会根据这里的 objectname 生成对应的C++代码。这个值可以在ui界面手动修改:

  • 第二个参数表示发出了个什么信号,这里 clicked 表示点击,当用户点击按钮时就会发出该信号。
  • 第三个参数表示谁来处理该信号,将第二个参数的信号能够关联到对应的槽函数上,在此之前需要确认关联到哪个对象的槽函数,填写 this ,关联到 widget 对象的槽函数。
  • 第四个参数传一个具体的处理函数,这个函数需要我们自己去写处理方式。我们的处理方式是:点击一次按钮,文本在 Hello World!和Hello Qt! 之间来回切换。代码如下:
void Widget::handClick()
{if(ui->pushButton->text() == QString("Hello World!")){ui->pushButton->setText("Hello Qt!");}else{ui->pushButton->setText("Hello World!");}
}

记得先在头文件声明该函数

声明以后,快速生成函数定义快捷键:alt + 回车 会自动在 .cpp 文件帮我们处理好一些代码 

运行效果如下:

 点击一次按钮:

文本内容改变了,再点击一次 

点击一次按钮就会在两个文本内容之间来回切换,成功实现我们想要的功能。 

2. 纯代码

纯代码方式实现,首先需要new 一个 button 对象,为了能让其他成员函数能够访问该变量,需要将该对象定义为成员变量:

具体代码如下,功能如上面所述:

运行查看效果:

点击后: 

再次点击: 

实际开发以纯代码为主还是图形化界面为主?

这两者都很重要,难分主次。

  • 如果当前程序界面,界面内容是比较固定的,此时就会以图形化界面的方式来构造界面。
  • 如果当前界面,经常要动态变化,此时就会以代码的方式来构造界面。
  • 这两种方式,哪种方便就用哪种,而且这两种方式可以配合使用。

七、关于 Qt 中的命名规范 / 快捷键 / 查询文档

命名规范

  • 类名:首字母大写,单词和单词之间首字母大写,也就是大驼峰命名法。如:QPushButton
  • 函数名:首字母小写,单词和单词之间首字母大写,也就是小驼峰命名法。如:setText

快捷键

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

使用帮助文档
打开文档一共有三种方法

  • 光标放到要查询的类名/方法名上,直接按F1
  • Qt Creator左侧边栏中直接用鼠标单击"帮助"按钮。
  • 找到Qt Creator的安装路径,在"bin"文件夹下找到assistant.exe,双击打开。

八、Qt 窗口坐标系

坐标体系:以左上角为原点(0,0),X向右增加,Y向下增加

对于嵌套窗口,其坐标是相对于父窗口来说的:

比如图中 QPushButton 的父窗口就是 QWidget,原点就是QWidget 左上角。QWidget没有父元素,相当于父元素就是整个显示器,原点就是显示器左上角。

上面我们提到过,以纯代码的方式创建一个这样一个程序,按钮会默认在左上角,也就是原点(0,0)处,这里的位置是可以通过设置改变的:

#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮"); // 如果不设置,默认位置就是父窗口的(0,0)坐标button->move(200, 300);
}

如果想要给控件设置位置就要使用 move() 方法: 

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮"); // 如果不设置,默认位置就是父窗口的(0,0)坐标button->move(200, 300);
}

 这里move的第一个参数就是X坐标,第二个参数就是Y坐标,而参数的单位就是像素,显示器本身就是一大堆可以发光的亮点:

电脑屏幕设置中的分辨率:在水平方向上有1920个像素,垂直方向上有1080个像素,显示器像素越大,画面越好。

把这个按钮相对于原点水平移动200像素,垂直移动300像素,效果如下:

还可以设置窗口弹出时的位置,设置为弹出为显示器的原点处:

查看效果,在窗口弹出后位置在显示器左上角: 

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

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

相关文章

php基础: 三角形

包含&#xff1a;左三角、左上三角、右三角、右上三角、等腰三角、倒等腰三角。注意空格的数量&#xff0c;因为*号后面加了空格 /*** * 左三角形* param $n* return void*/ function triangleLeft($n){echo <pre>;for ($i 1; $i < $n; $i) {for ($j 1; $j < $i…

el-table的selection多选表格改为单选

需求场景: 选择表格数据时&#xff0c;需要控制单条数据的操作按钮是否禁用。 效果图: html代码: <div><el-tableref"multipleTable":data"tableData"tooltip-effect"dark"style"width: 100%"selection-change"handl…

Android:创建自定义View

点击查看创建自定义view官网文档 一、简介 设计良好的自定义视图与任何其他精心设计的类一样。它通过一个简单的接口封装一组特定的功能&#xff0c;高效使用 CPU 和内存&#xff0c;诸如此类。除了是一个精心设计的类之外&#xff0c;自定义视图还必须执行以下操作&#xff1…

LinuxShell编程2——shell搭建Discuzz论坛网站

目录 一、环境准备 ①准备一台虚拟机 ②初始化虚拟机 1、关闭防火墙 2、关闭selinux 3、配置yum源 4、修改主机名 二、搭建LAMP环境 ①安装httpd(阿帕奇apache&#xff09;服务器 查看是否安装过httpd 启动httpd 设置开机启动 查看状态 安装网络工具 测试 ②安装…

STM32 - FLASH 笔记

STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&#xff0c;通过闪存存储器接口&#xff08;外设&#xff09;可以对程序存储器和选项字节进行擦除和编程 读写FLASH的用途&#xff1a; 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 通过在程序…

java设计模式(十五)命令模式(Command Pattern)

1、模式介绍&#xff1a; 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;其主要目的是将请求封装成一个对象&#xff0c;从而允许使用不同的请求、队列或者日志来参数化其他对象。这种模式使得命令的请求者和实现者解耦。 2、应用场景&…

【ARM】MDK-服务器与客户端不同网段内出现卡顿问题

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 记录不同网段之间的请求发送情况以及MDK网络版license文件内设置的影响。 2、 问题场景 客户使用很久的MDK网络版&#xff0c;在获取授权时都会出现4-7秒的卡顿&#xff0c;无法对keil进行任何操作&#xff0c;彻底…

ChatGPT Mac App 发布!

2024 年 6 月&#xff0c;OpenAI 的大语言模型 ChatGPT 的 Mac 客户端与 ChatGPT-4o 一起发布了。ChatGPT Mac 户端可以让用户直接在 Mac 电脑上使用 ChatGPT 进行对话。它提供了一个简单易用的用户界面&#xff0c;用户可以在其中输入文本或语音指令&#xff0c;并接收模型生成…

【Python百日进阶-Web开发-音频】Day702 - librosa安装及模块一览表

文章目录 一、Librosa简介与安装1.1 Librosa是什么1.2 Librosa官网 二、Librosa安装2.1 安装Librosa 三、安装ffmpeg3.1 ffmpeg官网下载3.2 ffmpeg安装3.2.1 解压3.2.2 添加环境变量3.2.3 测试ffmpeg是否安装成功 四、Librosa 库模块一览4.1 库函数结构4.2 Audio processing&am…

C++相关概念和易错语法(21)(虚函数、协变、析构函数的重写)

多态的核心是虚函数&#xff0c;本文从虚函数出发&#xff0c;根据原理慢慢推进得到结论&#xff0c;进而理解多态 1.虚函数 先看一下下面的代码&#xff0c;想想什么导致了这个结果 #include <iostream> using namespace std;class A { public:virtual void test(){co…

元服务体验-服务发现

服务发现&#xff0c;无论线上或线下的方式都可以发现元服务。 线上&#xff1a;基于用户意图。从精准意图的搜索、用户事件触发的推荐到主动探索等场景。用户可以在设备的负一屏、全局搜索、应用市场、桌面等场景发现元服务。 线下&#xff1a;用户在 HarmonyOS Connect标签…

华为HCIP Datacom H12-821 卷39

1.填空题 请2001 :0DB8:0000:C030:0000: 000: 09A0:CDEF地址进行压缩。() (若答案中存在字母&#xff0c;请采用大写格式) 参考答案&#xff1a;2001 :DB8:0:C030: :9A0:CDEF 解析&#xff1a; IPv6地址的表示方法 IPv6地址总长度为128比特&#xff0c;通常分为8组&#xff0c…

LeetCode 20.有效的括号 C写法

LeetCode 20.有效的括号 C写法 思路&#x1f9d0;&#xff1a; ​ 这题最优思路是用栈来进行匹配&#xff0c;如果是左括号就入栈&#xff0c;如果是右括号那么左括号就出栈去匹配&#xff0c;匹配成功就继续入栈或者出栈&#xff0c;匹配失败则字符串无效。不过C语言没有STL…

win10远程ubuntu服务器桌面且显示图像窗口工具及配置说明

仅需一个MobaXterm_Personal工具就可以实现 网上的教程比较多&#xff0c;实现起来比较复杂&#xff0c;这个是经过自己的钻研找到的方法&#xff08;请勿转载和抄袭&#xff09; 报错&#xff1a;cannot connect to X server :0.0 操作1&#xff1a;export DISPLAY自己windo…

SSE、Webworker 、webSocket、Http、Socket 服务器推送技术

Http协议 受浏览器的同源策略限制 HTTP 协议是一种无状态的、无连接&#xff08;短暂连接&#xff0c;客户端发送请求&#xff0c;服务器响应后即断开连接&#xff09;的、单向的应用层协议。 它采用了请求/响应模型。通信请求只能由客户端发起&#xff0c;服务端对请求做出应…

IP地址与物理地址:网络通信的基础详解

在学习网络通信时&#xff0c;理解IP地址与物理地址&#xff08;也称为硬件地址&#xff09;的区别至关重要。这篇文章将为你解答这些基本概念&#xff0c;并帮助你更好地掌握网络通信的基础。 什么是IP地址和物理地址&#xff1f; IP地址是网络层的逻辑地址&#xff0c;用于标…

leetcode算法题(反转链表)

思路1&#xff1a; 创建新的链表&#xff0c;遍历原链表&#xff0c;将原链表的节点进行头插到新链表中。 struct ListNode* reverseList(struct ListNode* head) {struct ListNode* next NULL;struct ListNode* new_head NULL;if (head NULL ||head->next NULL) // 空…

Python 获取今天(当天)、昨天(前一天)、前天(昨天的前一天)的开始时间、结束时间

描述&#xff1a;我这里是封装成DatetimeHelper工具类来调用 1.今天(当天)开始时间、结束时间 from datetime import datetime, timedeltaclass DatetimeHelper:# 获取今天(当天)的开始时间、结束时间(datetime类型)staticmethoddef getTodayStartEnd():# 获取当前的日期now …

在 electron+vite+vue3+express 项目中使用better-sqlite3

文章目录 一、安装 electron-rebuild 和 better-sqlite3二、使用 electron-rebuild 重建 Node.js 模块三、better-sqlite3 的基本使用四、打包五、参考资料 一、安装 electron-rebuild 和 better-sqlite3 yarn add -D electron-rebuild yarn add better-sqlite3Electron 内置的…

解决onlyoffice无法重命名的问题

当前的问题&#xff1a; 返回的是 error&#xff1a;1&#xff0c;根据官方文档的解释&#xff0c;这个是文档的key是错误的。 参考官方文档&#xff1a;https://api.onlyoffice.com/zh/editors/command 解决思路&#xff1a;看有没有什么事件&#xff0c;能够携带文档的key…