QT概括-Rainy

news2024/11/15 16:00:21

Qt 虽然经常被当做一个 GUI 库,用来开发图形界面应用程序,但这并不是 Qt 的全部;Qt 除了可以绘制漂亮的界面(包括控件、布局、交互),还包含很多其它功能,比如多线程、访问数据库、图像处理、音频视频处理、网络通信、文件操作等,这些 Qt 都已经内置了。

笔者大部分时间都在使用Qt开发各类应用,qt又恰好弥补了c++语言本身开发业务所需要的的库,纯C++一般做业务开发大量依赖第三方库,导致一个项目可能混杂十几个第三方库,每个库的线程管理机制都不尽相同,库使用文档之类的学习成本也甚高,这很让人困扰给开发者造成许多额外负担。

不过要学习qt也不是一件简单的事情,它的设计虽然已经尽可能易于使用,但不意味着简单qt就简单好学,它仍需要使用者对时间及自身的沉淀。



相关网址

官网在线下载
其它下载
关于大佬Qt总结
QML Book
QML Book中文
QCustomPlot 绘图库

ps:关于QML的学习,可以前往B站,输入QML搜索关键字,便可查阅到相关的大量学习视频。



常用小技巧

换源安装提速

- 中国科学技术大学 http://mirrors.ustc.edu.cn/qtproject/
- 清华大学 https://mirrors.tuna.tsinghua.edu.cn/qt/
- 北京理工大学 http://mirror.bit.edu.cn/qtproject/
- 中国互联网络信息中心 http://mirror.bit.edu.cn/qtproject/
// cmd命令程序可以是qt-unified-windows-x64-4.5.2-online,也可以是MaintenanceTool.exe。
[cmd] --mirror [URL]
// 示例
qt-unified-windows-x64-4.5.2-online.exe --mirror https://mirror.nju.edu.cn/qt  
PS D:\qt> .\MaintenanceTool.exe --mirror https://mirror.nju.edu.cn/qt

延迟调用对象函数

使用QMetaObject::invokeMethod()函数,进行安全调用及延迟调用。

// 记住最后一个参数必须为Qt::QueuedConnection,这样它就会进入对象的线程队列中去,否则它会立即执行的。
QMetaObject::invokeMethod(this, std::bind(&App::onOpen, this), Qt::QueuedConnection);

windows打包

可以在Qt的安装目录中,找到${QT_PATH}\qt\6.2.4\msvc2019_64\binwindeployqt.exe来进行程序打包。这里是对应msvc版本的,如果是mingw则去mingw的路径中去寻找。

D:\qt\6.2.4\msvc2019_64>windeployqt -h
Usage: windeployqt [options] [files]
Qt Deploy Tool 6.2.4

The simplest way to use windeployqt is to add the bin directory of your Qt
installation (e.g. <QT_DIR\bin>) to the PATH variable and then run:
  windeployqt <path-to-app-binary>
If ICU, etc. are not in the bin directory, they need to be in the PATH
variable. If your application uses Qt Quick, run:
  windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>

Options:
  -?, -h, --help              Displays help on commandline options.
  --help-all                  Displays help including Qt specific options.
  -v, --version               Displays version information.
  --dir <directory>           Use directory instead of binary directory.
  --qmake <path>              Use specified qmake instead of qmake from PATH.
  --libdir <path>             Copy libraries to path.
  --plugindir <path>          Copy plugins to path.
  --debug                     Assume debug binaries.
  --release                   Assume release binaries.
  --pdb                       Deploy .pdb files (MSVC).
  --force                     Force updating files.
  --dry-run                   Simulation mode. Behave normally, but do not
                              copy/update any files.
  --no-patchqt                Do not patch the Qt6Core library.
  --ignore-library-errors     Ignore errors when libraries cannot be found.
  --no-plugins                Skip plugin deployment.
  --no-libraries              Skip library deployment.
  --qmldir <directory>        Scan for QML-imports starting from directory.
  --qmlimport <directory>     Add the given path to the QML module search
                              locations.
  --no-quick-import           Skip deployment of Qt Quick imports.
  --translations <languages>  A comma-separated list of languages to deploy
                              (de,fi).
  --no-translations           Skip deployment of translations.
  --no-system-d3d-compiler    Skip deployment of the system D3D compiler.
  --compiler-runtime          Deploy compiler runtime (Desktop only).
  --no-virtualkeyboard        Disable deployment of the Virtual Keyboard.
  --no-compiler-runtime       Do not deploy compiler runtime (Desktop only).
  --json                      Print to stdout in JSON format.
  --no-opengl-sw              Do not deploy the software rasterizer library.
  --list <option>             Print only the names of the files copied.
                              Available options:
                               source:   absolute path of the source files
                               target:   absolute path of the target files
                               relative: paths of the target files, relative
                                         to the target directory
                               mapping:  outputs the source and the relative
                                         target, suitable for use within an
                                         Appx mapping file
  --verbose <level>           Verbose level (0-2).

