【Qt6】列表模型——便捷類型

来源:https://www.cnblogs.com/tcjiaan/archive/2023/09/17/17707380.html
-Advertisement-
Play Games

前一篇水文中,老周演示了 QAbstractItemModel 抽象類的繼承方法。其實,在 Qt 的庫裡面,QAbstractItemModel 類也派生了兩個基類,能讓開發者繼承起來【稍稍】輕鬆一些。 這兩個類是 QAbstractListModel 和 QAbstractTableModel。 ...


前一篇水文中,老周演示了 QAbstractItemModel 抽象類的繼承方法。其實,在 Qt 的庫裡面,QAbstractItemModel 類也派生了兩個基類,能讓開發者繼承起來【稍稍】輕鬆一些。

這兩個類是 QAbstractListModel 和 QAbstractTableModel。

  • QAbstractListModel類專門用來實現一維列表結構模型的。它實現了 index、parent 等方法,並且把 columnCount 方法變成了私有成員(一維列表不需要它)。繼承時直接實現 rowCount、data、setData 這幾個方法即可;
  • QAbstractTableModel類專門用來實現二維表結構的模型。它實現了 index、parent 等方法。繼承時咱們要實現 rowCount、columnCount、data、setData 等方法。

雖然它幫咱們實現了一些成員,但實際上也省不了多功夫的。下麵咱們用 QAbstractListModel 舉例,和上篇中的一樣,操作一個 QList<int> 數據。畢竟一維的比較簡單,演示出來大伙都容易懂。

// 頭文件
#ifndef LIST_H
#define LIST_H

#include <QAbstractListModel>
#include <QList>

class CustListModel: public QAbstractListModel
{
    Q_OBJECT

public:
    explicit CustListModel(QObject* parent = nullptr);
    ~CustListModel();

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);

    bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;

    Qt::ItemFlags flags(const QModelIndex &index) const override;
private:
    // 私有,存數據用
    QList<int> m_list;
};

#endif

先弄 rowCount 方法,返回總行數,這個簡單一點。

int CustListModel::rowCount(const QModelIndex &parent) const
{
    if(parent.isValid())
    {
        return 0;
    }
    return m_list.size();
}

實現 data 方法,獲取數據時用。

QVariant CustListModel::data(const QModelIndex &index, int role) const
{
    if( role == Qt::DisplayRole || role == Qt::EditRole)
    {
        if(!index.isValid())
        {
            return QVariant();
        }
        // 獲取索引
        int i = index.row();
        // 返回指定索引處的值
        return m_list.at(i);
    }
    return QVariant();
}

要返回 QList<T> 中指定的元素,用 at 方法,傳遞索引給它即可。

實現 setData 方法,編輯結束後用於更新數據的。

bool CustListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    if(role == Qt::EditRole || role == Qt::DisplayRole)
    {
        // 解包數據
        bool ok;
        int val = value.toInt(&ok);
        if(ok)
        {
            // 設置值
            m_list.replace(index.row(), val);

              // 發出信號
              emit dataChanged(index,index,{role});

return true;
        }
    }
    return false;
}

要修改某個索引處的值,用 replace 方法替換。

實現 flags 方法,表明該模型的列表項支持交互、編輯、被選擇。

Qt::ItemFlags CustListModel::flags(const QModelIndex &index) const
{
    return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
}

ItemIsEnabled 表明列表項是活動的,用戶可操作的;ItemIsEditable 表明列表項可編輯;ItemIsSelectable 表示列表項可以選擇。

 

下麵這兩個方法是有點麻煩的,這裡先介紹一下。

bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());

row 參數表示要插入元素的索引,count 表示插入元素個數,插入元素後,原來的元素向後移動。例如:A、B、C、D,現在我要在B處插入兩個元素。那麼 row = 1,count = 2,B 處放入 E、F,B、C、D 向後移,變成 A、E、F、B、C、D。

然後,刪除元素的方法也類似。

bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());

row 是要刪除的索引,count 是連續要刪掉的元素個數。

insertRows 和 removeRows 方法的返回值含義相同:成功返回 true,失敗返回 false。

好,現在上代碼。

bool CustListModel::insertRows(int row, int count, const QModelIndex &parent)
{
    if(parent.isValid())
        return false;
    // 註意這裡!!
    beginInsertRows(parent, row, row + count - 1);
    // 開始插入
    m_list.insert(row, count, 0);
    // 註意這裡!!
    endInsertRows();
    return true;
}

