[Qt基礎內容-08] Qt中MVC的M(Model)

来源:https://www.cnblogs.com/codegb/archive/2022/09/07/16667536.html
-Advertisement-
Play Games

Qt中MVC的M(Model)簡單介紹 Qt有自己的MVC框架,分別是model(模型)、view(視圖)、delegate(委托),這篇文章,簡單的介紹以下Qt中有關model(模型)的類以及一些基本的使用。 Qt官方的文檔已經很詳細了,如果想要詳細的去瞭解,建議花點精力去看官方文檔。 @ 類繼承 ...


Qt中MVC的M(Model)簡單介紹

Qt有自己的MVC框架,分別是model(模型)、view(視圖)、delegate(委托),這篇文章,簡單的介紹以下Qt中有關model(模型)的類以及一些基本的使用。
Qt官方的文檔已經很詳細了,如果想要詳細的去瞭解,建議花點精力去看官方文檔。

@

目錄

類繼承的結構

Qt中的模型類,都繼承自QAbstractItemModel,這個類定義了基本的必須的介面。

由於QAbstractItemModel這種帶有abstract的類是抽象類,不建議直接使用,所以本文只介紹可直接使用的類的基本用法。

QStringListModel

根據Qt幫助文檔中的解釋,QStringListModel是一個可編輯的模型,可用於需要在視圖小部件(如QListView或QComboBox)中顯示許多字元串的簡單情況。
下麵是使用的代碼以及效果展示:

QStringListModel *m_listModel_2 = new QStringListModel;
QStringList list_2  = {"111", "222", "333", "444", "555"};
m_listModel_2->setStringList(list_2);

ui->listView->setModel(m_listModel_2);

展現的效果:
PluginForMyDemo_kYnZ78Hcsa.png

QAbstractProxyModel

  這裡有一個Proxy(代理),這個要和Delegate(委托)區分開來。我的理解是,Proxy(代理)主要是應用在model上,用於對原數據進行處理,而Delegate(委托)主要是用來顯示和編輯數據。
  為什麼要有這個代理呢?個人理解是,當Model關聯了幾個View時,如果你需要對某一個Model的數據進行排序,那如果不用代理,那麼就意味著你原本的Model也會改變,那麼所有的View都會改變。那麼如果你僅僅只需要當前的view對這個數據進行改變,那麼就需要用到代理,幫你把內容進行一個處理,然後發出來。


QSortFilterProxyModel

這個代理,提供了排序和過濾的介面,能夠方便的調用,給數據提供一個排序過濾的功能;
根據Qt官方幫助文檔對於QSortFilterProxyModel的介紹:

QSortFilterProxyModel can be used for sorting items, filtering out items, or both. The model transforms the structure of a source model by mapping the model indexes it supplies to new indexes, corresponding to different locations, for views to use. This approach allows a given source model to be restructured as far as views are concerned without requiring any transformations on the underlying data, and without duplicating the data in memory.


QSortFilterProxyModel 可用於排序項目、過濾項目或兩者兼而有之。 該模型通過將其提供的模型索引映射到新索引(對應於不同位置)來轉換源模型的結構,以供視圖使用。 這種方法允許就視圖而言對給定的源模型進行重構,而無需對基礎數據進行任何轉換,也無需複製記憶體中的數據。