Qt libraries can be added by passing their name (-xml) or removed by passing
the name prepended by --no- (--no-xml). Available libraries:
bluetooth concurrent core declarative designer designercomponents gamepad gui
qthelp multimedia multimediawidgets multimediaquick network nfc opengl
openglwidgets positioning printsupport qml qmltooling quick quickparticles
quickwidgets script scripttools sensors serialport sql svg svgwidgets test
websockets widgets winextras xml webenginecore webengine webenginewidgets 3dcore
3drenderer 3dquick 3dquickrenderer 3dinput 3danimation 3dextras geoservices
webchannel texttospeech serialbus webview shadertools

Arguments:
  [files]                     Binaries or directory containing the binary.

打开vim按键映射

勾选使用FakeVim
QtVim



核心知识点

对象树

对象树机制并不是继承子父类关系,而是一种对象与对象之间的父节点与字节点的关系。在这个机制下,Qt是不建议你使用栈内存创建对象的(最顶层节点对象除外),所以你创建Qt对象应该以new动态内存分配比较合适。

setParent()方法可以设置对象的上级节点关系,一旦设置了这种节点关系之后,在父节点对象在析构销毁时,则会把子节点进行释放,以此来达到内存泄露的管理问题。

不过对象树有一点限制,就是对象树的整个节点树必须都是同一个线程对象绑定。假设对象A关联线程A,对象B关联线程B它们之间是无法设置父子节点对象树关系的。要设置对象树关系必须满足,对象A关联线程A,对象B也关联线程A,它们之间关联同一个线程才可以设置它们之间的对象树关系。

信号与槽与多线程

其实信号与槽是很优秀机制,它把异步编程做了很巧妙的封装,同时提出了解决多线程解决方案及思路及设计。

在关联信号与槽时,提供了一个参数,这个参数描述了触发信号时如何执行槽函数的策略,大多数时候我们不填最后一个参数代表默认自动。

对笔者而言只关注两个点,触发信号时立即调用还是由别的线程调用?

假设对象A是发射信号方,对象B是槽函数处理方。根据线程关联机制,那么有两种情况,1.对象A和对象B关联到同一个线程。2.对象A和对象B关联在不同的线程。

如果针对的是情况1,同属于一个线程,那么它则会立即调用。只有一个线程并不存在线程的缓存一致性问题及资源互斥问题。

如果针对的是情况2,不同属一个线程,那么它不会立即调用。而是将处理投入到槽函数所在的对象事件列表中,等待时机进行调用。

它这么设计的原因是,是以单线程为模型的多线程设计。因为在单线程中不存在资源互斥的问题,但是有些数据是要在另一个线程处理的,处理后的结果需要给回这个线程。因为它的每个线程都有一个执行事件队列,我们投入设置操作由那个线程去执行,在投入执行队列中肯定是互斥的,但对于使用者来说它可以避免使用大量的锁。只需要专注于单线程开发机制,控制好线程之间的变量与模块边界。

这种机制也不是完全没有问题,比如一些全局的数据操作就是一个很大的问题。比如,警报记录这种全局的消息,你可能给每个设备单独配置了一个线程,那么在查询处理设备时它产生的异常总是需要记录下来的。如果是多个设备那么就会存在,多个设备竞争互斥一个数据结构的问题。那如果我们将这个数据结构单独配置为一个线程,修改操作只能通过信号与槽的形式,是否就解决了这个问题呢?

没有解决,读写往往是同时存在的操作,一个数据结构往往都是要具备读和写的操作,所以这种形式你写也只能通过信号与槽进行查询,然后在将结果通过信号发出,其实不用将信号发出也是可以的,我们可以使用元调用一样将操作推入到该对象的线程去执行。

