【Qt應用程序】

Qt應用程序

    • 摘要
    • 概述
    • 快速開始
      • Qt在線下載與安裝
      • Visual Studio開發Qt項目
        • VS配置Qt擴展
        • VS創建Qt項目
        • 配置qDebug調試信息
        • 配置源程序的字符集
    • 項目結構
    • 對象樹與內存回收
    • 基礎數據類型
    • 信號槽
    • 定時器
    • 窗口
      • QWidget
      • QMainWindow
      • QDialog
      • 窗口布局
      • 窗口中添加右鍵菜單
    • 控件
      • 按鈕類
      • 容器類
      • 自定義控件
    • 事件
      • 事件處理機制
      • 事件過濾器
      • 事件分發器
      • 事件處理器
    • 多線程
      • QThread線程類
      • 線程池
    • 網絡編程
      • 基于TCP套接字的網絡通信
      • 基于WebSocket的雙向網絡通信
    • 數據持久化
        • 數據庫插件與操作
        • JSON
    • 程序發布與打包
      • 發布
      • 打包
    • C++語法補充
      • Lambda表達式

練習項目

摘要

本篇博客對Qt的核心基礎知識進行了總結,以便可以快速上手Qt應用程序,加深理解和記憶

概述

  • 介紹:Qt是一個跨平臺的C++應用程序開發框架
  • 特點
    • Qt是標準C++的擴展
    • 有自己的事件處理機制,提供了signals/slots的安全類型來代替回調函數,使得各個組件之間的協同工作十分簡單
    • 支持2D/3D圖形渲染,支持OpenGL
    • 框架底層模塊化,用戶使用時可進行裁剪
    • 可移植性強:不同平臺使用相同的上層接口,底層封裝了不同平臺的API
  • 模塊架構(All Modules)
    • 基本模塊Qt Essentials:提供了Qt在所有平臺上的基本功能
      • Qt Core:Qt類庫的核心,其他所有模塊都依賴于此模塊
      • Qt GUI:設計GUI的基礎類,包括OpenGL
      • Qt Widgets:用于構建GUI界面的C++圖形組件類
      • Qt Multimedia:音頻、視頻、攝像頭和廣播功能的類
      • Qt Multimedia Widgets:實現多媒體功能的界面組件類
      • Qt Network:網絡編程
      • Qt QML:用于QML和js語言的類
      • Qt Quick:用于構建具有定制用戶界面的動態應用程序的聲明框架
      • Qt Quick Controls:創建桌面樣式用戶界面,基于Qt Quick的用戶界面控件
      • Qt Quick Dialogs:用于Qt Quick的系統對話框類型
      • Qt Quick Layouts:用于Qt Quick 2界面元素的布局項
      • Qt SQL:數據庫操作
      • Qt Test:單元測試
    • 附加模塊Qt Add-Ons:實現了一些特定功能的附加價值的模塊
    • 增值模塊Value-AddModules:單獨發布的提供額外價值的模塊或工具
    • 技術預覽模塊Technology Preview Modules:一些處于開發階段,可作為技術預覽使用的模塊
    • Qt工具

快速開始

Qt在線下載與安裝

  • Qt及相關工具的在線下載

Qt官網:Qt | Tools for Each Stage of Software Development Lifecycle

在這里插入圖片描述

在這里插入圖片描述

  • 安裝

在這里插入圖片描述

在自定義安裝中,選擇編譯工具的類型

在這里插入圖片描述

  • 【為Qt配置環境變量】:如~\Qt\6.9.2\msvc2022_64\bin

Visual Studio開發Qt項目

VS配置Qt擴展
  • 安裝擴展:擴展 → 管理擴展 → Qt Visual Studio Tools

在這里插入圖片描述

  • 配置Qt版本:重啟Visual Studio → 擴展 → Qt VS Tools → Qt Versions

在這里插入圖片描述

  • 【配置Qt Designer與窗體分離】(避免雙擊*.ui文件報錯)

在這里插入圖片描述

VS創建Qt項目

在這里插入圖片描述

配置qDebug調試信息

從默認的窗口 → 控制臺
在這里插入圖片描述

配置源程序的字符集

Visual Studio源程序的字符集默認為國標,Qt默認的字符集是uft8。因此可以在VS中進行適配:

1)增加文件的高級配置選項
在這里插入圖片描述

image-20250906164303202

2)配置源文件字符集

image-20250906164442783

image-20250906164557739

項目結構

項目結構順序

項目文件配置項目信息,組織項目文件 → 由可視化工具設計界面UI(生成UI的XML布局文件)→ 由框架讀取XML布局文件生成UI頭文件UI_窗體類名.h,其包含了UI類,用于代碼級別的控件組織和布局 → 由窗體類名.h窗體頭文件的窗體類聲明,引入UI頭文件,實現窗體與UI的綁定 → 由窗體類名.cpp窗體類源代碼對窗體類頭文件中的窗體類聲明進行實現,并進行邏輯處理。

  • 項目文件.pro,該文件記錄了項目的屬性信息
# 項目編譯的時候需要加載哪些底層模塊
QT = core gui widgets # 關閉自動“fixpath”行為
# fixpath行為:
# 1.Qt會檢測你的項目所依賴的 Qt 模塊(如 core, gui, widgets)
# 2.Qt會找到這些模塊對應的 DLL 所在的目錄(即 Qt 安裝目錄下的 bin 文件夾)
# 3.Qt會在啟動你的 .exe 文件之前,臨時地將 Qt 的 bin 目錄路徑前置(prepend)到當前的環境變量 PATH 中
CONFIG += no_fixpath# 如果在項目中調用了廢棄的函數, 項目編譯的時候會有警告的提示    
# DEFINES += QT_DEPRECATED_WARNINGS# 項目中的源文件
# SOURCES += \
#         QtStudy.cpp \
#         QtStudy.cpp# 項目中的頭文件
# HEADERS += \
#         QtStudy.h# 項目中的窗口界面文件
# FORMS += \
#         QtStudy.ui
  • 程序入口文件
// 窗口類頭文件
#include "QtStudy.h"
// 應用程序類頭文件
#include <QtWidgets/QApplication>int main(int argc, char *argv[])
{// 創建應用程序對象:在一個Qt項目中僅有一個實例,作用進行事件循環并處理QApplication app(argc, argv);// 創建窗口類對象QtStudy window;// 顯示窗口window.show();// 應用程序對象開始事件循環return app.exec();
}
  • 窗口程序文件

    • UI文件:Qt窗口的UI布局是以XML文件形式組織的
    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0"><class>QtStudyClass</class><widget class="QMainWindow" name="QtStudyClass"><property name="geometry"><rect><x>0</x><y>0</y><width>580</width><height>400</height></rect></property><property name="windowTitle"><string>QtStudy</string></property><widget class="QMenuBar" name="menuBar"><property name="geometry"><rect><x>0</x><y>0</y><width>580</width><height>22</height></rect></property></widget><widget class="QToolBar" name="mainToolBar"><attribute name="toolBarArea"><enum>TopToolBarArea</enum></attribute><attribute name="toolBarBreak"><bool>false</bool></attribute></widget><widget class="QStatusBar" name="statusBar"/></widget><layoutdefault spacing="6" margin="11"/><resources><include location="QtStudy.qrc"/></resources><connections/>
    </ui>
    
    • 頭文件

      • UI頭文件:該文件通過讀取UI文件自動生成,當UI文件發送變動時,重新編譯項目,該文件會重新生成

          /********************************************************************************** Form generated from reading UI file 'QtStudy.ui'**** Created by: Qt User Interface Compiler version 6.9.2**** WARNING! All changes made in this file will be lost when recompiling UI file!********************************************************************************/#ifndef UI_QTSTUDY_H#define UI_QTSTUDY_H#include <QtCore/QVariant>#include <QtWidgets/QApplication>#include <QtWidgets/QMainWindow>#include <QtWidgets/QMenuBar>#include <QtWidgets/QPushButton>#include <QtWidgets/QStatusBar>#include <QtWidgets/QToolBar>#include <QtWidgets/QWidget>QT_BEGIN_NAMESPACEclass Ui_QtStudyClass{public:QWidget *centralWidget;QMenuBar *menuBar;QToolBar *mainToolBar;QStatusBar *statusBar;void setupUi(QMainWindow *QtStudyClass){if (QtStudyClass->objectName().isEmpty())QtStudyClass->setObjectName("QtStudyClass");QtStudyClass->resize(580, 400);centralWidget = new QWidget(QtStudyClass);centralWidget->setObjectName("centralWidget");QtStudyClass->setCentralWidget(centralWidget);menuBar = new QMenuBar(QtStudyClass);menuBar->setObjectName("menuBar");menuBar->setGeometry(QRect(0, 0, 580, 22));QtStudyClass->setMenuBar(menuBar);mainToolBar = new QToolBar(QtStudyClass);mainToolBar->setObjectName("mainToolBar");QtStudyClass->addToolBar(Qt::ToolBarArea::TopToolBarArea, mainToolBar);statusBar = new QStatusBar(QtStudyClass);statusBar->setObjectName("statusBar");QtStudyClass->setStatusBar(statusBar);retranslateUi(QtStudyClass);QMetaObject::connectSlotsByName(QtStudyClass);} // setupUivoid retranslateUi(QMainWindow *QtStudyClass){QtStudyClass->setWindowTitle(QCoreApplication::translate("QtStudyClass", "QtStudy", nullptr));}; // retranslateUinamespace Ui {class QtStudyClass: public Ui_QtStudyClass {};} // namespace UiQT_END_NAMESPACE#endif // UI_QTSTUDY_H
        
      • 窗體頭文件

         #pragma once// Qt標準窗口類頭文件#include <QtWidgets/QMainWindow>// 自動生成的UI頭文件#include "ui_QtStudy.h"class QtStudy : public QMainWindow{// 該宏是為了能夠使用Qt中的信號槽機制Q_OBJECTpublic:QtStudy(QWidget *parent = nullptr);~QtStudy();private:// 定義指向窗口的UI對象Ui::QtStudyClass ui;};
        
    • 源程序

      #include "QtStudy.h"QtStudy::QtStudy(QWidget *parent) : QMainWindow(parent)
      {// 雙向綁定,為UI對象設置對應的窗體ui.setupUi(this);
      }QtStudy::~QtStudy()
      {}
      

對象樹與內存回收

QObject是Qt所有類的基類,它是以對象樹的形式組織起來的。**當創建一個QObject對象時,其構造函數會接收一個QObject指針作為參數,該參數即為父對象指針parent(通常是組件的父窗體/容器),然后創建的QObject對象會被加入到其父對象的children()列表中。**當父對象被析構時,其children列表中的所有對象也會被析構。(如,當用戶關閉一個對話框時,應用程序會將該對話框對象的內存進行釋放回收,其包含的按鈕、圖標等組件對象也會被一并刪除)

  • 當一個QObject對象在堆上創建的時候,Qt會為其創建一個對象樹。不過,該對象樹中對象的順序是未定的,這意味著這些對象銷毀時的順序也是不定的
  • 對象樹中的任何對象被刪除時,若該對象有parent,則自動從parent的children()列表中刪除;如果該對象有孩子,則自動刪除列表中的每個孩子

為QObject對象指定parent的方式:

// 1.在創建對象時,在構造函數中傳入parent指針
QWidget::QWidget(QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
QTimer::QTimer(QObject *parent = nullptr);// 2.通過setParent()方法,假設這個控件沒有在構造的時候指定符對象, 可以調用QObject的api指定父窗口對象
void QObject::setParent(QObject *parent);

基礎數據類型

  • 基礎類型

頭文件#include <QtGlobal>(雖然在Qt中有屬于自己的整形或者浮點型, 但是在開發過程中一般不用, 常用的類型關鍵字還是 C/C++中的 int, float, double 等)

類型名稱對應關系備注
qint8signed char有符號8位數據
qint16signed short16位數據類型
qint32signed short32位有符號數據類型
qint64long long int 或(__int64)64位有符號數據類型,Windows中定義為__int64
qintptrqint32 或 qint64指針類型 根據系統類型不同而不同,32位系統為qint32、64位系統為qint64
qlonglonglong long int 或(__int64)Windows中定義為__int64
qptrdiffqint32 或 qint64根據系統類型不同而不同,32位系統為qint32、64位系統為qint64
qrealdouble 或 float除非配置了-qreal float選項,否則默認為double
quint8unsigned char無符號8位數據類型
quint16unsigned short無符號16位數據類型
quint32unsigned int無符號32位數據類型
quint64unsigned long long int 或 (unsigned __int64)無符號64比特數據類型,Windows中定義為unsigned __int64
quintptrquint32 或 quint64根據系統類型不同而不同,32位系統為quint32、64位系統為quint64
qulonglongunsigned long long int 或 (unsigned __int64)Windows中定義為__int64
ucharunsigned char無符號字符類型
uintunsigned int無符號整型
ulongunsigned long無符號長整型
ushortunsigned short無符號短整型
  • 控制臺輸出

在Qt中進行log輸出, 一般不使用c中的printf, 也不是使用C++中的cout, Qt框架提供了專門用于日志輸出的類qDebug,其頭文件為:#include <QDebug>

直接執行可執行程序(非IDE調試)下沒有日志輸出窗口,默認情況下日志信息是不會打印到終端窗口的, 如果想要實現這樣的效果, 必須在項目文件中添加相關的屬性信息(Visual Studio IDE的Debug調試配置如上文)

# 項目文件(*.pro)# 找到配置項 config, 添加 console 控制臺屬性
CONFIG += c++11 console
#include <QtGlobal>
#include <QDebug>int basic_data_type()
{qreal double_type = 9.9;uint unsigned_int_type = 9;qDebug() << double_type << "\t" << unsigned_int_type << "\n";return 0;
}

若在qDebug打印日志時使用中文,但是編譯報錯,此時屬于字符集的錯誤。有如下解決方法:

1)修改IDE編輯源碼時的字符集(VS參考如上)

2)在項目文件.pro中添加編碼配置:QMAKE_CXXFLAGS += /utf-8

3)打印時使用寬字符編碼:qDebug() << "中文";

4)依靠QString進行轉碼:qDebug() << QString::fromUtf8("中文");

5)使用tr函數:qDebug() << tr("中文");

  • 字符串類型
語言類型字符串類型
Cchar*
C++std::string, char*
QtQByteArray, QString 等

構造、操作、查找、類型轉換

  • 位置和尺寸

點QPoint、直線QLine、尺寸(長寬)QSize、矩形QRect

  • 日期和時間

日期QDate、時間QTime、日期時間QDateTime

  • QVariant

QVariant可以作為不同Qt和C++數據類型及其自定義數據的容器

#include <QVariant>/*
* QVariant
*/
int test_qvariant() 
{// 1.構造、初始化QVariant vInt(42);QVariant vDouble = 3.14159;QVariant vBool = false;QVariant q;q.setValue('a');QVariant qv = QVariant::fromValue(QStringList({ "hello lyf1" ,"hello lyf2" ,"hello lyf3" }));// 2.檢測存儲數據的類型qDebug() << vInt.typeName();qDebug() << vDouble.typeName();qDebug() << vInt.typeName();qDebug() << q.typeName();qDebug() << qv.typeName();// 3.檢索并轉換具體數據類型qDebug()<< vInt.toInt();qDebug() << vDouble.toDouble();qDebug() << vInt.toBool();qDebug() << q.toChar();// 【復雜】類型轉換檢查與轉換if (qv.canConvert<QStringList>()) {QStringList qsl = qv.value<QStringList>();for (QString qsli : qsl) {qDebug() << qsli;}}// 4.自定義類型return 0;
}

信號槽

信號槽是Qt的事件模塊(實際就是觀察者模式:發布-訂閱模式)當某個事件發生后(如按鈕被點擊)它就會發出一個信號signal,如果有對象對該信號感興趣,它就會通過connect函數,將待處理的信號和自己的槽函數slot(回調函數)進行綁定(訂閱)

  • Qt標準信號槽的使用

connect函數原型如下:

QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection);
參數:- sender:   發出信號的對象- signal:   屬于sender對象, 信號是一個函數, 這個參數的類型是函數指針, 信號函數地址- receiver: 信號接收者- method:   屬于receiver對象, 當檢測到sender發出了signal信號, receiver對象調用method方法,信號發出之后的處理動作//  參數 signal 和 method 都是函數地址, 因此簡化之后的 connect() 如下:
connect(const QObject *sender, &QObject::signal, const QObject *receiver, &QObject::method);

在Qt自身的源碼中,信號函數和槽函數是在組件類中用Q_SIGNALSQ_SLOTS進行聲明的,如下示例:

QPushButton繼承于QAbstractButtonQAbstractButton中的Q_SIGNALS聲明了其按下按鈕的信號函數:

image-20250905114723969

QMainWindow繼承于QWidgetQWidget中的Q_SLOTS聲明了其關閉窗口的槽函數:

image-20250905115925382

在窗口內將這兩個信號函數和槽函數連接