bool CustListModel::removeRows(int row, int count, const QModelIndex &parent)
{
    if(parent.isValid())
        return false;
    // 註意這裡!!!
    beginRemoveRows(parent, row, row + count - 1);
    // 刪除
    m_list.remove(row, count);
    // 註意這裡!!!
    endRemoveRows();
    return true;
}

在插入數據前必須調用 beginInsertRows 方法。註意這個方法的參數含義和 insertRows 有些不一樣。

void beginInsertRows(const QModelIndex &parent, int first, int last)

insertRows 方法是指定開始索引 ,然後連續加入多少個元素,而 beginInsertRows 方法是你插入元素後,它們在列表中的開始索引和結束索引。如 A、B、C,在B處插入兩個元素。開始索引是 1,結束索引是 2,即結束索引的計算方法是: endIndex = startIndex + count -1。插入元素結束後必須調用 endInsertRows 方法。這一對方法的作用是讓用戶界面上的視圖(如 QListView、QTableView 等)能及時做出響應。

同理,在刪除元素時,beginRemoveRows 和 endRemoveRows 方法也必須調用。beginRemoveRows 方法的參數與 beginInsertRows 方法一樣,也是索引的起止值。即要刪除元素的起始索引,和被刪除的最後一個元素的索引。如1、2、3、4,要刪除2、3、4,那麼起始索引是 1,結束索引 3。

模型已完工,下麵做個界面試一試。

int main(int argc, char** argv)
{
    QApplication myapp(argc, argv);
    // 視窗
    QWidget *window = new QWidget;
    // 佈局
    QGridLayout *layout = new QGridLayout;
    window->setLayout(layout);
    // 列表視圖
    QListView* view = new QListView(window);
    // 實例化模型
    CustListModel* model = new CustListModel(window);
    // 設置到視圖中
    view->setModel(model);
    layout->addWidget(view, 0, 0, 3, 1);

    // 按鈕
    QPushButton* btnAdd=new QPushButton("新增", window);
    QPushButton* btnDel = new QPushButton("移除", window);
    QPushButton* btnShowAll = new QPushButton("顯示列表", window);
    layout->addWidget(btnAdd, 0, 1);
    layout->addWidget(btnDel, 1, 1);
    layout->addWidget(btnShowAll, 2, 1);
    layout->setColumnStretch(0, 1);
    // 連接clicked信號
    QObject::connect(
        btnAdd,
        &QPushButton::clicked,
        [&view, &model, &window]()
        {
            bool res;
            int val = QInputDialog::getInt(
                window,             //父視窗
                "輸入",              //視窗標題
                "請輸入整數:",       //提示文本
                0,                   //預設值
                0,                  //最小值
                1000,               //最大值
                1,                  //步長值
                &res                 //表示操作結果
            );
            if(!res)
                return;
            // 索引為count
            int i = model->rowCount();
            // 添加一項
            model->insertRow(i);
            // 獲取新索引
            QModelIndex newIndex = model->index(i);
            // 設置此項的值
            model->setData(newIndex, QVariant(val), Qt::DisplayRole);
        }
    );
    QObject::connect(
        btnDel,
        &QPushButton::clicked,
        [&window, &view, &model]()
        {
            // 當前項
            QModelIndex curri = view->currentIndex();
            if(!curri.isValid())
                return;
            // 刪除
            model->removeRow(curri.row());
        }
    );
    QObject::connect(
        btnShowAll,
        &QPushButton::clicked,
        [&model, &window]()
        {
            // 獲取總數
            int c = model->rowCount();
            QString msg;
            for(int x = 0; x < c; x++)
            {
                // 獲取值
                QVariant value = model->data(model->index(x), Qt::DisplayRole);
                if(value.isValid())
                {
                    int n = qvariant_cast<int>(value);
                    msg += QString(" %1").arg(n);
                }
            }
            QMessageBox::information(window,"提示",msg);
        }
    );

    // 標題
    window->setWindowTitle("小型列表");
    // 顯示
    window->show();

    return QApplication::exec();
}

QListView 組件用來顯示模型中的數據。三個按鈕分別用來添加、刪除和顯示列表項。最後一個按鈕顯示模型中的所有數據,它的作用是驗證模型是否工作正常。

“新增”按鈕被點擊後,先通過 QInputDialog.getInt 靜態方法,彈出一個輸入內容的對話框,然後把輸入的值添加到模型中。模型中添加元素用的就是 insertRow 方法——只插入一個元素,它調用了咱們上面實現的 insertRows 方法,只是把 count 參數設置為 1。這裡我實現的是新元素總是追加在列表最後,即新元素的行號等於 rowCount。