以下是基本的用法:

  1. 排序

    QTableView* tableview = new QTableView();
    
    QStandardItemModel *model = new QStandardItemModel();
    model->setItem(0, 0, new QStandardItem("Aba"));
    model->setItem(1, 0, new QStandardItem("aba"));
    model->setItem(2, 0, new QStandardItem("ABc"));
    model->setItem(0, 1, new QStandardItem("C"));
    model->setItem(1, 1, new QStandardItem("A"));
    model->setItem(2, 1, new QStandardItem("c"));
    model->setItem(0, 2, new QStandardItem("c"));
    model->setItem(1, 2, new QStandardItem("b"));
    model->setItem(2, 2, new QStandardItem("C"));
    
    QSortFilterProxyModel* sortFilterModel = new QSortFilterProxyModel();
    // 為代理設置源model
    sortFilterModel->setSourceModel(listModel);
    // 設置大小寫敏感
    sortFilterModel->setSortCaseSensitivity();
    
    tableview->setModel(sortFilterModel);
    // 設置開啟點擊表頭進行排序
    tableview->setSortingEnable(true);
    

      需註意的是,當你使用QTableView或者QTreeView時,調用setSortingEnable並設置為true,就可以設置點擊表頭進行排序。
    image.png
    當然,你可以手動進行排序

    // 對第二列進行升序排序
    ui->tableview->sortByColumn(1, Qt::AscendingOrder);
    

      但是這樣排序有一個問題:表的序號沒有進行改變。暫時沒有找到方法來解決,有一個參考的解決方法可以看:QTableView自定義Model實現排序 。同樣,如果你要自定義排序的規則的話,你可以繼承QSortFilterProxyModel類,然後重寫lessThan函數,重新寫一下裡面的排序規則。可以參考Qt官方的例子Custom Sort/Filter Model

  2. 過濾

    過濾的規則你可以選擇

    • 正則表達式
    • 通配符模式
    • 固定字元串


      在層級結構中,會遞歸的去過濾其子節點。同時,當父節點被過濾時,子節點也不會被顯示。
    基本用法如下:

    QStringListModel *m_listModel_2 = new QStringListModel;
    QStringList list_2  = {"111", "222", "333", "444", "555", "a.jpg", "b.jpg"};
    
    QSortFilterProxyModel* listviewFilterModel = new QSortFilterProxyModel;
    // 設置源model
    listviewFilterModel->setSourceModel(m_listModel_2);
    m_listModel_2->setStringList(list_2);
    
    listviewFilterModel->setFilterRegExp(QRegExp(".jpg", Qt::CaseSensitive,
        								 QRegExp::FixedString));
    ui->listView->setModel(listviewFilterModel);
    

    image.png
      其他的用法,請參考Qt官方例子Custom Sort/Filter Model
      預設的情況下,在源model的數據發生改變時,會自動重新排序或者重新過濾信息。想要控制這個特性,設置dynamicSortFilter這個屬性。

QTransposeProxyModel

這個類是一個用來行列交換的model。就是說如果源model中有一個索引index(0, 1),那麼這個在代理model中就是index(1, 0)。

QStandardItemModel *model = new QStandardItemModel();
model->setItem(0, 0, new QStandardItem("2022-9-4 21:11:08"));
model->setItem(1, 0, new QStandardItem("2022-9-5 17:21:08"));
model->setItem(2, 0, new QStandardItem("2022-9-1 13:03:12"));
model->setItem(0, 1, new QStandardItem("C"));
model->setItem(1, 1, new QStandardItem("A"));
model->setItem(2, 1, new QStandardItem("c"));
model->setItem(0, 2, new QStandardItem("c"));
model->setItem(1, 2, new QStandardItem("b"));
model->setItem(2, 2, new QStandardItem("C"));

QTransposeProxyModel* transposeModel = new QTransposeProxyModel;
// 設置源model
transposeModel->setSourceModel(model);
ui->tableView->setModel(transposeModel);

image.png

QIdentityProxyModel

根據官方的文檔:

QIdentityProxyModel can be used to forward the structure of a source model exactly, with no sorting, filtering or other transformation. This is similar in concept to an identity matrix where A.I = A.
Because it does no sorting or filtering, this class is most suitable to proxy models which transform the data() of the source model. For example, a proxy model could be created to define the font used, or the background colour, or the tooltip etc. This removes the need to implement all data handling in the same class that creates the structure of the model, and can also be used to create re-usable components.


QIdentityProxyModel可以用於準確地轉發源模型的結構,不需要排序、過濾或其他轉換。這在概念上類似於單位矩陣,其中A.I = A。
因為它不進行排序或過濾,所以這個類最適合於轉換源模型的data()的代理模型。例如,可以創建一個代理模型來定義所使用的字體、背景顏色或工具提示等。這樣就不需要在創建模型結構的同一個類中實現所有數據處理,並且還可以用於創建可重用的

  也就是說,這個model不會對源model進行任何轉換,只會簡簡單單的進行映射。主要是為了使用者可以通過重寫data()函數,來定製每一個元素所顯示的效果。同時這樣也不會污染源model的數據,方便進行重用。也對應這上面的,proxy(代理)的作用就是一個源model可以在許多個view里設置,且基本互不影響。
  以下代碼源自Qt官方文檔:

class DateFormatProxyModel : public QIdentityProxyModel
 {
   // ...

   void setDateFormatString(const QString &formatString)
   {
     m_formatString = formatString;
   }

   QVariant data(const QModelIndex &index, int role) const override
   {
     if (role != Qt::DisplayRole)
       return QIdentityProxyModel::data(index, role);

     const QDateTime dateTime = sourceModel()->data(SourceClass::DateRole).toDateTime();

     return dateTime.toString(m_formatString);
   }

 private:
   QString m_formatString;
 };

像上面這樣,重載model類的data函數,輸出定製化的日期格式。
  至於為什麼要引入這樣一個類,我的理解是,你在進行繼承的時候,你需要找一個和你要實現的功能類似的父類來繼承,這樣的話就方便一點。但是如果你要實現的子類,功能和前面兩個代理類沒有關係,那麼你繼承上面兩個類會進行一些多餘的操作,這個時候引入一個只做映射的類,不對源model進行任何的改變,這樣定製化自己的代子類而不進行多餘操作。

QSqlQueryModel

QSqlQueryModel提供了一個用於讀取資料庫數據的model,能夠為像QTableView這樣的view提供數據。
常用的函數有:

  1. void setQuery(const QSqlQuery &query)

    void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase())
    這個函數是用來設置查詢的語句以及查詢的資料庫;

  2. QSqlRecord QSqlQueryModel::record(int row) const

    查詢指定第_row_行的數據;

  3. QSqlRecord QSqlQueryModel::record( ) const

    返回一個空的QSqlRecord,但是裡面包含了欄位的信息;

  4. void fetchMore(const QModelIndex &parent = QModelIndex())

    從資料庫中取得更多的行數。這個只會對不返回QuerySize的資料庫起作用。例如:oracle;可參看本人寫的博客:QTableView實現在表格內直接對資料庫內容進行修改、新增和刪除等操作中,關於新增的部分。

其簡單的用法是(代碼源自Qt官方文檔):

QSqlQueryModel *model = new QSqlQueryModel;
// 設置資料庫查詢語句,這裡如果不指定QSqlDatabase的話,就會使用預設的資料庫連接
model->setQuery("SELECT name, salary FROM employee");
// 設置表格的表頭
model->setHeaderData(0, Qt::Horizontal, tr("Name"));
model->setHeaderData(1, Qt::Horizontal, tr("Salary"));

QTableView *view = new QTableView;
// 為view設置model
view->setModel(model);
view->show();

此處有一個重要的點,那就是你需要自己設置QSqlQueryModel所要訪問的資料庫。根據不同的資料庫,創建不同的QSqlDatabase連接

// 以Sqlite為例
QSqlDatabase m_db;
// 添加QSqlDatabase
// 此處addDatabase函數不指定connectName,就會添加一個defaultConnection
// 上面的setQuery的就可以訪問到預設的資料庫連接了。
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName("D:/database.db");

if(!m_db.open())
{
    qDebug()<<"打開失敗";
    return;
}

同樣,也可以只用model查詢資料庫數據,而不與view綁定中去。

QSqlQueryModel model;
model.setQuery("SELECT name, salary FROM employee");
// 獲取第四行數據中欄位salary的值
int salary = model.record(4).value("salary").toInt();

QSqlQueryModel是只讀的,如果想要可讀可寫的話,你可以使用QSqlTableModel

QSqlTableModel