/*
* QtStudy.cpp:主窗口函數
*/
#include "QtStudy.h"/*
* 主窗口函數構造函數的實現
* connect()操作一般寫在窗口的構造函數中, 相當于在事件產生之前在qt框架中先進行注冊, 這樣在程序運行過程中假設產生了按鈕的點擊事件, 框架就會調用信號接收者對象對應的槽函數了, 如果信號不產生, 槽函數也就一直不會被調用
*/
QtStudy::QtStudy(QWidget *parent) : QMainWindow(parent)
{// 雙向綁定,為UI對象設置對應的窗體ui.setupUi(this);// 1.Qt5后的語法,使用函數指針。這種方法在Qt6中仍被推薦connect(ui.pushButton, &QPushButton::clicked, this, &QMainWindow::close);// 2.Qt4的語法,字符串語法。這種方法在Qt5后不再被推薦connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(close()));// 3.lamuda表達式語法,結合C++11語法新特性// 正確:槽函數(回調函數)可以使用拉姆達表達式(實際是匿名函數)connect(ui.pushButton, &QPushButton::clicked, this, [this]() {this->close();});// 錯誤,原因是:信號是類接口的一部分,需要在類定義中明確,不能使用拉姆達表達式。且此處手動觸發按鈕按下,邏輯也是錯誤的。//connect(ui.pushButton, [this]() {//    ui.pushButton->clicked(); }, this, [this]() {//        this->close();//        });// 錯誤,原因是:信號是類接口的一部分,需要在類定義中明確,不能使用拉姆達表達式。且此處手動觸發按鈕按下,邏輯也是錯誤的。//QPushButton* btn = ui.pushButton;//connect(ui.pushButton, [btn]() {//    btn->clicked(); }, this, [this]() {//    this->close(); // });// 4.使用QObject::connect的返回值:Qt6 中 connect() 返回 QMetaObject::Connection,可用于斷開連接// QMetaObject::Connection connection = connect(ui.pushButton, &QPushButton::clicked, this, &QMainWindow::close);// disconnect(connection);// 5.利用自動連接命名約定:如果槽函數命名為 on_對象名_信號名,Qt 會自動連接}QtStudy::~QtStudy()
{}
  • 自定義信號槽函數

    • 自定義信號函數

      • 信號函數的返回值必須是void類型
      • 信號函數支持重載
      • 信號函數是成員函數,只需要聲明,不能實現
      • 信號函數使用signals進行聲明,無需加訪問修飾符(實際默認是protect),由此只能類內訪問,這是為了保護信號的發送在類內進行(但也可以增加其他訪問修飾符)如果要在類外發送信號,則可以增加一個Public修飾的中間函數,在中間函數中使用emit(其作用只是聲明信號要被發送了,可省略不寫,底層是emit==#define emit)調用信號函數
      • 信號函數、槽函數的返回值必須是void類型
    • 自定義槽函數

      • 槽函數的返回值必須是void類型
      • 槽函數支持重載
      • 槽函數的類型可以是:類成員函數、全局函數、靜態函數、拉姆達表達式(如上例)
      • 信號函數用【用slots進行修飾】(Qt5之后,slots可以省略)槽函數的訪問修飾符通常為public slot,使得該類的槽函數可以被Qt框架回調(如下圖,signals和slots實際同Qt框架內的Q_SIGNALS和Q_SLOTS一致)

    image-20250905153236077

    • 示例:Qt標準控件自帶的信號函數和自定義的槽函數
    /*
    * QtStudy.cpp:窗口類聲明
    */
    #pragma once#include <QtWidgets/QMainWindow>
    #include "ui_QtStudy.h"class QtStudy : public QMainWindow
    {Q_OBJECTpublic:QtStudy(QWidget *parent = nullptr);~QtStudy();private:Ui::QtStudyClass ui;// 按鈕點擊計數函數,在按鈕點擊信號綁定的槽函數中使用int click_count = 0;// 聲明窗口類的自定義槽函數
    public slots:void on_push_button_click();
    };
    
    /*
    * 窗口類的實現函數
    */
    #include "QtStudy.h"QtStudy::QtStudy(QWidget *parent) : QMainWindow(parent)
    {ui.setupUi(this);// 連接自定義信號槽connect(ui.pushButton, &QPushButton::clicked, this, &QtStudy::on_push_button_click);}QtStudy::~QtStudy()
    {}/*
    * 按鍵計數槽函數
    */
    void QtStudy:: on_push_button_click()
    {QString s;s = s.fromStdString(std::to_string(this->click_count++));qDebug() << s;ui.pushButton->setText(s);
    }
    
    • 示例:自定義信號和槽
    #pragma once#include <QtWidgets/QMainWindow>
    #include "ui_QtStudy.h"class QtStudy : public QMainWindow
    {// 該宏是為了能夠使用Qt中的信號槽機制Q_OBJECTpublic:QtStudy(QWidget *parent = nullptr);~QtStudy();private:Ui::QtStudyClass ui;public slots:void get_message();void get_message(int x);
    };/*
    * 自定義信號類,需繼承QObject類,必須使用 public 繼承
    */
    class TestSignal : public QObject
    {// 必須的宏,啟用信號槽功能Q_OBJECTsignals:// 信號只能聲明,不能有實現void send_signal();void send_signal(int x);public:// 提供公共方法來觸發信號void triggerSignal();void triggerSignal(int x);
    };
    
    #include "QtStudy.h"/*
    * 提供公共方法來觸發信號
    */
    void TestSignal::triggerSignal()
    {qDebug() << "Emitting send signal with no args";// 發射無參信號,使用emitemit send_signal();
    }void TestSignal::triggerSignal(int x)
    {qDebug() << "Emitting send signal with arg:" << x;// 發射無參信號,使用emitemit send_signal(x);
    }QtStudy::QtStudy(QWidget *parent) : QMainWindow(parent)
    {ui.setupUi(this);// 1.自定義信號和槽函數// 1.1創建信號源對象TestSignal ts;// 1.2.1針對信號槽重載的情況,Qt4版本的綁定信號函數和槽函數:字符串連接。正確connect(&ts, SIGNAL(send_signal()), this, SLOT (get_message()));connect(&ts, SIGNAL(send_signal(int)), this, SLOT(get_message(int)));// 1.2.2針對信號槽重載的情況,若直接Qt4版本的綁定信號函數和槽函數:函數指針,則會出錯。因為不知道指向// connect(&ts, &TestSignal::send_signal, this, &QtStudy::get_message);// 不過可以手動定義對應類型的函數指針,給connect傳參void (TestSignal:: *send_signal_with_no_arg)() = &TestSignal::send_signal;void (QtStudy:: *get_message_with_no_arg)() = &QtStudy::get_message;connect(&ts, send_signal_with_no_arg, this, get_message_with_no_arg);void (TestSignal:: * send_signal_with_arg)(int) = &TestSignal::send_signal;void (QtStudy:: * get_message_with_arg)(int) = &QtStudy::get_message;connect(&ts, send_signal_with_arg, this, get_message_with_arg);// 1.3調用信號函數// 無參ts.triggerSignal();// 有參ts.triggerSignal(9);}QtStudy::~QtStudy()
    {}/*
    * 無參接收信息槽函數
    */
    void QtStudy::get_message() 
    {qDebug() << "get message with no args";
    }/*
    * 含參接收信息槽函數
    */
    void QtStudy::get_message(int x)
    {qDebug() << "get message with no arg: " << x;
    }
    
  • 擴展

    • 一個信號可以連接多個槽函數,槽函數的執行順序是隨機的,與connect順序無關
    • 一個槽函數可以連接多個信號函數,響應不同信號
    • 信號可以連接信號,進行信號的鏈式傳遞

定時器

Qt中的定時器類是QTimer,使用時:創建定時器實例 → 設置相關參數 → 通過connect與定時器的信號函數綁定 → 周期性啟用或一次性啟用定時器 → 超時發送信號(調用定時器自身的信號函數[signal] void QTimer::timeout();)→ 通過綁定的槽函數執行回調,實現定時任務

#pragma once#include <QtWidgets/QMainWindow>
#include "ui_QtStudy.h"
#include "QTimer"
#include "QDateTime"class QtStudy : public QMainWindow
{// 該宏是為了能夠使用Qt中的信號槽機制Q_OBJECTpublic:QtStudy(QWidget *parent = nullptr);~QtStudy();private:Ui::QtStudyClass ui;// 注意定時器實例應創建在成員變量(或全局變量)中,若在成員函數中創建,周期性的定時器會隨著局部函數執行結束而被銷毀,因此無法實現周期性定時功能QTimer qtimer;public slots:void update_current_time();
};
#include "QtStudy.h"
#include "basic_data_type.h"QtStudy::QtStudy(QWidget *parent) : QMainWindow(parent)
{ui.setupUi(this);/* 三.定時器 */// 1.設置、讀取時間精度qtimer.setTimerType(Qt::PreciseTimer);Qt::TimerType timer_type = qtimer.timerType();qDebug() << "時間精度: " << timer_type;// 2.設置、讀取時間間隔qtimer.setInterval(1);int interval = qtimer.interval();qDebug() << "時間間隔: " << interval;// 3.設置定時器只觸發一次// qtimer.setSingleShot(true);// 判斷定時器是否只觸發一次bool single_mark = qtimer.isSingleShot();qDebug() << "是否只執行一次: " << interval;// 4.啟動或重啟定時器,若無參數傳入,則需通過setInterval設置qtimer.start();// 啟動或重啟定時器,若有參數傳入,則無需通過setInterval設置// qtimer.start(1);// 5.判斷定時器是否正在運行bool active_mark = qtimer.isActive();qDebug() << "是否運行: " << active_mark;// 6.停止定時器// qtimer.stop();// 7.綁定定時器connect(&qtimer, &QTimer::timeout, this, &QtStudy::update_current_time);// 8.定時器的另一種用法:static pubic 靜態公共方法,定時器僅執行一次// [static] void QTimer::singleShot(//     int msec, const QObject * receiver,//     PointerToMemberFunction method);QTimer::singleShot(1, this, []() {qDebug() << "靜態單次定時器被執行";});}QtStudy::~QtStudy()
{}/*
* 更新當前時間
*/
void QtStudy::update_current_time() 
{QDateTime qdt = QDateTime::currentDateTime();QString s = qdt.toString();ui.lineEdit_2->setText(s);
}

窗口

窗口的坐標系:以窗口的左上角為坐標系原點,向右和向下分別為X、Y軸的延申軸

窗口的相對坐標:每個窗口都有自己的坐標原點,內嵌窗口的原點位置為其父窗口坐標系中的相對坐標

QWidget

QWidget類是所有窗口類的父類(控件類是也屬于窗口類)

在這里插入圖片描述
在這里插入圖片描述

#pragma once#include <QtWidgets/QMainWindow>
#include "ui_QtStudy.h"class QtStudy : public QMainWindow
{Q_OBJECTpublic:QtStudy(QWidget *parent = nullptr);~QtStudy();private:// 子窗口QWidget sub_window;
};
#include "QtStudy.h"
#include "basic_data_type.h"QtStudy::QtStudy(QWidget *parent) : QMainWindow(parent)
{ui.setupUi(this);/* 四.窗口 */// 1.widgit// 1.1設置父對象// void QWidget::setParent(QWidget *parent);// void QWidget::setParent(QWidget* parent, Qt::WindowFlags f);sub_window.setParent(this);// 1.2獲取窗口的父對象QWidget* sub_parent = sub_window.parentWidget();qDebug() << sub_parent;// 1.3設置當前窗口的幾何信息(位置和尺寸信息), 不包括邊框sub_window.setGeometry(0,300,50,50);// 1.4獲取當前窗口相對于父窗口的位置(包括邊框)QRect r_no_edge = sub_window.frameGeometry();qDebug() << r_no_edge;// 1.t獲取當前窗口相對于父窗口的位置(不包括邊框)QRect r_with_edge = sub_window.geometry();qDebug() << r_with_edge;// 1.6設置窗體背景色(樣式表)sub_window.setStyleSheet("background-color: red;");// 1.7顯示窗口sub_window.show();// 1.8移動窗口位置connect(ui.pushButton_3, &QPushButton::clicked, this, [this]() {if (sub_window.frameGeometry().x() + 10 > this->frameGeometry().x()) {sub_window.move(0, sub_window.frameGeometry().y());}else {sub_window.move(sub_window.frameGeometry().x() + 10, sub_window.frameGeometry().y());}});// 1.9獲取、設置窗口的尺寸信息QSize qs = sub_window.size();qDebug() << qs;sub_window.resize(50,50);// 1.10 獲取、設置窗口的固定尺寸// 1.11 獲取、設置窗口的最大尺寸// 1.12 獲取、設置窗口的最小尺寸// 1.13 獲取、設置窗口的高度、最小高度、最大高度// 1.14 獲取、設置窗口的標題sub_window.setWindowTitle(QString("子窗口"));QString sub_window_tittle = sub_window.windowTitle();qDebug() << sub_window_tittle;// 1.15 獲取、設置窗口的圖標// 使用.qrc維護項目中的資源文件QIcon set_sub_window_icon(":/QtStudy/icon.ico");sub_window.setWindowIcon(set_sub_window_icon);QIcon get_sub_window_icon = sub_window.windowIcon();qDebug() << get_sub_window_icon;}QtStudy::~QtStudy()
{}

image-20250906175952314

QWidget的信號函數:

// QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy);
// 窗口的右鍵菜單策略 contextMenuPolicy() 參數設置為 Qt::CustomContextMenu, 按下鼠標右鍵發射該信號
[signal] void QWidget::customContextMenuRequested(const QPoint &pos);
// 窗口圖標發生變化, 發射此信號
[signal] void QWidget::windowIconChanged(const QIcon &icon);
// 窗口標題發生變化, 發射此信號
[signal] void QWidget::windowTitleChanged(const QString &title);

QWidget的槽函數:

//------------- 窗口顯示 -------------
// 關閉當前窗口
[slot] bool QWidget::close();
// 隱藏當前窗口
[slot] void QWidget::hide();
// 顯示當前創建以及其子窗口
[slot] void QWidget::show();
// 全屏顯示當前窗口, 只對windows有效
[slot] void QWidget::showFullScreen();
// 窗口最大化顯示, 只對windows有效
[slot] void QWidget::showMaximized();
// 窗口最小化顯示, 只對windows有效
[slot] void QWidget::showMinimized();
// 將窗口回復為最大化/最小化之前的狀態, 只對windows有效
[slot] void QWidget::showNormal();//------------- 窗口狀態 -------------
// 判斷窗口是否可用
bool QWidget::isEnabled() const; // 非槽函數
// 設置窗口是否可用, 不可用窗口無法接收和處理窗口事件
// 參數true->可用, false->不可用
[slot] void QWidget::setEnabled(bool);
// 設置窗口是否可用, 不可用窗口無法接收和處理窗口事件
// 參數true->不可用, false->可用
[slot] void QWidget::setDisabled(bool disable);
// 設置窗口是否可見, 參數為true->可見, false->不可見
[slot] virtual void QWidget::setVisible(bool visible);

QMainWindow

在這里插入圖片描述

  • 菜單欄

可視化添加方式:

image-20250906181512196

代碼添加方式:

// 給菜單欄添加菜單
QAction *QMenuBar::addMenu(QMenu *menu);
QMenu *QMenuBar::addMenu(const QString &title);
QMenu *QMenuBar::addMenu(const QIcon &icon, const QString &title);// 給菜單對象添加菜單項(QAction)
QAction *QMenu::addAction(const QString &text);
QAction *QMenu::addAction(const QIcon &icon, const QString &text);// 添加分割線
QAction *QMenu::addSeparator();

單擊菜單項的信號函數:

[signal] void QAction::triggered(bool checked = false);// ui->menu_item為QAction的名稱(菜單項)
connect(ui->actionClick, &QAction::triggered, this, [=]()
{qDebug()<< "點擊菜單項";
});

image-20250906182917232

  • 工具欄

可視化添加方式:在菜單欄中新增菜單項 → 將菜單項拖動至工具欄中

image-20250906183657605

代碼添加:

// 在QMainWindow窗口中添加工具欄
void QMainWindow::addToolBar(Qt::ToolBarArea area, QToolBar *toolbar);
void QMainWindow::addToolBar(QToolBar *toolbar);
QToolBar *QMainWindow::addToolBar(const QString &title);// 將Qt控件放到工具欄中
// 工具欄類: QToolBar
// 添加的對象只要是QWidget或者啟子類都可以被添加
QAction *QToolBar::addWidget(QWidget *widget);// 添加QAction對象
QAction *QToolBar::addAction(const QString &text);
QAction *QToolBar::addAction(const QIcon &icon, const QString &text);// 添加分隔線
QAction *QToolBar::addSeparator();/* 示例 */
// 添加工具欄
QToolBar* toolbar = new QToolBar("toolbar");
this->addToolBar(Qt::LeftToolBarArea, toolbar);// 給工具欄添加按鈕和單行輸入框
ui->toolBar->addWidget(new QPushButton("搜索"));
QLineEdit* edit = new QLineEdit;
edit->setMaximumWidth(200);
edit->setFixedWidth(100);
ui->toolBar->addWidget(edit);
// 添加QAction類型的菜單項
ui->toolBar->addAction(QIcon("{資源路徑}"), "");
  • 狀態欄

一般情況下, 需要在狀態欄中添加某些控件, 顯示某些屬性, 使用最多的就是添加標簽 QLabel

// 類型: QStatusBar
void QStatusBar::addWidget(QWidget *widget, int stretch = 0);[slot] void QStatusBar::clearMessage();
[slot] void QStatusBar::showMessage(const QString &message, int timeout = 0);
// 狀態欄添加子控件
// 按鈕
QPushButton* button = new QPushButton("按鈕");
ui->statusBar->addWidget(button);
// 標簽
QLabel* label = new QLabel("hello,world");
ui->statusBar->addWidget(label);
  • 停靠窗口

停靠窗口Dock Widget可以通過鼠標拖拽停靠到窗口的上下左右位置上或浮動在窗口上方(若在非QMainWindows中添加了停靠窗口,則該窗口是不能浮動的)

QDialog

// 構造函數
QDialog::QDialog(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());// 模態顯示窗口
[virtual slot] int QDialog::exec();// 隱藏模態窗口, 并且解除模態窗口的阻塞, 將 exec() 的返回值設置為 QDialog::Accepted
[virtual slot] void QDialog::accept();
// 隱藏模態窗口, 并且解除模態窗口的阻塞, 將 exec() 的返回值設置為 QDialog::Rejected
[virtual slot] void QDialog::reject();
// 關閉對話框并將其結果代碼設置為r。finished()信號將發出r;
// 如果r是QDialog::Accepted 或 QDialog::Rejected,則還將分別發出accept()或Rejected()信號。
[virtual slot] void QDialog::done(int r);// 信號函數
[signal] void QDialog::accepted();
[signal] void QDialog::rejected();
[signal] void QDialog::finished(int result);

示例:

自定義一個對話框

#include <QDialog>
class MyDialog : public QDialog
{Q_OBJECT
public:MyDialog(QWidget * parent = nullptr);~MyDialog();private:QPushButton dialog_button1;QPushButton dialog_button2;QPushButton dialog_button3;
};

寫該對話框的實現,在其初始化函數中,對其三個按鈕進行設置,按下時分別調用其3個方法、并發送信號;在主窗體中顯示模態對話框

#include "QtStudy.h"QtStudy::QtStudy(QWidget *parent) : QMainWindow(parent)
{ui.setupUi(this);// 3.QDialog自定義對話框connect(ui.pushButton_4, &QPushButton::clicked, this, [this]() {MyDialog my_dialog(this);// 模態顯示窗口int ret = my_dialog.exec();if (ret == QDialog::Accepted){qDebug() << "Accept: " << ret;}else if (ret == QDialog::Rejected){qDebug() << "Accept: " << ret;}else{qDebug() << "Others: " << ret;}});
}QtStudy::~QtStudy()
{}/*
* 自定義對話框的初始化函數
*/
MyDialog::MyDialog(QWidget* parent) : QDialog(parent)
{this->setParent(parent);this->dialog_button1.setParent(this);this->dialog_button1.setText("accept");this->dialog_button1.setGeometry(50,50,50,30);connect(&this->dialog_button1, &QPushButton::clicked, this, [this]() {// 隱藏模態窗口, 并且解除模態窗口的阻塞, 將 exec() 的返回值設置為 QDialog::Acceptedthis->accept();});this->dialog_button1.show();this->dialog_button2.setParent(this);this->dialog_button2.setText("reject");this->dialog_button2.setGeometry(150, 50, 50, 30);connect(&this->dialog_button2, &QPushButton::clicked, this, [this]() {// 隱藏模態窗口, 并且解除模態窗口的阻塞, 將 exec() 的返回值設置為 QDialog::Rejectedthis->reject();});this->dialog_button2.show();this->dialog_button3.setParent(this);this->dialog_button3.setText("done");this->dialog_button3.setGeometry(250, 50, 50, 30);connect(&this->dialog_button3, &QPushButton::clicked, this, [this]() {// 關閉對話框并將其結果代碼設置為r。finished()信號將發出r;// 如果r是QDialog::Accepted 或 QDialog::Rejected,則還將分別發出accept()或Rejected()信號。this->done(9);});this->dialog_button3.show();}/*
* 自定義對話框的析構函數
*/
MyDialog::~MyDialog()
{}

Qt中QDialog對話框的子類(調用其靜態方法)

  • QMessageBox:通過這個類可以顯示一些簡單的提示框, 用于展示警告、錯誤、問題等信息

  • QFileDialog:通過這個類可以選擇要打開/保存的文件或者目錄

  • QFontDialog:通過這個類我們可以得到一個進行字體屬性設置的對話框窗口

  • QColorDialog:通過這個類我們可以得到一個選擇顏色的對話框窗口

  • QInputDialog:通過這個類我們可以得到一個輸入對話框窗口。

  • 根據實際需求我們可以在這個輸入窗口中輸入整形, 浮點型, 字符串類型的數據, 并且還可以顯示下拉菜單供使用者選擇

  • QProgressDialog:

  • 通過這個類我們可以得到一個帶進度條的對話框窗口, 這種類型的對話框窗口一般常用于文件拷貝、數據傳輸等實時交互的場景中

窗口布局

  • 在UI中設置布局

    • 方式1:使用Qt提供的布局, 從工具箱中找到相關的布局, 然后將其拖拽到UI窗口中,再將控件放入布局對應的區域內
      在這里插入圖片描述

    • 方式2:將控件添加到容器類中(一般使用QWidget),然后右鍵容器,修改容器的布局方式

    image-20250910113906187

    注意:通過UI編輯窗口的樹狀列表我們可以對所有窗口的布局進行檢查, 如果發現某個窗口沒有布局, 一定要對其進行設置, 如果某個窗口沒有進行布局, 那么當這個窗口顯示出來之后里邊的子部件就可能無法被顯示出來。

  • 布局屬性

在這里插入圖片描述

  • 彈簧:彈簧可以用來調整控件的位置及其控件間的距離,有水平彈簧Horizontal Spacer和垂直彈簧Vertical Spacer,彈簧也有其自己的屬性

在這里插入圖片描述

  • 通過API設置布局
    在這里插入圖片描述

    QHBoxLayoutQVBoxLayoutQGridLayout分別為水平、垂直、網格布局

    • QLayout
    // 在布局最后面添加一個窗口
    void QLayout::addWidget(QWidget *w);
    // 將某個窗口對象從布局中移除, 窗口對象如果不再使用需要自己析構
    void QLayout::removeWidget(QWidget *widget);
    // 設置布局的四個邊界大小, 即: 左、上、右和下的邊距。
    void QLayout::setContentsMargins(int left, int top, int right, int bottom);
    // 設置布局中各個窗口之間的間隙大小
    void setSpacing(int);
    
    • QHBoxLayout、QVBoxLayout
    // 創建符窗口對象
    QWidget *window = new QWidget;
    // 創建若干個子窗口對象
    QPushButton *button = new QPushButton("One");// 創建水平布局對象
    QHBoxLayout *layout = new QHBoxLayout;
    // 將子窗口添加到布局中
    layout->addWidget(button);
    // 將水平布局設置給父窗口對象
    window->setLayout(layout);
    // 顯示父窗口
    window->show();
    
    • QGridLayout
    // 創建父窗口對象
    QWidget* window = new QWidget;
    // 創建子窗口對象
    QPushButton *button1 = new QPushButton("One");
    QPushButton *button2 = new QPushButton("Two");
    QPushButton *button3 = new QPushButton("Three");
    QPushButton *button4 = new QPushButton("Four");
    QPushButton *button5 = new QPushButton("Five");
    QPushButton *button6 = new QPushButton("Six");
    // 多行文本編輯框
    QTextEdit* txedit = new QTextEdit;
    txedit->setText("我占用了兩行兩列的空間哦。");QGridLayout* layout = new QGridLayout;
    // 兩參數:第n行,第n列;四參數:第n行,第n列,長占幾行,寬占幾行
    // 按鈕起始位置: 第1行, 第1列, 該按鈕占用空間情況為1行1列
    layout->addWidget(button1, 0, 0);
    // 按鈕起始位置: 第1行, 第2列, 該按鈕占用空間情況為1行1列
    layout->addWidget(button2, 0, 1);
    // 按鈕起始位置: 第1行, 第3列, 該按鈕占用空間情況為1行1列
    layout->addWidget(button3, 0, 2);
    // 編輯框起始位置: 第2行, 第1列, 該按鈕占用空間情況為2行2列
    layout->addWidget(txedit, 1, 0, 2, 2);
    // 按鈕起始位置: 第2行, 第3列, 該按鈕占用空間情況為1行1列
    layout->addWidget(button4, 1, 2);
    // 按鈕起始位置: 第3行, 第3列, 該按鈕占用空間情況為1行1列
    layout->addWidget(button5, 2, 2);
    // 按鈕起始位置: 第4行, 第1列, 該按鈕占用空間情況為1行3列
    layout->addWidget(button6, 3, 0, 1, 3);// 設置布局中水平方向窗口之間間隔的寬度
    // void QGridLayout::setHorizontalSpacing(int spacing);
    // 設置布局中垂直方向窗口之間間隔的寬度
    // void QGridLayout::setVerticalSpacing(int spacing);// 網格布局設置給父窗口對象
    window->setLayout(layout);
    // 顯示父窗口
    window->show();  
    

窗口中添加右鍵菜單

  • 基于鼠標事件的實現:鼠標事件配合QMenu
  • 基于窗口菜單的實現:
  • QWidget類中的右鍵菜單函數 QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy) 其可選參數如下:
    • Qt::NoContextMenu:不能實現右鍵菜單
    • Qt::PreventContextMenu:不能實現右鍵菜單
    • Qt::DefaultContextMenu:基于事件處理器函數 QWidget::contextMenuEvent() 實現
    • Qt::ActionsContextMenu:添加到當前窗口中所有 QAction 都會作為右鍵菜單項顯示出來
    • Qt::CustomContextMenu:基于 QWidget::customContextMenuRequested() 信號實現

控件

所有控件的基類都是QWidget(參考窗口類中的QWidget)

按鈕類

在這里插入圖片描述

  • QAbstractButton

    • 按鈕狀態: Normal, Hover, Pressed
    • check屬性
    // 判斷按鈕是否設置了checkable屬性, 如果設置了點擊按鈕, 按鈕一直處于選中狀態(默認這個屬性是關閉的, not checkable)
    bool QAbstractButton::isCheckable() const;// 設置按鈕的checkable屬性:
    // 參數為true: 點擊按鈕, 按鈕被選中, 松開鼠標, 按鈕不彈起
    // 參數為false: 點擊按鈕, 按鈕被選中, 松開鼠標, 按鈕彈起
    void QAbstractButton::setCheckable(bool);// 判斷按鈕是不是被按下的選中狀態
    bool QAbstractButton::isChecked() const;// 設置按鈕的選中狀態: true-選中, false-沒選中(設置該屬性前, 必須先進行 checkable屬性的設置)
    void QAbstractButton::setChecked(bool);
    
    • 信號函數
    /*
    當按鈕被激活時(即,當鼠標光標在按鈕內時按下然后釋放),當鍵入快捷鍵時,或者當click()或animateClick()被調用時,這個信號被發出。值得注意的是,如果調用setDown()、setChecked()或toggle(),則不會觸發此信號。
    */
    [signal] void QAbstractButton::clicked(bool checked = false);
    // 在按下按鈕的時候發射這個信號
    [signal] void QAbstractButton::pressed();
    // 在釋放這個按鈕的時候發射直觀信號
    [signal] void QAbstractButton::released();
    // 每當可檢查按鈕改變其狀態時,就會發出此信號。checked在選中按鈕時為true,在未選中按鈕時為false。
    [signal] void QAbstractButton::toggled(bool checked);
    
    • 槽函數
    // 執行一個動畫點擊:按鈕被立即按下,并在毫秒后釋放(默認是100毫秒)。
    [slot] void QAbstractButton::animateClick(int msec = 100);// 執行一次按鈕點擊, 相當于使用鼠標點擊了按鈕
    [slot] void QAbstractButton::click();// 參考 1.2 中的函數介紹
    [slot] void QAbstractButton::setChecked(bool);
    // 設置按鈕上圖標大小
    [slot]void setIconSize(const QSize &size);
    // 切換可檢查按鈕的狀態。 checked <==> unchecked
    [slot] void QAbstractButton::toggle();
    
    • 關聯菜單(通常在QToolButton子類中使用)
    /*
    將彈出菜單菜單與此按鈕關聯起來。這將把按鈕變成一個菜單按鈕,
    在某些樣式中會在按鈕文本的右邊產生一個小三角形。
    */
    void QPushButton::setMenu(QMenu *menu);/*
    顯示(彈出)相關的彈出菜單。如果沒有這樣的菜單,這個函數什么也不做。
    這個函數直到彈出菜單被用戶關閉后才返回。
    */
    [slot] void QPushButton::showMenu();
    
  • QPushButton

默認按鈕:

// 判斷按鈕是不是默認按鈕
bool isDefault() const;
// 一般在對話框窗口中使用, 將按鈕設置為默認按鈕, 自動關聯 Enter 鍵 
void setDefault(bool);

容器類

在這里插入圖片描述

自定義控件

  • 自定義控件定義:在代碼里寫繼承于基礎控件的類
  • GUI使用自定義控件:將基礎控件拖拽到窗口中,右鍵控件,提升為:寫入提升的類的名稱

事件

事件處理機制

事件是由系統(如鼠標、鍵盤事件)在不同場景下發出的。每個Qt應用程序都對應唯一的QApplication應用對象,程序啟動后,會調用該對象的exec函數,這樣Qt框架內部的事件檢測就開始了。具體為,當事件產生時,事件會經過:事件派發→事件過濾→事件分發→ 事件處理這幾個階段。

注意區分:事件和信號,事件是由系統產生的(事件的發送與傳遞是嵌套容器/控件,由Qt鏈式傳遞的),信號是由Qt對象主動發出的(發送和接收通過connect對兩個對象進行綁定)

  • 事件派發

當事件產生之后,Qt使用用應用程序對象調用notify()函數將事件發送到指定的窗口:

[override virtual] bool QApplication::notify(QObject *receiver, QEvent *e);
  • 事件過濾

事件在發送過程中可以通過事件過濾器進行過濾,默認不對任何產生的事件進行過濾。

// 需要先給窗口安裝過濾器, 該事件才會觸發
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event);
  • 事件分發

當事件發送到指定窗口之后,窗口的事件分發器會對收到的事件進行分發。

[override virtual protected] bool QWidget::event(QEvent *event);
  • 事件處理

事件分發器會將分類之后的事件(鼠標事件、鍵盤事件、繪圖事件…)分發給對應的事件處理器函數進行處理,每個事件處理器函數都有默認的處理動作

事件過濾器

當Qt的事件通過應用程序對象發送給相關窗口之后,窗口接收到數據之前這個期間可對事件進行過濾,過濾掉的事件就不能被繼續處理了。

// 窗口頭文件: mainwindow.h
class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();// 聲明主窗口的eventFilter方法(在窗口的實現類中對該方法進行重寫),該方法用于主窗口對傳遞給它的事件進行過濾// 參數1 watched:要過濾的事件的所有者對象// 參數2 event:要過濾的具體的事件// 返回值 如果想過濾掉這個事件,停止它被進一步處理,返回true,否則返回 falsebool eventFilter(QObject *watched, QEvent *event);private:Ui::MainWindow *ui;
};
// 窗口源文件: mainwindow.cppMainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 給要被過濾事件的類對象安裝事件過濾器:給窗口中的textEdit安裝事件過濾器,由此textEvent可作為watch被監控ui->textEdit->installEventFilter(this);
}MainWindow::~MainWindow()
{delete ui;
}// 重寫窗口的事件過濾器
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{// 判斷對象和事件if(watched == ui->textEdit && event->type() == QEvent::KeyPress){QKeyEvent* keyEv = (QKeyEvent*)event;if(keyEv->key() == Qt::Key_Enter ||         // 小鍵盤確認keyEv->key() == Qt::Key_Return)     // 大鍵盤回車{qDebug() << "我是回車, 被按下了...";return true;}}return false;
}

事件傳遞順序:如容器A 包含了 容器B 再包含 容器C,容器C包含了控件,這樣的嵌套結構。其鼠標點擊控件的事件傳遞順序為:控件→ 容器C → 容器B → 容器A(由內到外)。上述在控件installEventFilter,在容器中重寫EventFilter,實際是事件先到達控件,但發現控件注冊了容器的事件過濾器,然后系統會先到容器的事件過濾器進行檢查(而非事件先到容器,從外到內)

如果一個對象安裝了多個事件過濾器,那么最后安裝的一個會最先執行。

**注意:**事件過濾器和被安裝過濾器的組件必須在同一線程,否則,過濾器將不起作用。另外,如果在安裝過濾器之后,這兩個組件到了不同的線程,那么,只有等到二者重新回到同一線程的時候過濾器才會有效。

事件分發器

事件分發器,是QWidget窗體基類的成員方法[override virtual protected] bool QWidget::event(QEvent *event);,該函數接受一個QEvent類型的事件參數,該QEvent類具有向外傳遞(忽略)setAccepted(false) == ignore()和接收(自己處理,不再向外傳遞)setAccepted(true) == accept()

  • 事件分發

在無人為干預的情況下,事件分發器會自主完成事件的分發,如下部分源碼:

bool QWidget::event(QEvent *ev)
{switch(ev->type()){// 鼠標移動case QEvent::MouseMove:		mouseMoveEvent((QMouseEvent*)event);break;// 鼠標按下case QEvent::MouseButtonPress:	mousePressEvent((QMouseEvent*)event);break;// 鼠標釋放case QEvent::MouseButtonRelease:	mouseReleaseEvent((QMouseEvent*)event);break;// 鼠標雙擊case QEvent::MouseButtonDblClick:	mouseDoubleClickEvent((QMouseEvent*)event);break;// 鍵盤按鍵被按下事件case QEvent::KeyPress:break;.........default:break;}
}
  • 事件攔截/過濾

通過重寫該QWidget的成員函數即可,根據QEvent傳入參數的類型進行判斷,然后選擇接收還是忽略,以做到對事件的攔截和過濾

bool MainWindow::event(QEvent *ev)
{if(ev->type() == QEvent::MouseButtonPress || ev->type() == QEvent::MouseButtonDblClick){// 過濾調用鼠標按下的事件return true;}// 注意:對于這兩個事件以外的其他事件是沒有任何影響的,因為在重寫的事件分發器函數的最后調用了父類的事件分發器函數return QWidget::event(ev);
}

事件處理器

事件處理器位于Qt事件機制的末端,即事件的回調函數

  • 一些事件處理器
// 1.鼠標事件
// 1.1鼠標按下:當鼠標左鍵、鼠標右鍵、鼠標中鍵被按下,該函數被自動調用,通過參數可以得到當前按下的是哪個鼠標鍵
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 1.2鼠標釋放:當鼠標左鍵、鼠標右鍵、鼠標中鍵被釋放,該函數被自動調用,通過參數可以得到當前釋放的是哪個鼠標鍵
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 1.3鼠標移動:當鼠標移動(也可以按住一個或多個鼠標鍵移動),該函數被自動調用,通過參數可以得到在移動過程中哪些鼠標鍵被按下了
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
// 1.4鼠標雙擊:當鼠標雙擊該函數被調用,通過參數可以得到是通過哪個鼠標鍵進行了雙擊操作
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event);
// 1.5鼠標進入:當鼠標進入窗口的一瞬間,觸發該事件,注意:只在進入的瞬間觸發一次該事件
[virtual protected] void QWidget::enterEvent(QEvent *event);
// 1.6鼠標離開:當鼠標離開窗口的一瞬間,觸發該事件,注意:只在離開的瞬間觸發一次該事件// 2.鍵盤事件
// 2.1鍵盤按下:當鍵盤上的按鍵被按下了,該函數被自動調用,通過參數可以得知按下的是哪個鍵
[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event);
// 2.2鍵盤釋放:當鍵盤上的按鍵被釋放了,該函數被自動調用,通過參數可以得知釋放的是哪個鍵
[virtual protected] void QWidget::keyReleaseEvent(QKeyEvent *event);// 3.窗口繪制事件
// 3.1窗口重繪:當窗口需要刷新的時候,該函數就會自動被調用。窗口需要刷新的情景很多,比如:窗口大小發生變化,窗口顯示等
[virtual protected] void QWidget::paintEvent(QPaintEvent *event);
// 3.2窗口關閉
[virtual protected] void QWidget::closeEvent(QCloseEvent *event);
// 3.3重置窗口大小
[virtual protected] void QWidget::resizeEvent(QResizeEvent *event);
  • 重新事件處理函數
// 窗體頭文件
#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();protected:// 重寫事件處理器函數void closeEvent(QCloseEvent* ev);private:Ui::MainWindow *ui;
};
// 窗體源文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCloseEvent>
#include <QMessageBox>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::closeEvent(QCloseEvent *ev)
{QMessageBox::Button btn = QMessageBox::question(this, "關閉窗口", "您確定要關閉窗口嗎?");if(btn == QMessageBox::Yes){// 接收并處理這個事件ev->accept();}else{// 忽略這個事件ev->ignore();}
}

多線程

  • 主線程

默認的線程在Qt中稱之為窗口線程,也叫主線程,負責窗口事件處理或者窗口控件數據的更新

  • 子線程

子線程負責后臺的業務邏輯處理,子線程中不能對窗口對象做任何操作,這些事情需要交給窗口線程處理

  • 線程通信

主線程和子線程之間如果要進行數據的傳遞,需要使用Qt中的信號槽機制

QThread線程類

  • 常用成員函數
// 構造函數
QThread::QThread(QObject *parent = Q_NULLPTR);// 判斷線程中的任務是不是處理完畢了
bool QThread::isFinished() const;// 判斷子線程是不是在執行任務
bool QThread::isRunning() const;// 得到當前線程的優先級
Priority QThread::priority() const;// 設置Qt中線程的優先級
void QThread::setPriority(Priority priority);
// QThread::IdlePriority         // --> 最低的優先級
// QThread::LowestPriority
// QThread::LowPriority
// QThread::NormalPriority
// QThread::HighPriority
// QThread::HighestPriority
// QThread::TimeCriticalPriority // --> 最高的優先級
// QThread::InheritPriority      // --> 子線程和其父線程的優先級相同, 默認是這個// 退出線程, 停止底層的事件循環
void QThread::exit(int returnCode = 0);// 調用線程退出函數之后, 線程不會馬上退出因為當前任務有可能還沒有完成, 調回用這個函數是;等待任務完成, 然后退出線程, 一般情況下會在 exit() 后邊調用這個函數
bool QThread::wait(unsigned long time = ULONG_MAX);
  • 信號槽
// 和調用 exit() 效果是一樣的 調用這個函數之后, 再調用 wait() 函數
[slot] void QThread::quit();
// 啟動子線程
[slot] void QThread::start(Priority priority = InheritPriority);
// 線程退出, 可能是會馬上終止線程, 一般情況下不使用這個函數
[slot] void QThread::terminate();// 線程中執行的任務完成了, 發出該信號
[signal] void QThread::finished();
// 開始工作之前發出這個信號
[signal] void QThread::started();
  • 靜態函數
// 返回一個指向管理當前執行線程的QThread的指針
[static] QThread *QThread::currentThread();
// 返回可以在系統上運行的理想線程數 == 和當前電腦的 CPU 核心數相同
[static] int QThread::idealThreadCount();
// 線程休眠函數
[static] void QThread::msleep(unsigned long msecs);	// 單位: 毫秒
[static] void QThread::sleep(unsigned long secs);	// 單位: 秒
[static] void QThread::usleep(unsigned long usecs);	// 單位: 微秒
  • 任務執行函數
// 子線程要處理什么任務, 需要寫到 run() 中
[virtual protected] void QThread::run();

run()是一個虛函數,如果想讓創建的子線程執行某個任務,需要寫一個子類讓其繼承QThread,并且在子類中重寫父類的run()方法,函數體就是對應的任務處理流程。另外,這個函數是一個受保護的成員函數,不能夠在類的外部調用,如果想要讓線程執行這個函數中的業務流程,需要通過當前線程對象調用槽函數start()啟動子線程,當子線程被啟動,這個run()函數也就在線程內部被調用了。

  • 使用方法1
// 自定義線程類頭文件
#ifndef MYTHREAD_H
#define MYTHREAD_H#include <QThread>class MyThread : public QThread
{Q_OBJECT
public:explicit MyThread(QObject *parent = nullptr);protected:void run();signals:// 自定義信號, 給主線程,傳遞數據(線程通信)void curNumber(int num);public slots:
};#endif // MYTHREAD_H
// 自定義線程類的實現文件
#include "mythread.h"
#include <QDebug>MyThread::MyThread(QObject *parent) : QThread(parent)
{}void MyThread::run()
{qDebug() << "當前線程對象的地址: " << QThread::currentThread();int num = 0;while(1){emit curNumber(num++);if(num == 10000000){break;}QThread::usleep(1);}qDebug() << "run() 執行完畢, 子線程退出...";
}
// 主窗體實現類
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mythread.h"
#include <QDebug>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);qDebug() << "主線程對象地址:  " << QThread::currentThread();// 創建子線程MyThread* subThread = new MyThread;connect(subThread, &MyThread::curNumber, this, [=](int num){ui->label->setNum(num);});// 啟動自定義線程subThread->start();
}MainWindow::~MainWindow()
{delete ui;
}

該使用方法的缺點:假設要在一個子線程中處理多個任務,所有的處理邏輯都需要寫到run()函數中,這樣該函數中的處理邏輯就會變得非常混亂,不太容易維護。

  • 使用方法2

彌補使用方法1的缺陷,創建多個類似于業務流程類的類,將業務流程放多類的公共成員函數中,然后將這個業務類的實例對象移動到對應的子線程中moveToThread()就可以了,這樣可以讓編寫的程序更加靈活,可讀性更強,更易于維護。

// 業務流程類的頭文件
#ifndef MYWORK_H
#define MYWORK_H#include <QObject>class MyWork : public QObject
{Q_OBJECT
public:explicit MyWork(QObject *parent = nullptr);// 工作函數:在子線程中實際執行任務的函數void working();signals:void curNumber(int num);
};#endif // MYWORK_H
// 業務流程類的實現
#include "mywork.h"
#include <QDebug>
#include <QThread>MyWork::MyWork(QObject *parent) : QObject(parent)
{}void MyWork::working()
{qDebug() << "當前線程對象的地址: " << QThread::currentThread();int num = 0;while(1){emit curNumber(num++);if(num == 10000000){break;}QThread::usleep(1);}qDebug() << "run() 執行完畢, 子線程退出...";
}
// 窗體實現函數:主線程創建并執行子線程
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include "mywork.h"
#include <QDebug>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);qDebug() << "主線程對象的地址: " << QThread::currentThread();// 創建線程對象QThread* sub = new QThread;// 創建工作的類對象// 千萬不要指定給創建的對象指定父對象如果指定了: QObject::moveToThread: Cannot move objects with a parent// MyWork* work = new MyWork(this); 錯誤MyWork* work = new MyWork;// 將工作的類對象移動到創建的子線程對象中work->moveToThread(sub);// 綁定信號槽connect(work, &MyWork::curNumber, this, [=](int num){ui->label->setNum(num);});// 啟動線程sub->start();// 讓工作的對象開始工作, 開始工作work.working();
}MainWindow::~MainWindow()
{delete ui;
}

線程池

  • 線程池組成和原理

主要組成:任務隊列:存儲需要處理任務的隊列(M個任務);工作線程(任務隊列的消費者,N個線程);管理線程者(對任務隊列和工作線程進行管理:接收、拒絕新任務,任務優先級排序;線程添加和銷毀,1個線程)

在這里插入圖片描述

  • 線程池任務QRunnable(待執行的任務)

在Qt中使用線程池需要先創建任務,添加到線程池中的每一個任務都需要是一個QRunnable類型,因此在程序中需要創建子類繼承QRunnable這個類,然后重寫 run() 方法,在這個函數中編寫要在線程池中執行的任務,并將這個子類對象傳遞給線程池,這樣任務就可以被線程池中的某個工作的線程處理掉了。

// 在子類中必須要重寫的函數, 里邊是任務的處理流程
[pure virtual] void QRunnable::run();// 參數設置為 true: 任務對象處理完畢后會自動銷毀;false: 任務對象處理完畢后需要手動銷毀
void QRunnable::setAutoDelete(bool autoDelete);
// 獲取當前任務對象的析構方式,返回true->自動析構, 返回false->手動析構
bool QRunnable::autoDelete() const;
  • 線程池QThreadPool

Qt中的 QThreadPool 類管理了一組 QThreads, 里邊還維護了一個任務隊列。QThreadPool 管理和回收各個 QThread 對象,以幫助減少使用線程的程序中的線程創建成本。每個Qt應用程序都有一個全局 QThreadPool 對象,可以通過調用 globalInstance() 來訪問它。也可以單獨創建一個 QThreadPool 對象使用。(一般情況下,我們不需要在Qt程序中創建線程池對象,直接使用Qt為每個應用程序提供的線程池全局對象即可)

// 獲取和設置線程中的最大線程個數
int maxThreadCount() const;
void setMaxThreadCount(int maxThreadCount);// 給線程池添加任務, 任務是一個 QRunnable 類型的對象,如果線程池中沒有空閑的線程了, 任務會放到任務隊列中, 等待線程處理
void QThreadPool::start(QRunnable * runnable, int priority = 0);// 如果線程池中沒有空閑的線程了, 直接返回值, 任務添加失敗, 任務不會添加到任務隊列中
bool QThreadPool::tryStart(QRunnable * runnable);// 線程池中被激活的線程的個數(正在工作的線程個數)
int QThreadPool::activeThreadCount() const;// 嘗試性的將某一個任務從線程池的任務隊列中刪除, 如果任務已經開始執行就無法刪除了
bool QThreadPool::tryTake(QRunnable *runnable);
// 將線程池中的任務隊列里邊沒有開始處理的所有任務刪除, 如果已經開始處理了就無法通過該函數刪除了
void QThreadPool::clear();// 在每個Qt應用程序中都有一個全局的線程池對象, 通過這個函數直接訪問這個對象
static QThreadPool * QThreadPool::globalInstance();
  • 使用示例
// 任務類聲明// 如果需要在這個任務中使用Qt的信號槽機制進行數據的傳遞就必須繼承QObject這個類,如果不使用信號槽傳遞數據就可以不繼承了,只繼承QRunnable即可
class MyWork  public QObject, :public QRunnable
{Q_OBJECT
public:explicit MyWork();~MyWork();void run() override;
}
// 任務類實現MyWork::MyWork() : QRunnable()
{// 任務執行完畢,該對象自動銷毀setAutoDelete(true);
}
void MyWork::run()
{// 業務處理代碼
}
// 窗口實現類,使用線程池MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);// 線程池初始化,設置最大線程池數QThreadPool::globalInstance()->setMaxThreadCount(4);// 添加任務MyWork* task = new MyWork;QThreadPool::globalInstance()->start(task);    
}

得到線程池對象之后,調用start()方法就可以將一個任務添加到線程池中,這個任務就可以被線程池內部的線程池處理掉了,使用線程池比自己創建線程的這種多種多線程方式更加簡單和易于維護。

網絡編程

基于TCP套接字的網絡通信

QTcpServer、QTcpSocket

基于WebSocket的雙向網絡通信

QWebSocket、QWebSocketServer

數據持久化

數據庫插件與操作
JSON
說明
QJsonDocument封裝了一個完整的JSON文檔,并可以從UTF-8編碼的基于文本的表示以及Qt自己的二進制格式讀取和寫入該文檔
QJsonArrayJSON數組是一個值列表。可以通過從數組中插入和刪除QJsonValue來操作該列表
QJsonObjectJSON對象是鍵值對的列表,其中鍵是唯一的字符串,值由QJsonValue表示
QJsonValue該類封裝了JSON支持的數據類型

程序發布與打包

發布

在VS中編譯的可執行文件離開開發環境不能獨立運行,要使它能夠獨立運行則需要進行發布(發布工具包含在上面下載安裝Qt 的MSVC工具鏈中)將可執行程序放在某一空目錄中,并執行下面的命令

# Qt6之前的版本
windeployqt --release --compiler-runtime {程序名}.exe 
# Qt6版本
windeployqt6 --release --compiler-runtime {程序名}.exe 

打包

NIS Edit、Inno Setup

C++語法補充

Lambda表達式

Lambda表達式是 C++ 11 最重要也是最常用的特性之一,是現代編程語言的一個特點,簡潔,提高了代碼的效率并且可以使程序更加靈活,Qt是完全支持c++語法的, 因此在Qt中也可以使用Lambda表達式。其本質就是一個匿名函數

  • 捕獲列表
列表項解釋
[]不捕捉任何變量
[&]捕獲外部作用域中所有變量, 并作為引用在函數體內使用 (按引用捕獲)
[=]捕獲外部作用域中所有變量, 并作為副本在函數體內使用 (按值捕獲),拷貝的副本在匿名函數體內部是只讀的
[=, &foo]按值捕獲外部作用域中所有變量, 并按照引用捕獲外部變量 foo
[bar]按值捕獲 bar 變量, 同時不捕獲其他變量
[&bar]按引用捕獲 bar 變量, 同時不捕獲其他變量
[this]捕獲當前類中的this指針;讓lambda表達式擁有和當前類成員函數同樣的訪問權限;如果已經使用了 & 或者 =, 默認添加此選項
  • 參數列表:和普通函數的參數列表一樣
  • opt 選項(可省略)
    • mutable: 可以修改按值傳遞進來的拷貝(注意是能修改拷貝,而不是值本身)
    • xception: 指定函數拋出的異常,如拋出整數類型的異常,可以使用throw();
  • 返回值類型:
  • 標識函數返回值的類型,當返回值為void,或者函數體中只有一處return的地方(此時編譯器可以自動推斷出返回值類型)時,可省略
  • 函數體
// 定義:不會被調用
[](){qDebug() << "hello, 我是一個lambda表達式...";
};// 調用
[](){qDebug() << "hello, 我是一個lambda表達式...";
}();

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98727.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98727.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98727.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

機器學習實戰(一): 什么是機器學習

機器學習&#xff1a;讓機器學會思考的魔法前言 在當今數字化的浪潮中&#xff0c;人工智能無疑是最引人注目的技術之一&#xff0c;而機器學習正是其核心驅動力。它不再是科幻電影中的遙遠設想&#xff0c;而是已經滲透到我們日常生活的方方面面&#xff0c;從智能推薦到自動駕…

java流水號生成方式

1、基于時間戳生成流水號利用當前時間戳生成流水號&#xff0c;可以確保唯一性。通過格式化時間戳&#xff0c;可以生成固定位數的流水號。SimpleDateFormat sdf new SimpleDateFormat("yyyyMMddHHmmssSSS"); String serialNumber sdf.format(new Date());特點&…

前端工具大全:前端開發工具、前端調試工具、前端性能優化工具與構建工具的對比與最佳實踐

在現代前端開發中&#xff0c;工具鏈已經成為開發效率與代碼質量的關鍵。無論是 編輯器與 IDE、構建與打包工具、調試工具 還是 性能優化工具&#xff0c;每一個環節都有成熟的解決方案。 然而&#xff0c;工具太多也容易讓團隊選擇困難&#xff1a;該選 VS Code 還是 WebStorm…

ABAP 使用ECHARTS實現圖表展示

最近發現ECHARTS可以整合到SAP中的開源項目&#xff0c;可以絲滑的在SAP中展示各種圖表&#xff0c;還是相當驚艷的。 ECHARTS官方網站&#xff1a;https://echarts.apache.org/examples/zh/index.html 今天順手在開發環境成功安裝了&#xff0c;做下記錄&#xff1a; 1、ABA…

hot100-貪心算法(附圖解思路)

貪心算法的核心&#xff0c;就是用局部最優去代替全局最優。一般的步驟就是去試思路&#xff0c;然后舉反例&#xff0c;如果舉不出反例&#xff0c;基本可以看作是正確的方法。121. 買賣股票的最佳時機&#xff08;Best Time to Buy and Sell Stock&#xff09;難度&#xff1…

從齒輪到智能:機器人如何重塑我們的世界【科普類】

新晉碼農一枚&#xff0c;小編會定期整理一些寫的比較好的代碼和知識點&#xff0c;作為自己的學習筆記&#xff0c;試著做一下批注和補充&#xff0c;轉載或者參考他人文獻會標明出處&#xff0c;非商用&#xff0c;如有侵權會刪改&#xff01;歡迎大家斧正和討論&#xff01;…

python超市購物 2025年6月電子學會python編程等級考試一級真題答案解析

python超市購物 2025年6月 python編程等級考試一級真題 博主推薦 所有考級比賽學習相關資料合集【推薦收藏】 1、Python比賽 信息素養大賽Python編程挑戰賽 藍橋杯python選拔賽真題詳解

淺談代理流程自動化 (APA)

一、什么是APA Agentic Process Automation (APA)APA 利用大型語言模型 &#xff08;LLM&#xff09; 自動執行復雜的動態工作流程。它可以自主構建、執行和調整工作流程&#xff0c;同時將人員干預降至最低。與依賴基于規則的系統的傳統機器人流程自動化 &#xff08;RPA&…

LeetCode - 和為K的子數組 / 爬樓梯

?歡迎光臨小站&#xff1a;致橡樹 和為K的子數組 給你一個整數數組 nums 和一個整數 k &#xff0c;請你統計并返回 該數組中和為 k 的子數組的個數 。 子數組是數組中元素的連續非空序列。 示例 1&#xff1a; 輸入&#xff1a;nums [1,1,1], k 2 輸出&#xff1a;2示例…

day40 SQLite3單詞查詢程序設計與實現

day40 SQLite3單詞查詢程序設計與實現 核心知識點 SQLite3 C接口應用&#xff1a;使用sqlite3_open、sqlite3_exec等函數操作數據庫回調函數機制&#xff1a;通過回調函數處理查詢結果集SQL語句構建&#xff1a;動態生成SELECT、INSERT等SQL語句事務處理&#xff1a;使用BEGIN …

GitHub 熱榜項目 - 日榜(2025-09-08)

GitHub 熱榜項目 - 日榜(2025-09-08) 生成于&#xff1a;2025-09-08 統計摘要 共發現熱門項目&#xff1a;17 個 榜單類型&#xff1a;日榜 本期熱點趨勢總結 本期GitHub熱榜呈現三大技術趨勢&#xff1a;AI智能體與LLM應用持續爆發&#xff08;emcie-co/parlant、coleam00…

設計模式-工廠方法原型模板方法外觀

設計模式概述 - 工廠方法 & 原型 & 模板方法 & 外觀 工廠方法模式簡述 工廠方法模式&#xff08;Factory Method Pattern&#xff09;是一種創建型設計模式&#xff0c;它定義了一個用于創建對象的接口&#xff0c;但由子類決定實例化哪個類。工廠方法將類的實例化…

推動檢測認證行業邁向智能化 AITIC一體機發布會在京舉辦

來源&#xff1a;新華社客戶端國家市場監督管理總局認證認可技術研究中心(簡稱“認研中心”)近日聯合技術合作伙伴在北京舉辦AITIC軟硬件一體機發布會。據了解&#xff0c;“AITIC一體機”是專為檢測認證行業設計的智能硬件&#xff0c;提供低成本的本地化部署方案&#xff0c;…

權限即數據:企業系統中的字段級訪問控制架構實戰(β=0.6)

摘要 這篇文章介紹了一個企業系統中的字段權限解析方案&#xff0c;通過規則表與命中記錄表&#xff08;biz_rule_hit&#xff09;聯動&#xff0c;實現對業務數據的動態權限控制。流程包括替換用戶上下文變量、記錄命中規則、查詢業務數據并關聯命中信息&#xff0c;最終在內存…

Python爬蟲實戰:研究Specialty Plots模塊,構建空氣質量監測數據采集和分析系統

1. 引言 1.1 研究背景 隨著全球城市化進程的加速和工業的快速發展,空氣質量問題已成為影響人類健康和生態環境的重要因素。世界衛生組織數據顯示,全球超過 90% 的人口生活在空氣質量超標的環境中,空氣污染每年導致約 700 萬人過早死亡。準確、及時地獲取和分析空氣質量數據…

字典樹算法

一、什么是Trie&#xff1f; Trie&#xff08;發音為"try"&#xff09;&#xff0c;也稱為字典樹、前綴樹&#xff0c;是一種多叉樹結構&#xff0c;專門用于高效存儲和檢索字符串集合。其核心特點是共享字符串的公共前綴&#xff0c;從而大幅減少冗余存儲&#xff0…

Laya使用VideoNode動態加載視頻,可以自定義播放視頻此處以及位置

export class VideoCommand {video: Laya.VideoNode;public duration: number 0;/*** param videoPos 視頻位置* param videoSize 視頻大小*/public constructor(videoPos: Laya.Vector2, videoSize: Laya.Vector2) {this.video new Laya.VideoNode;//添加到舞臺 1是場景中的…

yum localinstall安裝本地包

yum localinstall 是一個用于安裝本地 RPM 包并自動處理依賴關系的命令。當你有一個或多個本地的 RPM 包需要安裝,又希望 yum 能幫你解決可能存在的依賴問題時,這個命令就非常有用。下面我會詳細解釋它的用法和注意事項。 ??? 命令基本用法 yum localinstall 命令的基本…

LeetCode 面試經典 150 題:輪轉數組(三次翻轉法詳解 + 多解法對比)

在數組類算法題中&#xff0c;“輪轉數組” 是一道考察 “原地操作” 與 “邏輯轉換” 能力的經典題目。所謂 “輪轉”&#xff0c;是指將數組元素向右移動指定步數&#xff0c;且超出數組長度的元素需 “循環” 到數組開頭。這道題的最優解 ——三次翻轉法&#xff0c;能以 O …

網絡編程---TCP

1.TCP&#xff1a;傳輸控制協議&#xff0c;位于傳輸層2.TCP的特性&#xff1a;a.使用流式套接字&#xff0c;數據連續&#xff0c;有順序b.TCP是可靠傳輸&#xff0c;有有應答機制ACK&#xff0c;即收到數據后會明確告知發送方已收到數據&#xff1b;若發送方沒有在預計時間收…