最简单的方式就是将这个列表使用互斥锁保护起来,这样就不需要通过线程读写的方式来进行了,这在大多数时候都是非常有效且简单的方式。但有些时候我们往往读的操作要数倍于写操作,这个时候需要提升读的并行能力,最简单且有效的方式是读写锁,该互斥提升允许读锁的并行能力,在大量读操作的情况下效率是要优于写操作的。当然笔者在大多数时候也是优先考虑互斥量及读写锁来解决全局数据结构的访问问题。

当然对于读操作写操作更多情况下,仍有一种方案。即们设计一个数据结构作为master独立运行于单独的一个模块内,然后在其它salve模块内放置一份这个数据结构的拷贝。这样在模块有数据进行读操作直接从模块的数据数据进行读操作即可,这样不需要加锁以为该资源为线程资源,提升了读的效率。但写操作则要更加复杂一些,写操作只能将操作发送到master模块中进行修改,修改完毕要发送修到所有salve模块告诉他们那个数据已经进行修改了,让他们的数据结构进行同步操作放置数据出现不一致的情况。

线程对象绑定

继承QObject对象之后,可以使用moveThread()将对象转移到另一个线程中去。由于父节点于子节点必须同属关联同一个线程,如果转移节点的线程拥有父节点,那么需要设置setParent(nullptr)脱离父节点才可以转移,并且其节点下面的所有子节点也会一并转移到此线程进行关联。在创建对象时,会将所在的线程进行关联为线程对象,例如在主线程创建的对象默认就关联主线程。

线程事件循环

QThread对象中,如果执行start()函数,它默认执行的run()函数实现是调用exec()进行事件循环阻塞。这个事件阻塞,会等待事件进行执行调用也就是信号与槽的基础。如果你实现的是自定义QThread如果要关联其它对象那么必须要执行exec(),否则它无法进行关联的槽函数调用。

元属性系统

Qt的元属性系统非常复杂,相关的有Q_PROPERTY设置的动态属性,还要Q_INVOKABLE所设置的元属性方法。其中Q_INVOKABLE所设置的方法能被QML直接调用,Q_PORPERTY属性也是一样的。这是一个基于反射的信息系统,会调用相关连绑定的一些函数。除了这两个常用的外,还有许多元属性的宏,它是由moc生成的部分代码。比如Q_ENUM。包括信号槽传参也是一样要对应的类型进行元注册之后才可以使用。
当然Qt的元系统没有那么简单,不过也是依赖moc生成文件,里面涉及到的东西复杂且多。笔者在这里也不乱说什么。



Qt插件系统

Qt基本插件

所谓的插件就是动态库的一种延申扩展,基于系统所支持的动态加载库及卸载库的基础实现的,Qt Plugin则是qt的一种规范,或者所支持的包装格式。

当然关于插件的设计思想其实大差不差的,必须要满足一些规则。比如说,必须是dll的形式,存在在某些指定目录下在程序运行的过程中进行加载。当然插件设计者本身仍需考虑二进制兼容的问题,我们无法保证dll与.exe使用的是同一个编译器各方面的规则都完全相同。尤其是在dll与exe进行交互时,参数的设计对象等。

Qt插件实现类需要继承,QObject。

相关宏

  • Q_DECLARE_INTERFACE 这个宏将给定的标识符(字符串字面值)关联到名为ClassName的接口类,标识符必须是唯一的。
  • Q_PLUGIN_METADATA 此宏用于声明元数据,该元数据是实例化此对象的插件的一部分。
  • Q_INTERFACES 这个宏告诉Qt类实现了哪些接口。这在实现插件时使用。
  • QT_MOC_EXPORT_PLUGIN moc编译器生成的代码文件,该宏创建了dll导出函数以及创建对象实例函数。

从源码中看出Q_DECLARE_INTERFACE宏,实际上是创建了对应的对象的元信息系统函数尤其是关于qobject_cast<IFace *>(QObject *object),qt安全转换的对象真相qt_metacast()函数实际上是由moc编译器所生成的函数。

