没志青年
发布于 2025-07-17 / 31 阅读
0

QT 视图模型

先调用 View的drawRow,再调用代理的 paint

QTreeView::paintEvent()
 └─ QTreeView::drawTree()
     └─ 对每个可见 row:
         ├─ drawBranches()        // 画树枝/展开箭头
         ├─ drawRow()             // 行级绘制钩子(你能重写)
         │   └─ 对该行的每个 column:
         │       └─ delegate->paint()   // 单元格绘制

每一个 view 有一个SelectionModel

QItemSelectionModel *sm = view->selectionModel();

Widget 在数据量大的时候容易卡,推荐使用View。

QListWidget、QTreeWidget和QTableWidget 这 3 个用于处理项数据的组件是它们对应的子类,叫做便利类,没有模型,用项的方式代替了模型的功能,因此缺乏了对大型数据源的灵活处理的能力。

视图模型架构:

  • View:负责展示

  • Delegate:怎么画/怎么编辑

  • Model:负责数据处理

数据模型 Model

索引 QModelIndex

特定行和特定列上的数据有唯一标识(ID),即 QModalIndex

角色 Role

role

角色是view或代理传过来的,要某一种数据。

view的话,就是固定的,代理的话可自定义role

角色

宏定义

说明

Qt::DisplayRole

0

🟢 最常用的角色!用于显示在界面上的​​文本内容​​,比如表格单元格中显示的文字

Qt::EditRole

用于​​编辑时显示的内容​​,通常和 DisplayRole一样,但如果你想编辑时显示不同内容(比如数字输入框显示数字而不是文字),可以区分

Qt::DecorationRole

用于显示​​图标、图片等装饰性内容​​,比如在表格中某列显示一个小图标

Qt::ToolTipRole

当鼠标悬停在项上时,显示的​​提示文本(Tooltip)​

Qt::StatusTipRole

在状态栏显示的提示信息

Qt::WhatsThisRole

“这是什么”帮助文本(通常很少用)

Qt::FontRole

控制该项的​​字体(QFont)​

Qt::TextAlignmentRole

控制文本的​​对齐方式(Qt::Alignment)​

Qt::BackgroundRole

控制该项的​​背景颜色(QBrush)​​,比如设置单元格背景色

Qt::ForegroundRole

控制该项的​​前景色(比如文字颜色,QBrush)​

Qt::CheckStateRole

用于显示和交互一个 ​​复选框状态(Qt::Checked / Unchecked / PartiallyChecked)​

​自定义角色​

比如 Qt::UserRole + 1Qt::UserRole + 2...

你可以定义自己的用途的数据,比如存储 ID、状态标志等

flags 控制某个item可执行的操作。

Qt::NoItemFlags

什么都不支持,通常用于无效索引

Qt::ItemIsEnabled

该项是可用的(如果未设置,则该项不可用、不可选、不可交互)

Qt::ItemIsSelectable

该项可以被选中(比如点击选中)

Qt::ItemIsEditable

该项可以被编辑(双击进入编辑模式)

Qt::ItemIsUserCheckable

该项可以显示为一个复选框,并且可以被用户勾选/取消勾选

Qt::ItemIsDragEnabled

该项可以被拖动(用于拖放操作)

Qt::ItemIsDropEnabled

该项所在位置可以接受拖入的内容(作为放置目标)

Flag

说明

什么都不支持,用于无效索引。

可交互

可选中

可编辑

可拖动

可放入

显示为复选框

自定义代理

QStyledItemDelegate:

// 自定义更新数据的编辑器
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

// 从Model中获取数据
void setEditorData(QWidget *editor, const QModelIndex &index) const override;

// 保存数据到Model中
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;

// 自定义编辑器的位置和大小
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

bool eventFilter(QObject *editor, QEvent *event) override;

// 自定义数据如何展示
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

// 自定义数据单元的大小
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;

列表

若是继承了 QAbstractItemDelegate,他没有默认的样式,需要自己实现,不像 QStyledItemDelegate 那样。

在 QStyledItemDelegate 中实现系统默认绘制:

void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
bool useCustom = index.data(Qt::UserRole + 1).toBool();

    if (useCustom)
    {
        // 自定义绘制
        painter->fillRect(option.rect, Qt::yellow);
        painter->setPen(Qt::black);
        painter->drawText(option.rect, Qt::AlignCenter, "Custom Drawn");
    }
    else
    {
        // 条件不满足时,调用基类 => 使用系统默认样式绘制
        QStyledItemDelegate::paint(painter, option, index);
    }
}</code></pre><p style=""></p><p style="">QTreeView 可实现:</p><ul><li><p style="">无列的显示(通常)</p></li><li><p style="">像 QTableView 那样有列的显示</p></li></ul><p style=""></p><p style=""></p><p style=""></p><h1 style="" id="table-%E8%A1%A8%E6%A0%BC">Table 表格</h1><p style="">QAbstractItemModel:</p><p style=""></p><p style="">一般重写这几个:</p><pre><code>int rowCount(const QModelIndex &amp;parent) const override;

int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

这些函数用于通知状视图刷新,否则可能出现数据错乱和诡异的bug,必须成对出现。

void endInsertColumns() void endInsertRows() void endMoveColumns() void endMoveRows() void endRemoveColumns() void endRemoveRows() void endResetModel()

QAbstractItemModel 里的信号分两类:

① 框架内部自动触发的(调用 begin/end 这些函数中触发的)

② 业务数据相关的,必须你手动触发

emit dataChanged(topLeft, bottomRight, roles);
emit headerDataChanged(orientation, first, last);
emit layoutAboutToBeChanged();
emit layoutChanged();

QAbstractTableModel:

bool QAbstractTableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
Qt::ItemFlags QAbstractTableModel::flags(const QModelIndex &index) const
QModelIndex QAbstractTableModel::index(int row, int column, const QModelIndex &parent = QModelIndex()) const
QModelIndex QAbstractTableModel::sibling(int row, int column, const QModelIndex &idx) const
  • dropMimeData:有数据拖拽到当前 Model 时怎么处理这个数据(松手时)

  • flags:单元格的操作

  • index:给我 (row, column),我给你一个 QModelIndex,这个一般不需要重写

  • sibling:在同一层级,跳到同一行/列的另一个 index

继承 QAbstractTableModel 需要重写:

函数

说明

QColumnView