刪除元素時,QListView 組件的 currentIndex 方法返回當前選中項的索引。在單選模式下,99.9% 被選中的項是等同於當前項的。多選模式下就不好說了,所以,如果需要,可以訪問 selectionModel 方法,再通過 selectedIndexes 方法獲得被選項的列表。

看看效果。

 

QAbstractTableModel 類的用法也是差不多的,只不過要實現行和列的讀寫。

這兩個類哪怕幫咱們實現了一些代碼,但用起來還是麻煩,要是有不需要繼承、開箱就用的類就會好多了。Qt 還真的提供了這樣的類型。

首先說的是 QStringListModel,這個就是針對 QStringList 類的——字元串列表。QStringList 其實就是 QList<QString>。這個 QStringListModel 模型就是以字元串列表為數據源的。不需要繼承(除非你需要自定義),直接可用。

咱們演練一下。

// 頭文件
#ifndef DEMO_H
#define DEMO_H

#include <QWidget>
#include <QPushButton>
#include <QListView>
#include <QVBoxLayout>

class MyWindow : public QWidget
{
    Q_OBJECT

public:
    explicit MyWindow(QWidget* parent = nullptr);
    ~MyWindow();

private:
    QVBoxLayout* m_layout;
    QPushButton* m_btn;
    QListView* m_view;
    // 用於連接clicked信號
    void onClicked();
};

#endif

三個組件:按鈕被點擊後載入數據;QListView 是視圖組件,顯示數據;QVBoxLayout 是佈局用的,界面元素沿垂直方向排列(縱向)。公共成員就是構造函數和析構函數。析構是空白的,實現構造函數即可。

#include "../include/demo.h"
#include <QStringList>
#include <QStringListModel>

MyWindow::MyWindow(QWidget * parent)
    : QWidget::QWidget(parent)
{
    m_layout = new QVBoxLayout();
    setLayout(m_layout);
    // 按鈕
    m_btn = new QPushButton("載入數據", this);
    m_layout->addWidget(m_btn);
    // 視圖
    m_view = new QListView(this);
    m_layout->addWidget(m_view, 1);
    // 連接信號
    connect(m_btn, &QPushButton::clicked, this, &MyWindow::onClicked);
}

MyWindow::~MyWindow()
{
}

下麵是重點,實現 onClicked 成員方法,構建數據併在視圖中顯示。

void MyWindow::onClicked()
{
    // 創建字元串列表
    QStringList list;
    list << "手榴彈" << "地雷" << "魚雷" << "燃燒彈";
    // 創建模型實例
    QStringListModel *model = new QStringListModel(list, this);
    // 設置給視圖
    m_view->setModel(model);
}

代碼不複雜。QStringList 和我們熟悉的 cout 一樣,可以用“<<”運算符寫入字元串。每一次運算就寫入一個元素,故上述代碼向列表填充了四個元素。接著實例化 QStringListModel 類,傳給 setModel 方法就可以與視圖關聯。這裡實例化模型類最好使用指針類型,可以將 this 傳給構造函數,這樣,當前視窗會管理它的生命周期,你不用擔心堆記憶體沒有釋放。

main 函數就沒什麼了,初始化應用程式,顯示視窗就完事了。

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    MyWindow* win = new MyWindow();
    win->setWindowTitle("示例");
    win->resize(320, 275);
    win->show();

    return QApplication::exec();
}

運行代碼後,點一下按鈕,就能看到字元串列表了。

 

QStringListModel 只針對字元串列表,對於複雜一些的列表項,還是不夠用的。所以咱們要請出下一位表演者—— QStandardItemModel。字面翻譯:標準列表模型。這個模型就不僅僅可以用字元串列表當源了,它可以設置更複雜的數據。比如圖標、顯示文本、背景色、文本顏色等。

為了便於使用,標準列表模型中的“項”由 QStandardItem 封裝。你可以先創建 QStandardItem 實例,設置好各種數據後,再添加進 QStandardItemModel 中。在設置子項,你可以不用 setData,而改用 setItem 方法。setItem 方法調用時只要指出行號、列號(如果是一維列表,調用另一個重載可以忽略列號)即可。

對於行標題、列標題,還有更方便的 setHorizontalHeaderLabels(水平)和 setVerticalHeaderLabels(垂直)方法,只要提供一個字元串列表,就能設置行列標題了。

現在,咱們瞭解一下 QStandardItem 這廝怎麼用,這個很重要,用好標準模型得靠它。它的構造函數傳參允許你指定顯示文本、圖標,或者總共多少行多少列。畢竟它有四個重載:

QStandardItem();
explicit QStandardItem(const QString &text);
QStandardItem(const QIcon &icon, const QString &text);
explicit QStandardItem(int rows, int columns = 1);