QSqlTableModel繼承自QSqlQueryModel類,是可讀可寫的。
常用的函數:

  1. void setTable(const QString &tableName)

     設置需要查詢的資料庫表名為tableName。

  2. void setEditStrategy(QSqlTableModel::EditStrategy strategy)

     設置數據編輯的策略,主要有三種策略,分別是有任何改變就提交、行改變提交、手動提交。

  3. bool select()

     根據生成的sql語句來查詢。

  4. bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole)

     設置指定角色的水平標題的標題值。如果orientationQt::Horizontal,並且_section_指的是一個有效的section,則返回true;否則返回假;

  5. void setFilter(const QString &filter)

     設置過濾規則。filter的內容為,一個沒有where的關鍵字的where語句。比如:正常的語句"select * from table where name = 'ZhangSan' ",那麼此時filter的內容就應該為" name = 'ZhangSan' "

  6. void setSort(int column, Qt::SortOrder order)

     設置指定列column排序,註意:調用這個函數設置新的排序之後,不會影響當前的數據,需要調用select函數來刷新數據。

  7. void revert()

     根據官方的文檔中的解釋,如果模型的策略設置為OnRowChangeOnFieldChange,則恢復更改。對OnManualSubmit策略不做任何操作。使用revertAll()恢復OnManualSubmit策略的所有掛起更改,或者使用revertRow()恢復特定行

  8. bool submit()

     如果模型的策略設置為OnRowChangeOnFieldChange,則提交當前編輯的行。對OnManualSubmit策略不做任何操作。使用submitAll()OnManualSubmit策略提交所有掛起的更改

最基本的用法:

// 創建/打開資料庫
QSqlDatabase db;

if (QSqlDatabase::contains("qt_sql_default_connection"))
{
    // 獲取預設的連接
    db = QSqlDatabase::database("qt_sql_default_connection");
}
else
{
    // 建立和SQlite資料庫的連接
    db = QSqlDatabase::addDatabase("QSQLITE");
    // 設置資料庫文件的名字
    db.setDatabaseName("Database.db");
}

if (db.open()) {
    // 創建表以及往表裡插入數據
    QSqlQuery query;
    query.exec("create table person (Name varchar(20), Salary int)");
    query.exec("insert into person values('ZhangSan', 1)");
    query.exec("insert into person values('LiSi', 2)");
    query.exec("insert into person values('WangWu', 3)");
    query.exec("insert into person values('ZhaoLiu', 4)");
    query.exec("insert into person values('QianQi', 5)");
}

QSqlTableModel* tableModel = new QSqlTableModel();
// 設置表名
tableModel->setTable("person");
// 設置編輯策略,設置為需手動提交
tableModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
// 設置表頭數據
tableModel->setHeaderData(0, Qt::Horizontal, "Name");
tableModel->setHeaderData(1, Qt::Horizontal, "Salary");
// 查詢,必須要有,不然沒有數據顯示
tableModel->select();

ui->tableView->setModel(tableModel);

QTableView *view = new QTableView;
view->setModel(tableModel);
view->hideColumn(0); // don't show the ID
view->show();

image.png
通過setFilter函數來設置過濾規則。

tableModel->setFilter("name='ZhangSan' or name = 'WangWu'");

image.png
通過setSort函數來設置排序

tableModel->setSort(0, Qt::AscendingOrder);
tableModel->select();

image.png
其餘的用法也可以參看之前寫的博客:QTableView實現在表格內直接對資料庫內容進行修改、新增和刪除等操作

QConcatenateTablesProxyModel

 這個也是一個代理,其作用是可以聯立多個model,將數據放到一起顯示。顯示的列的數量為所有聯立的model中,列數量最小的model決定。
 簡單的用法為:

QStringList list;
list << "5" << "2" << "1" << "4" << "3";
QStringListModel* listModel = new QStringListModel();
listModel->setStringList(list);

QSqlDatabase db;

if (QSqlDatabase::contains("qt_sql_default_connection"))
{
    // 獲取預設的連接
    db = QSqlDatabase::database("qt_sql_default_connection");
}
else
{
    // 建立和SQlite資料庫的連接
    db = QSqlDatabase::addDatabase("QSQLITE");
    // 設置資料庫文件的名字
    db.setDatabaseName("Database.db");
}

if (db.open()) {
    // 創建表以及往表裡插入數據
    QSqlQuery query;
    query.exec("create table person (Name varchar(20), Salary int)");
    query.exec("insert into person values('ZhangSan', 1)");
    query.exec("insert into person values('LiSi', 2)");
    query.exec("insert into person values('WangWu', 3)");
    query.exec("insert into person values('ZhaoLiu', 4)");
    query.exec("insert into person values('QianQi', 5)");
}