#  define Q_DECLARE_INTERFACE(IFace, IId) \
    template <> inline const char *qobject_interface_iid<IFace *>() \
    { return IId; } \
    template <> inline IFace *qobject_cast<IFace *>(QObject *object) \
    { return reinterpret_cast<IFace *>((object ? object->qt_metacast(IId) : nullptr)); } \
    template <> inline IFace *qobject_cast<IFace *>(const QObject *object) \
    { return reinterpret_cast<IFace *>((object ? const_cast<QObject *>(object)->qt_metacast(IId) : nullptr)); }
#endif // Q_MOC_RUN

Q_PLUGIN_METADATA,其实就是Qt自动生成对应的元信息的宏,那个iid数值是用于qobject_cast<>()转换时用到的FILE则是一个文件的内容是JSON格式,里面描述的信息可以被QPluginLoader的metaData()获取到。

#define Q_PLUGIN_METADATA(x) QT_ANNOTATE_CLASS(qt_plugin_metadata, x)

Q_INTERFACES,也是Qt自动生成对应的元信息宏,生成的信息用于qobject_cast<>()进行类型转换查询。

#define Q_INTERFACES(x) QT_ANNOTATE_CLASS(qt_interfaces, x)

QT_MOC_EXPORT_PLUGIN,生成导出dll函数以及创建对象实例方法,静态插件的方法会有点差别但位置一样的。

#define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \
        { \
            static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \
            if (!_instance) {    \
                QT_PLUGIN_RESOURCE_INIT \
                _instance = new IMPLEMENTATION; \
            } \
            return _instance; \
        }

#  define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME)      \
            Q_EXTERN_C Q_DECL_EXPORT \
            const char *qt_plugin_query_metadata() \
            { return reinterpret_cast<const char *>(qt_pluginMetaData); } \
            Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \
            Q_PLUGIN_INSTANCE(PLUGINCLASS)

一个示例

接口文件定义接口

#ifndef ECHOINTERFACE_H
#define ECHOINTERFACE_H

#include <QObject>
#include <QString>

//! [0]
class EchoInterface
{
public:
    virtual ~EchoInterface() = default;
    virtual QString echo(const QString &message) = 0;
};

QT_BEGIN_NAMESPACE

#define EchoInterface_iid "org.qt-project.Qt.Examples.EchoInterface"

Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
QT_END_NAMESPACE

//! [0]
#endif

EchoPlugin插件文件

#ifndef ECHOPLUGIN_H
#define ECHOPLUGIN_H

#include <QObject>
#include <QtPlugin>
#include "echointerface.h"


//! [0]
class EchoPlugin : public QObject, EchoInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.EchoInterface" FILE "echoplugin.json")
    Q_INTERFACES(EchoInterface)

public:
    QString echo(const QString &message) override;
};
//! [0]

#endif

自动生成的moc代码文件,接口转换部分代码。ps:这可是qt安全转换对象的真相哦。

void *EchoPlugin::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_EchoPlugin.stringdata0))
        return static_cast<void*>(this);
    if (!strcmp(_clname, "EchoInterface"))
        return static_cast< EchoInterface*>(this);
    if (!strcmp(_clname, "org.qt-project.Qt.Examples.EchoInterface"))
        return static_cast< EchoInterface*>(this);
    return QObject::qt_metacast(_clname);
}

在这里的话,只能算作是低级插件。但其实高级插件也是一样的东西,只不过是继承它指定的类,而不是自己编写类。
具体的话,你可以查阅源码编译MySQL你会发现它在main文件则是继承的QSqlDriverPlugin驱动来实现。具体可以查阅文档,一般都是实现它的create方法即可。

编译MySQL数据库Qt驱动

目前版本的Qt并不自带Mysql驱动,Mysql驱动需要自行进行编译。好在源码中提供了,Qt插件驱动的项目D:\qt\5.15.2\Src\qtbase\src\plugins\sqldrivers\mysql
ps:如果没有安装源码,请先安装源码。
ps:请自行替换为自己的QT路径。

需要对此mysql的.pro文件进行修改,按照下面的方式修改,MySQL C库设置INCLUDEPATH 和LIBS。

TARGET = qsqlmysql

HEADERS += $$PWD/qsql_mysql_p.h
SOURCES += $$PWD/qsql_mysql.cpp $$PWD/main.cpp

#QMAKE_USE += mysql

OTHER_FILES += mysql.json

PLUGIN_CLASS_NAME = QMYSQLDriverPlugin
include(../qsqldriverbase.pri)

#MySQL c库的头文件路径
INCLUDEPATH += "C:\Program Files\MySQL\MySQL Server 8.0\include"

#mysql c库的.lib路径
LIBS += -L"C:\Program Files\MySQL\MySQL Server 8.0\lib" -l"libmysql"

点击编译,即可将MySQL驱动插件编译完成,然后打开vs命令行,进入编译出来的目录输入指令nmake install安装到qt环境中去,最后再把libmysql.dll拷贝到qt的bin目录,这样运行MySQL驱动时就不会缺少底层依赖了。

windows c sdk获取问题。Windows平台安装的mysql,在mysql server中默认包含了c api库。



Model-View(模型视图)

MVC模式是软件工程中常见的一种软件架构模式,该模式把软件系统(项目)分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。使用MVC模式有很多优势,例如:简化后期对项目的修改、扩展等维护操作;使项目的某一部分变得可以重复利用;使项目的结构更加直观。

有三个关键的抽象类作为扩展接口。

QAbstractItemDelegate               // 呈现项交互项,即渲染显示项,以及与用户交互时的QWidget部件
QAbstractItemModel                  // 数据源模型,用来提供显示数据层面的一个模型
QAbstractItemView                   // 视图交互展示并且与交互事件

代理(QAbstractItemView)

项代理,负责项的视觉呈现以及生产用户交互的编辑代理,最后将交互的数据设置到模型中,最后模型刷新view视图更新数据显示。在自定义项代理时分为两个部分。

  • 渲染部分
 // 绘制代理项内容,option包含了widget及rect等关键数据
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const = 0  

// 推荐绘制项的大小,并不一定起作用,比如listview中,你设置height是可以生效的但width则是不会考虑,tableview则是height与widht都不予以考虑。
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const = 0  
  • 交互部分
// 创建一个用户交互编辑部件,然后返回
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const

// 设置交互编辑部件里面的数据
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const

// 更新设置小部件基于父项部件的大小及坐标
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const

// 将编辑完成的数据写入到模型中去
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const

当然,qt也提供了QStyledItemDelegate一个带有渲染的代理项进行展示,用户只需要负责创建交互小部件即可。这也是推荐的作法。
一个代码例子,我们在项的左边画个椭圆200个像素,然后右边显示数据内容。用到了自定义绘制,用了Qt样式部件的项绘制就是项的默认呈现绘制样式表也是可以生效的。
当然QStyledItemDelegate也差不多是这个逻辑实现的。除了显示还创建了编辑交互代理,需要注意创建交互代理的流程,在编辑完成时要发送的数据。

ps:原谅笔者只展示核心的关键代码部分,完整代码有些不方便编写。

void ItemDelegate::paint(QPainter *painter,
                         const QStyleOptionViewItem &option, const QModelIndex &index) const {

    // 绘制背景
    if (option.state & QStyle::State_Selected) {
      // 被选中状态设置成红色
      painter->setBrush(Qt::red);
    } else if (option.state & QStyle::State_MouseOver) {
      // 鼠标盘旋设置为绿色
      painter->setBrush(Qt::green);
    } else {
      // 默认为黑色
      painter->setBrush(Qt::black);
    }
    // 画一个椭圆
    painter->drawEllipse(option.rect.x(), option.rect.y(), 200, option.rect.height());

    QStyleOptionViewItem opt = option;
    // 设置宽度及x轴,这里不需要减去椭圆部分的,因为默认它会减去x的坐标
    opt.rect.setWidth(opt.rect.width());
    opt.rect.setX(opt.rect.x() + 200);

    // 拿到要显示的数据
    opt.text = index.data().toString();

    // 使用qt样式进行绘制控制外形可以 样式表能生效,重点是要传入opt.widget参数
    qApp->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
}

QSize ItemDelegate::sizeHint(const QStyleOptionViewItem &option,
                             const QModelIndex &/*index*/) const {
    return QSize(option.rect.width(), 200);
}

QWidget *ItemDelegate::createEditor(QWidget *parent,
                              const QStyleOptionViewItem &option,
                                    const QModelIndex &index) const {
    // 交互小部件,调用顺序1
    qDebug() << __FUNCTION__;

    // 创建一个编辑小部件
    QLineEdit* line = new QLineEdit(parent);

    // 关联小部件编辑完成时,进行提交和关闭小部件,否则的话是不会调用到sheModelData函数的
    QObject::connect(line, &QLineEdit::editingFinished, this, &ItemDelegate::editFinish);
    return line;
}

void ItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
    // 交互小部件,调用顺序3
    qDebug() << __FUNCTION__;

    // 设置小部件数据显示
    QLineEdit* line = qobject_cast<QLineEdit*>(editor);
    if (line)
      line->setText(index.data(Qt::DisplayRole).toString());
}

void ItemDelegate::setModelData(QWidget *editor,
                          QAbstractItemModel *model,
                                const QModelIndex &index) const {
    // 交互小部件,调用顺序4
    qDebug() << __FUNCTION__;

    // 将小部件的数据更新到模型中
    QLineEdit* line = qobject_cast<QLineEdit*>(editor);
    if (line)
      model->setData(index, line->text(), Qt::EditRole);
}

void ItemDelegate::updateEditorGeometry(QWidget *editor,
                                  const QStyleOptionViewItem &option,
                                        const QModelIndex &/*index*/) const {
    // 交互小部件,调用顺序2
    qDebug() << __FUNCTION__;

    // 设置小部件对于父部件的位置,位置信息在opiton中
    editor->setGeometry(option.rect);
}

void ItemDelegate::editFinish() {
    // 完成编辑提交数据,关闭编辑器
    QLineEdit* line = qobject_cast<QLineEdit*>(sender());
    emit commitData(line);
    emit closeEditor(line);
}

未完待续....

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

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

相关文章

为Android做一个ShowModal窗口

大家知道&#xff0c;用Delphi实现一个Form&#xff0c;并用ShowModal显示出来&#xff0c;在Android平台是非阻塞的&#xff0c;即执行了Form.ShowModal&#xff0c;代码会继续往下执行而不是等待&#xff0c;这跟在Windows平台是完全不一样的。如果我们需要类似阻塞的效果&am…

使用eclipse编写Java代码:将缩进用空格,而不用Tab

在菜单中选择Window->Preferences&#xff1a; 选择Java下面的Formatter&#xff1a; 点击Edit按钮&#xff0c;在弹出窗口中&#xff0c;Tab policy选择Spaces only&#xff1a; 将Profile name改为一个其它的名字才能保存&#xff1a; 以后新创建的函数、类缩进就…

[ VMware 虚拟机 ] 启动不了图形界面,报 “The system is running in low-graphics mode” 错误

文章目录 问题现象异常原因解决方案 问题现象 在启动虚拟机的时候&#xff0c;不能正常的进入图形界面&#xff0c;报 “The system is running in low-graphics mode” 错误。 异常原因 启动界面的xorg.conf文件失败并删除。 解决方案 1、点击异常界面上的 “ok”后&…

智慧工地项目管理平台源码 项目工程云平台源码 劳务管理、数据预警、视频实时监控、重大危险源和绿色文明施工

通过运用物联网、大数据、云计算、BIM 等技术&#xff0c;搭建由劳务管理、质量安全数据预警、施工现场视频实时监控、重大危险源和绿色文明施工监控等板块构成的模块化、一站式的信息化管理平台。平台可实现施工现场数据采集、数据综合统计及分析、手机App 应用等&#xff0c;…

【LeetCode-面试经典150题-day14】