當然,可以調用無參數構造函數,然後再調用 set**** 方法來設置各項數據,比如:

  1. setText:設置要顯示的文本,就是你想讓用戶看到的文本;
  2. setIcon:設置圖標。不管是 QTableView 視圖還是 QTreeView 視圖,其實只有第一列才會顯示圖標。說直接點就是其他非第一列的項,你設置了圖標也是呵呵;
  3. setToolTip:設置工具提示。就是當滑鼠懸浮在上面時顯示的文本;
  4. setWhatsThis:設置 What the FK,不,是 What is This 幫助信息;
  5. setFont:設置顯示該項所使用的字體;
  6. setTextAlignment:設置顯示該項時,文本的對齊方式;
  7. setBackground:設置該項的背景顏色;
  8. setForeground:前景色,就是設置文本的顏色;
  9. setStatusTip:顯示在狀態欄上的提示信息(這個好像不常用);
  10. setDragEnabled、setDropEnabled:該項支不支持拖放操作;
  11. setAutoTristate:樹形視圖時用得上,比如父節點被 check 後,是否所有子節點也跟著 check;
  12. setCheckable:該項是否顯示一個 CheckBox 框,讓用戶可以在那裡勾來勾去;
  13. setSelectable:該項允許用戶選擇嗎;
  14. setEnabled:若是 true,用戶可以操作該項,如選中它;如為 false,此項為禁用狀態;
  15. setEditable:該項允許編輯嗎。

你要是覺得 setEnabled、setEditable、setSelectable 這些方法麻煩,可以用 setFlags 方法一次性解決,用 Or 運算組合的 Qt::ItemFlags 枚舉來設置。

當然,還有些成員我沒列出,看著那麼多成員方法好像很複雜,其實我們不一定全調用一遍的,看需要,不需要的可以不管的。

下麵咱們也弄個例子。這個例子,我直接從 QTableView 類派生。

// 頭文件

#ifndef CUST_H
#define CUST_H

#include<qwidget.h>
#include<qstandarditemmodel.h>
#include <qtableview.h>
#include <qcolor.h>

class MyTableView : public QTableView
{
    Q_OBJECT

public:
    explicit MyTableView(QWidget* parent = nullptr);
    ~MyTableView();

private:
    QStandardItemModel *model;
};

#endif

私有成員是模型對象,然後,咱們實際要做的是實現構造函數,實例化模型,再往裡面塞數據。

MyTableView::MyTableView(QWidget *parent)
    : QTableView(parent)
{
    // 實例化模型類
    model = new QStandardItemModel(this);
    // 五行三列
    // model->setRowCount(5);
    // model->setColumnCount(3);
    // 設置列標題
    model->setHorizontalHeaderLabels({"學號", "姓名", "分數"});
    // 好,我們開始準備數據
    // 第一行
    QStandardItem *cell00 = new QStandardItem(QIcon("a.png"), "2572");
    QStandardItem *cell01 = new QStandardItem("小李");
    QStandardItem *cell02 = new QStandardItem("58");
    // 不合格,讓它顯示黃色
    cell02->setBackground(QColor("yellow"));
    cell02->setForeground(QColor("red"));
    // 設置到模型中
    model->setItem(0, 0, cell00);
    model->setItem(0, 1, cell01);
    model->setItem(0, 2, cell02);
    // 第二行
    QStandardItem *cell10 = new QStandardItem(QIcon("a.png"), "2055");
    QStandardItem *cell11 = new QStandardItem("小陳");
    QStandardItem *cell12 = new QStandardItem("85");
    model->setItem(1, 0, cell10);
    model->setItem(1, 1, cell11);
    model->setItem(1, 2, cell12);
    // 第三行
    QStandardItem *cell20 = new QStandardItem(QIcon("a.png"), "1069");
    QStandardItem *cell21 = new QStandardItem("小杜");
    QStandardItem *cell22 = new QStandardItem("70");
    model->setItem(2, 0, cell20);
    model->setItem(2, 1, cell21);
    model->setItem(2, 2, cell22);
    // 第四行
    QStandardItem *cell30 = new QStandardItem(QIcon("a.png"), "2469");
    QStandardItem *cell31 = new QStandardItem("小王");
    QStandardItem *cell32 = new QStandardItem("100");
    // 滿分,給他點獎勵
    cell32->setBackground(QColor("blue"));
    cell32->setForeground(QColor("white"));
    model->setItem(3, 0, cell30);
    model->setItem(3, 1, cell31);
    model->setItem(3, 2, cell32);
    // 第五行
    QStandardItem *cell40 = new QStandardItem(QIcon("a.png"), "6394");
    QStandardItem *cell41 = new QStandardItem("小張");
    QStandardItem *cell42 = new QStandardItem("89");
    model->setItem(4, 0, cell40);
    model->setItem(4, 1, cell41);
    model->setItem(4, 2, cell42);

    // 設置模型
    setModel(model);
}