QSqlTableModel* tableModel = new QSqlTableModel();
tableModel->setTable("person");
tableModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
tableModel->setHeaderData(0, Qt::Horizontal, "Name");
tableModel->setHeaderData(1, Qt::Horizontal, "Salary");
tableModel->setSort(0, Qt::AscendingOrder);
tableModel->select();

QConcatenateTablesProxyModel* concatenateModel = new QConcatenateTablesProxyModel;
// 添加源model
concatenateModel->addSourceModel(listModel);
concatenateModel->addSourceModel(tableModel);

ui->tableView->setModel(concatenateModel);

image.png
從上面就可以看出,tableModel中原應該有的第二列,被忽略掉了,因為listModel只有1列。

QDirModel、QFileSystemModel

 根據Qt官方文檔中的描述,已經不建議用QDirModel,建議使用QFileSystemModel,由於兩個類很類似,所以本文只介紹QFileSystemModel。
 QFileSystemModel是一個用於訪問本地文件系統的類,提供了一些基本的讀、寫文件或目錄以及創建新的目錄的介面方便使用。常用的函數有:

  1. 獲取文件的一些信息

    函數原型 函數功能
    QIcon fileIcon(const QModelIndex &index) const 獲取指定文件的圖標
    QFileInfo fileInfo(const QModelIndex &index) const 獲取指定文件的信息
    QString fileName(const QModelIndex &index) const 獲取指定文件的名字
    QString filePath(const QModelIndex &index) const 獲取指定文件的路徑
  2. 目錄操作

    函數原型 函數功能
    bool isDir(const QModelIndex &index) const 判斷指定文件是否是目錄
    QModelIndex mkdir(const QModelIndex &parent, const QString &name) 在指定的目錄下,創建一個新的子目錄
    bool rmdir(const QModelIndex &index) 刪除指定目錄

基本的用法:

QFileSystemModel* fileModel = new QFileSystemModel;
fileModel->setRootPath(QDir::currentPath());

ui->treeView->setModel(fileModel);
ui->treeView->setRootIndex(fileModel->index(QDir::currentPath()));

image.png

QStandardItemModel

根據Qt官方文檔的描述:

QStandardItemModel provides a classic item-based approach to working with the model. The items in a QStandardItemModel are provided by QStandardItem.


QStandardItemModel提供了一種經典的基於項的方法來處理模型。QStandardItemModel中的項由QStandardItem提供。

QStandardItemModel實現了QAbstractItemModel介面,這意味著該模型可以用於在任何支持該介面的視圖中提供數據(例如QListView, QTableView和QTreeView,以及您自己的自定義視圖)。這是一個基於項的模型,像上面介紹的這些,基本都是特例化的一些子類,QStandardItemModel則可以自己創建一個整體的結構,Table、Tree或者List這些,都可以創建並填充數據。

When you want a list or tree, you typically create an empty QStandardItemModel and use appendRow() to add items to the model, and item() to access an item. If your model represents a table, you typically pass the dimensions of the table to the QStandardItemModel constructor and use setItem() to position items into the table. You can also use setRowCount() and setColumnCount() to alter the dimensions of the model. To insert items, use insertRow() or insertColumn(), and to remove items, use removeRow() or removeColumn().
You can set the header labels of your model with setHorizontalHeaderLabels() and setVerticalHeaderLabels().
You can search for items in the model with findItems(), and sort the model by calling sort().
Call clear() to remove all items from the model


當您需要列表或樹時,您通常創建一個空的QStandardItemModel,並使用appendRow()向模型添加項,並使用item()訪問項。如果您的模型表示一個表,您通常將表的維度傳遞給QStandardItemModel構造函數,並使用setItem()將項目定位到表中。您還可以使用setRowCount()和setColumnCount()來更改模型的維度。要插入項,請使用insertRow()或insertColumn(),要刪除項,請使用removeRow()或removeColumn()。您可以使用setHorizontalHeaderLabels()

以Table結構為例,簡單的用法:

QStandardItemModel* model = new QStandardItemModel(4, 4);
for (int row = 0; row < model->rowCount(); ++row) {
    for (int column = 0; column < model->columnCount(); ++column) {
        QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
        model->setItem(row, column, item);
    }
}
model->setHorizontalHeaderLabels({"Column 1", "Column 2", "Column 3", "Column 4"});
ui->tableView->setModel(model);