目录 19.删除链表的倒数第N个结点 82.删除排序链表中的重复元素Ⅱ 61. 旋转链表 86.分隔链表 146.LRU缓存 19.删除链表的倒数第N个结点 题意&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 【输入样例】head [1,2,3,4,5…

什么是性能测试?

性能测试的方法 随着计算机技术的飞速发展&#xff0c;软件性能测试在软件工程领域中占据了越来越重要的地位。本文将介绍软件性能测试的基本概念、方法以及常见的技术指标&#xff0c;帮助读者更好地理解和实施软件性能测试。 一、软件性能测试的基本概念 软件性能测试主要用于…

探索pytest:Python自动化测试的新境界

在当今的软件开发领域&#xff0c;测试已经不仅仅是一个简单的步骤&#xff0c;而是确保软件质量的核心环节。Python&#xff0c;作为全球最受欢迎的编程语言之一&#xff0c;拥有丰富的测试框架和工具。而在这其中&#xff0c;pytest无疑是最受欢迎和最具影响力的一个。本文将…

苍穹外卖总结

前言 1、软件开发流程 瀑布模型需求分析//需求规格说明书、产品原型↓ 设计 //UI设计、数据库设计、接口设计↓编码 //项目代码、单元测试↓ 测试 //测试用例、测试报告↓上线运维 //软件环境安装、配置第一阶段&#xff1a;需求分析需求规格说明书、产品原型一般来说…

机器学习的测试和验证(Machine Learning 研习之五)

关于 Machine Learning 研习之三、四&#xff0c;可到秋码记录上浏览。 测试和验证 了解模型对新案例的推广效果的唯一方法是在新案例上进行实际尝试。 一种方法是将模型投入生产并监控其性能。 这很有效&#xff0c;但如果你的模型非常糟糕&#xff0c;你的用户会抱怨——这…

Config:客户端连接服务器访问远程

springcloud-config: springcloud-config push pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocatio…

Jenkins工具系列 —— 插件 实现将单个JOB聚合在一个JOB中

文章目录 安装插件创建聚合JOB 安装插件 点击 左侧的 Manage Jenkins —> Plugins ——> 左侧的 Available plugins 创建聚合JOB 1、新建一个名为D的聚合JOB 2、设置聚合JOB下所有单个JOB的参数&#xff0c;配置完对应的参数后&#xff0c;当聚合JOB运行时&#xff…

Python采集1000多所世界大学排名数据,并制作可视化数据展示

前言 QS世界大学排名&#xff08;QS World University Rankings&#xff09;是由英国一家国际教育市场咨询公司Quacquarelli Symonds&#xff08;简称QS&#xff09;所发表的年度世界大学排名 采集全球大学排名数据&#xff08;源码已打包&#xff0c;无偿分享 私信自行领取…

Linux(Ubuntu)安装docker

2017年的3月1号之后&#xff0c;Docker 的版本命名开始发生变化&#xff0c;同时将 CE 版本和 EE 版本进行分开。 Docker社区版&#xff08;CE&#xff09;&#xff1a;为了开发人员或小团队创建基于容器的应用,与团队成员分享和自动化的开发管道。docker-ce 提供了简单的安装…

2023/8/17总结

项目完善&#xff1a; 算法推荐 item-CF 算法推荐我主要写的是协同过滤算法&#xff0c;然后协同过滤算法分成俩种—— 基于用户的 user-CF 基于物品的 item-CF 因为害怕用户冷启动&#xff0c;和数据量的原因 我选择了 item-CF 主要思路是——根据用户的点赞列表&…

grep命令的用法

文章目录 前言一、使用说明二、应用举例 前言 grep 命令用于查找文件里符合条件的字符串。 一、使用说明 -r: 如果需要搜索目录中的文件内容, 需要进行递归操作, 必须指定该参数 -i: 对应要搜索的关键字, 忽略字符大小写的差别 -n: 在显示符合样式的那一行之前&#xff0c;标…

Linux操作系统--常用指令(压缩和解压缩类)

(1).gzip/gunzip 压缩 功能:进行文件的压缩和解压缩 语法: gzip 文件 压缩文件,只能将文件压缩为*.gz 文件 gunzip 文件.gz 解压缩文件命令 使用注意事项: -1:只能压缩文件不能压缩目录 -2:不保留原来的文件,也就是直接把源文件压缩覆盖替换掉 -3:同时多个文件会产…

【附安装包】Python-3.9.5安装教程

软件下载 软件&#xff1a;Python版本&#xff1a;3.9.5语言&#xff1a;英文大小&#xff1a;26.9M安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.5GHz 内存2G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.baidu.com/…

性能测试指标拟定参考

性能测试指标 要点一&#xff1a;获取用户数信息 1&#xff09;调查系统当前和未来使用的用户数 系统用户数本系统目前注册的用户数&#xff0c;注册用户数并不代表他会每天并且无时无刻的使用着。 在线用户数同时在线对系统进行操作的用户数量&#xff08;相当于混合场景&a…

Android Mvvm设计模式的详解与实战教程

一、介绍 在开发设计模式中&#xff0c;模式经历了多次迭代&#xff0c;从MVC到MVP&#xff0c;再到如今的MVVM。发现的过程其实很简单&#xff0c;就是为了项目更好的管理。 设计模式严格来说属于软件工程的范畴&#xff0c;但是如今在各大面试中或者开发中&#xff0c;设计模…