MyTableView::~MyTableView()
{
}

setRowCount、setColumnCount 方法可以不調用,模型會根據你放的數據調整。有兩點得註意:

1、這裡每個 QStandardItem 代表的是一個單元格的數據,而不是一行;

2、QStandardItem 類型的變數要聲明為指針類型,並且要在堆上分配;不能用棧分配,會顯示空白(提前析構了)。

然後,main 函數就更簡單了。

#include <QApplication>
#include "cust.h"

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    MyTableView *win = new MyTableView();
    win->setWindowTitle("月考結果");
    win->resize(300, 260);
    win->show();

    return QApplication::exec();
}

結果如下圖:

 第一行和第四行,咱們修改過背景色和文本顏色。

// 不合格,讓它顯示黃色
cell02->setBackground(QColor("yellow"));
cell02->setForeground(QColor("red"));
……
// 滿分,給他點獎勵
cell32->setBackground(QColor("blue"));
cell32->setForeground(QColor("white"));

 

好了,今天就聊到這兒,QStandardItemModel 還有其他耍法,尤其是在樹形結構上。咱們下一期繼續扯。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.分組group by 在MySQL中,GROUP BY的意思是“分組查詢”,它可以根據一個或多個欄位對查詢結果進行分組。 GROUP BY的作用是通過一定的規則將一個數據集劃分成若幹個小的區域,然後針對若幹個小區域進行數據處理。這可以理解為將數據按照某個欄位或者多個欄位進行分組。 使用GROUP ...
  • MySQL資料庫管理 資料庫-->數據表-->行(記錄):用來描述一個對象的信息 列(欄位):用來描述對象的一個屬性 常用的數據類型: int :整型 無符號[0,2^32-1],有符號[-2^31,2^31-1] float :單精度浮點 4位元組32位 double :雙精度浮點 8位元組64位 c ...
  • 在MySQL中,高級查詢是指使用更複雜的查詢語句和操作符來檢索和操作資料庫中的數據。高級查詢可以幫助您更精確地找到所需的信息,並提高查詢的效率和靈活性。 以下是高級查詢的一些常見應用場景和意義: 連接多個表:使用JOIN操作符將多個表連接起來,以便在一次查詢中獲取相關聯的數據。這對於在多個表之間建立 ...
  • 背景: 隨著項目體量越來越大,用戶群體越來越多,用戶的聲音也越來越明顯;關於應用發版之後用戶無感知,導致用戶用的是仍然還是老版本功能,除非用戶手動刷新,否則體驗不到最新的功能;這樣的體驗非常不好,於是我們團隊針對該問題給出了相應的解決方案來處理;技術棧:vue3+ts+vite+ant-design ...
  • 相比用戶停留時間短、用完即走的 Web 頁面,桌面 QQ 用戶在一次登錄後,可能會掛機一周以上,這段期間,如果沒有嚴格控制好 QQ 記憶體占用,那麼結果可能是用戶交互響應變慢、甚至 Crash。在系統監控工具里,高記憶體占用也會被直觀地反映出來,帶來不好的口碑。MAC QQ 灰度期間,也聽到了一些用戶關... ...
  • 介紹 ESLint 是一個根據方案識別並報告 ECMAScript/JavaScript 代碼問題的工具,其目的是使代碼風格更加一致並避免錯誤。在很多地方它都與 JSLint 和 JSHint 類似,除了: ESLint 使用 Espree 對 JavaScript 進行解析。 ESLint 在代碼 ...
  • 這是一個講解DDD落地的文章系列,作者是《實現領域驅動設計》的譯者滕雲。本文章系列以一個真實的並已成功上線的軟體項目——碼如雲(https://www.mryqr.com)為例,系統性地講解DDD在落地實施過程中的各種典型實踐,以及在面臨實際業務場景時的諸多取捨。 本系列包含以下文章: DDD入門 ...
  • 系統設計之緩存五種策略 當我們在架構中引入緩存時,緩存和資料庫之間的同步就變得不可避免。 讓我們看看如何保持數據同步的五種常見策略。 1)閱讀策略: 緩存在一邊 通讀2)寫策略:寫周圍 回信 寫通緩存策略經常組合使用。例如,write-around 通常與 cache-aside 一起使用,以確保緩 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...