image.png

自定義Model

有時候會需要對model中的數據進行一種修改, 然後反饋到View上,這個時候,你就需要子類化一個model,然後重寫其data函數,來實現你想要的要求。

下麵以Table的內容為例子:
mymodel.h

#ifndef MYMODEL_H
#define MYMODEL_H

#include <QStandardItemModel>

class MyModel : public QStandardItemModel
{
public:
    explicit MyModel();

protected:
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};
#endif

mymodel.cpp

#include "mymodel.h"

MyModel::MyModel()
{

}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    // 背景色
    if (index.column() == 1 && role == Qt::BackgroundRole) {
        return QVariant(QColor(Qt::red));
    }

    // 前景色
    if (index.column() == 2 && role == Qt::ForegroundRole) {
        return QVariant(QColor(Qt::blue));
    }

    // 文字位置
    if (index.column() == 3 && role == Qt::TextAlignmentRole) {
        return QVariant(Qt::AlignBottom);
    }

    // 字體
    if (index.column() == 0 && role == Qt::FontRole) {
        return QVariant(QFont("MicroSoft YaHei", 18));
    }

    return QStandardItemModel::data(index, role);
}

使用代碼:

MyModel* model = new MyModel;
for (int row = 0; row < 4; ++row) {
    for (int column = 0; column < 4; ++column) {
        QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
        model->setItem(row, column, item);
    }
}
model->setHorizontalHeaderLabels({"Column 1", "Column 2", "Column 3", "Column 4"});
ui->tableView->setModel(model);

最終呈現的效果為:
image.png
可以根據不同的role,來做到定製化不同的效果。
image.png


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

-Advertisement-
Play Games
更多相關文章
  • 本項目主要基於`Elux+Antd`構建,包含React版本和Vue版本,旨在提供給大家一個簡單基礎、開箱即用的後臺管理系統通用模版,主要包含運行環境、腳手架、代碼風格、基本Layout、狀態管理、路由管理、增刪改查邏輯、列表、表單等。 ...
  • 前言 本文將對 Vue-Vben-Admin 角色許可權的狀態管理進行源碼解讀,耐心讀完,相信您一定會有所收穫! 更多系列文章詳見專欄 👉 📚 Vben Admin 項目分析&實踐 。 本文涉及到角色許可權之外的較多內容(路由相關)會一筆帶過,具體功能實現將在後面專題中詳細討論。為了更好的理解本文內 ...
  • JS實現數組扁平化處理 點擊打開視頻講解更加詳細 期望結果: 將數組扁平化並去重 最終得到一個升序且不重覆的數組 步驟: 1、數組扁平化 2、去重 3、排序 <template> <div id="home"> JS實現數組扁平化處理,妙不可言啊! <!-- 期望結果: 將數組扁平化並去重 最終得到 ...
  • 最近接到一個很有意思的需求,能否做到當內容橫向溢出時,依然能夠使用滑鼠滾輪對內容進行滾動的方法。 什麼意思呢?來看看這麼一種情況: 我們有一個垂直方向溢出滾動的容器,以及一個水平方向溢出滾動的容器: 如果使用的是非觸控板(大部分用戶沒有觸控板),而是使用滑鼠來進行操作,會發現,這兩個容器中,只有垂直 ...
  • 我因為最近在學習游戲開發相關知識,然後意識到自己設計模式知識缺乏,所以就去尋找相關書籍,這時候《游戲設計模式》這本書就跳到了我的眼前。 github上有大佬將這本書翻譯了,中文版閱讀地址在這:架構,性能和游戲 · Introduction · 游戲設計模式 (tkchu.me) 序章:架構,性能和游 ...
  • 原型模式(Prototype Pattern)是用於創建重覆的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。 ...
  • Python自學第六天:實戰練習——機選雙色球 我是一個編程小白,目前從事運維工作。 對於運維相關的技術,基本上都是瞭解點皮毛。 因為最近接觸自動化運維工具,看到很多工具都需要用到Python來寫腳本。 於是,利用業餘時間,開始自學Python。 目的並不是要學到很精通,而是希望大致看明白別人寫的代 ...
  • 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 特效 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> O ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...