如何在不使用QML的情况下修改QItemDelegate的x/y/z属性
QtWidgets实现QML GridView的点击放大居中效果
在QtWidgets的模型视图架构中,QItemDelegate仅负责单个项的绘制逻辑,项的位置和布局完全由视图(如QTableView/QListView)管控,无法像QML那样直接修改delegate的坐标和层级。要实现点击项放大居中、再次点击恢复的效果,需要换两种可行思路:
方案一:通过视图自定义绘制顺序实现
重载视图的paintEvent,先绘制所有非选中项,再单独将选中项放大绘制到视图中央,利用绘制顺序天然实现层级覆盖(后绘制的内容在上层)。
自定义GridView实现
#include <QTableView> #include <QItemDelegate> #include <QMouseEvent> #include <QPainter> class MyGridView : public QTableView { Q_OBJECT public: MyGridView(QWidget *parent = nullptr) : QTableView(parent), m_selectedIndex(QModelIndex()), m_maxItemsPerLine(4) { setItemDelegate(new MyCustomDelegate(this)); setSelectionMode(QAbstractItemView::NoSelection); // 禁用默认选中逻辑,自行处理 setShowGrid(false); updateRowColumnCount(); } void setModel(QAbstractItemModel *model) override { QTableView::setModel(model); updateRowColumnCount(); } protected: void mousePressEvent(QMouseEvent *event) override { QModelIndex index = indexAt(event->pos()); if (index.isValid()) { m_selectedIndex = (m_selectedIndex == index) ? QModelIndex() : index; update(); // 触发重绘 } QTableView::mousePressEvent(event); } void paintEvent(QPaintEvent *event) override { if (m_selectedIndex.isValid()) { // 1. 先绘制所有非选中项 QPainter painter(viewport()); for (int row = 0; row < rowCount(); ++row) { for (int col = 0; col < columnCount(); ++col) { QModelIndex idx = model()->index(row, col); if (idx == m_selectedIndex) continue; QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(idx); delegate()->paint(&painter, option, idx); } } // 2. 绘制选中项:放大1.5倍并居中 QStyleOptionViewItem option = viewOptions(); QSize originalSize = delegate()->sizeHint(option, m_selectedIndex); QSize scaledSize = originalSize * 1.5; QRect scaledRect( (viewport()->width() - scaledSize.width()) / 2, (viewport()->height() - scaledSize.height()) / 2, scaledSize.width(), scaledSize.height() ); option.rect = scaledRect; delegate()->paint(&painter, option, m_selectedIndex); } else { // 无选中项时正常绘制 QTableView::paintEvent(event); } } private: void updateRowColumnCount() { if (!model()) return; setColumnCount(m_maxItemsPerLine); int totalItems = model()->rowCount(); setRowCount((totalItems + m_maxItemsPerLine - 1) / m_maxItemsPerLine); } QModelIndex m_selectedIndex; int m_maxItemsPerLine; };
自定义委托实现
class MyCustomDelegate : public QItemDelegate { Q_OBJECT public: using QItemDelegate::QItemDelegate; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { painter->save(); // 绘制背景 QColor bgColor = (option.state & QStyle::State_MouseOver) ? option.palette.light() : option.palette.base(); painter->fillRect(option.rect, bgColor); // 绘制内容(示例为文本,可替换为自定义控件内容) painter->drawText(option.rect, Qt::AlignCenter, index.data(Qt::DisplayRole).toString()); painter->restore(); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { MyGridView *view = qobject_cast<MyGridView*>(parent()); if (view) { int cellWidth = view->viewport()->width() / view->columnCount(); int cellHeight = view->viewport()->height() / view->rowCount(); return QSize(cellWidth, cellHeight); } return QItemDelegate::sizeHint(option, index); } };
方案二:通过独立上层控件实现
创建一个独立的QWidget承载选中项的放大内容,显示在视图中央,通过raise()保证层级优先级。这种方式更适合添加平滑缩放/移动动画。
核心修改(在MyGridView中添加)
private: QWidget* m_zoomWidget = nullptr; // mousePressEvent中替换选中逻辑: void mousePressEvent(QMouseEvent *event) override { QModelIndex index = indexAt(event->pos()); if (index.isValid()) { if (m_selectedIndex == index) { // 取消选中:销毁放大控件 delete m_zoomWidget; m_zoomWidget = nullptr; m_selectedIndex = QModelIndex(); } else { m_selectedIndex = index; // 创建放大控件(需自定义MyZoomItemWidget实现内容绘制) delete m_zoomWidget; m_zoomWidget = new MyZoomItemWidget(this); m_zoomWidget->setItemData(index.data()); // 设置尺寸和位置 QSize originalSize = delegate()->sizeHint(viewOptions(), index); QSize scaledSize = originalSize * 1.5; m_zoomWidget->resize(scaledSize); m_zoomWidget->move( (viewport()->width() - scaledSize.width())/2, (viewport()->height() - scaledSize.height())/2 ); m_zoomWidget->raise(); // 确保在视图内容上层 m_zoomWidget->show(); } } QTableView::mousePressEvent(event); }
关键说明
- 层级问题:方案一通过绘制顺序实现层级,方案二通过
raise()将控件置于视图上层,均替代QML中的z属性效果。 - 位置管控:QtWidgets中视图全权负责项的布局,因此无法通过委托修改位置,必须通过视图重绘或额外控件绕开原有布局逻辑。
内容的提问来源于stack exchange,提问作者Fred Vulliet
相关产品推荐
相关产品推荐

