首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Qt 學習之路 (46):自訂 Model 實戰 (二) —— 可編輯城市距離表模型

     編輯:關於C語言

承上篇唯讀模型,本篇進一步實作 **可編輯的對稱矩陣模型** —— 城市距離表。展示如何覆寫 setData()、flags() 實現資料雙向同步與編輯權限控制。

業務需求與資料結構

  • 顯示 N 個城市兩兩距離的 N×N 表格。
  • 對稱矩陣:dist(A,B) == dist(B,A),對角線為 0。
  • **儲存優化**:僅儲存上/下三角矩陣,以一維 QVector<int> 緊湊存放,大小為 N*(N-1)/2。
  • 支援使用者編輯任一非對角儲存格,對稱位置自動同步更新。

模型類別定義

class CityModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    CityModel(QObject *parent = 0);
    void setCities(const QStringList &cityNames);

    int rowCount(const QModelIndex &parent) const override;
    int columnCount(const QModelIndex &parent) const override;
    QVariant data(const QModelIndex &index, int role) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role) override;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;

private:
    int offsetOf(int row, int column) const;   // 二維座標 -> 一維索引
    QStringList cities;
    QVector<int> distances;                     // 緊湊儲存三角矩陣
};

座標轉換:offsetOf()

int CityModel::offsetOf(int row, int column) const
{
    if (row < column) qSwap(row, column);      // 統一轉為下三角索引
    return (row * (row - 1) / 2) + column;     // 三角數公式
}

資料顯示:data()

QVariant CityModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid()) return QVariant();

    if (role == Qt::TextAlignmentRole)
        return int(Qt::AlignRight | Qt::AlignVCenter);

    if (role == Qt::DisplayRole) {
        if (index.row() == index.column()) return 0;          // 對角線
        int offset = offsetOf(index.row(), index.column());
        return distances[offset];
    }
    return QVariant();
}

編輯支援:setData() —— 核心亮點

bool CityModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    // 僅處理:合法索引、非對角線、編輯角色
    if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) {
        int offset = offsetOf(index.row(), index.column());
        distances[offset] = value.toInt();                    // 更新儲存

        // 建立對稱位置的 ModelIndex
        QModelIndex transposedIndex = createIndex(index.column(), index.row());

        // 發射 dataChanged 通知視圖刷新「兩個」儲存格
        emit dataChanged(index, index);
        emit dataChanged(transposedIndex, transposedIndex);

        return true;
    }
    return false;
}

關鍵細節:

  1. createIndex(row, col) 手動建立對稱位置的索引。
  2. 發射 dataChanged(topLeft, bottomRight) 兩次,分別通知原位置與對稱位置更新。
  3. 回傳 true 表示編輯成功,false 則視圖會撤銷編輯。

編輯權限控制:flags()

Qt::ItemFlags CityModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flags = QAbstractItemModel::flags(index);
    if (index.row() != index.column())       // 對角線不可編輯
        flags |= Qt::ItemIsEditable;
    return flags;
}

初始化:setCities()

void CityModel::setCities(const QStringList &cityNames)
{
    cities = cityNames;
    distances.resize(cities.count() * (cities.count() - 1) / 2);
    distances.fill(0);
    reset();                                  // 完整重置模型
}

主程式啟動

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QStringList cities;
    cities << "Arvika" << "Boden" << "Eskilstuna" << "Falun";

    CityModel cityModel;
    cityModel.setCities(cities);

    QTableView tableView;
    tableView.setModel(&cityModel);
    tableView.setAlternatingRowColors(true);
    tableView.setWindowTitle(QObject::tr("Cities"));
    tableView.show();

    return a.exec();
}

執行結果

城市距離表執行畫面

總結

本例完整示範 **可編輯自訂模型** 的三大關鍵:

  1. setData():處理寫入邏輯、對稱同步、發射 dataChanged。
  2. flags():精細控制每個儲存格的可編輯性。
  3. 緊湊儲存 + 座標映射:以一維陣列模擬對稱矩陣,節省記憶體並簡化同步邏輯。

掌握此模式,即可應對各類對稱矩陣、三角矩陣等特殊資料結構的模型化需求。

本文出自 “豆子空間” 博客,請務必保留此出處:https://devbean.blog.51cto.com/448512/267972

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved