使用 QStyledItemDelegate 子类在 QTableView 中创建 PushButtons
Posted
技术标签:
【中文标题】使用 QStyledItemDelegate 子类在 QTableView 中创建 PushButtons【英文标题】:Create PushButtons in QTableView with QStyledItemDelegate subclass 【发布时间】:2017-03-29 00:53:44 【问题描述】:我有完全相同的problem,但我将使用 QTableView 小部件。我阅读了this 并想知道是否可以重写 createEditor 函数以使用例如 QFileDialog 来获取新数据。
如果可能的话,谁能给我一个例子来实现这样一个 QItemDelegate 的子类。
如果没有,任何人都可以提供一个示例来实现 QItemDelegate 的子类,女巫可以在 QLineEdit 旁边绘制一个按钮以获取功能here。
编辑:也许这个问题真的很愚蠢,我没有意识到,因为我离开这个项目大约半年。
第二:从 Qt 5.7 更新到 5.8 是否安全?
【问题讨论】:
【参考方案1】:我已经尽力了,这是我的解决方案。 QStyledItemDelegate 子类的代码大部分来自here。
解决方案图片
但是,我很想解决一件事:(也许有人可以帮助我并发表评论)
QPixmap::grabWidget is deprecated, use QWidget::grab() instead
但看起来 QWidget::grab()
不是用于此目的的正确解决方案。
foo.h:
#ifndef LIBRARYITEMDELEGATE_H
#define LIBRARYITEMDELEGATE_H
#include <QStyledItemDelegate>
#include <QWidget>
#include <QPushButton>
#include <QTableView>
class LibraryItemDelegate : public QStyledItemDelegate
Q_OBJECT
public:
explicit LibraryItemDelegate(QObject *parent = 0);
~LibraryItemDelegate();
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
// QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
void setModelData(QWidget *editor, QAbstractItemModel *modal, const QModelIndex &index) const Q_DECL_OVERRIDE;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
public slots:
void cellEntered(const QModelIndex &index);
private:
QTableView *myView;
QPushButton *btn;
bool isOneCellInEditMode;
QPersistentModelIndex currentEditedCellIndex;
;
#endif // LIBRARYITEMDELEGATE_H
foo.cpp:
#include "libraryitemdelegate.h"
#include <QPainter>
#include <QStylePainter>
LibraryItemDelegate::LibraryItemDelegate(QObject *parent) : QStyledItemDelegate(parent)
if(QTableView *tableView = qobject_cast<QTableView*>(parent))
myView = tableView;
btn = new QPushButton("...", myView);
btn->hide();
myView->setMouseTracking(true);
connect(myView, SIGNAL(entered(QModelIndex)), this, SLOT(cellEntered(QModelIndex)));
isOneCellInEditMode = false;
LibraryItemDelegate::~LibraryItemDelegate()
void LibraryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
btn->setGeometry(option.rect);
btn->setText("...");
if(option.state == QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
QPixmap map = QPixmap::grabWidget(btn);
painter->drawPixmap(option.rect.x(), option.rect.y(), map);
else
QStyledItemDelegate::paint(painter, option, index);
//QSize LibraryItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
//
// // return the QSize of the item in Your view
//
QWidget *LibraryItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
QPushButton *btn = new QPushButton(parent);
// btn->setText(index.data().toString());
btn->setText("...");
return btn;
else
return QStyledItemDelegate::createEditor(parent, option, index);
void LibraryItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
QPushButton *btn = qobject_cast<QPushButton*>(editor);
// btn->setProperty("data_value", index.data());
btn->setProperty("data_value", "...");
btn->setText("...");
else
QStyledItemDelegate::setEditorData(editor, index);
void LibraryItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
QPushButton *btn = qobject_cast<QPushButton*>(editor);
model->setData(index, btn->property("data_value"));
else
QStyledItemDelegate::setModelData(editor, model, index);
void LibraryItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
editor->setGeometry(option.rect);
void LibraryItemDelegate::cellEntered(const QModelIndex &index)
if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
if(isOneCellInEditMode)
myView->closePersistentEditor(currentEditedCellIndex);
myView->openPersistentEditor(index);
isOneCellInEditMode = true;
currentEditedCellIndex = index;
else
if(isOneCellInEditMode)
isOneCellInEditMode = false;
myView->closePersistentEditor(currentEditedCellIndex);
实施:
QStandardItemModel *myModel; // This is in the Header file
myModel = new QStandardItemModel(0,2,this);
myModel->setHeaderData(1, Qt::Horizontal, 1, Qt::UserRole);
myModel->setHorizontalHeaderLabels(QStringList(tr("Pfad zu den bibliotheks Ordnern")));
// Set Model and delegate to the View
ui->tableView_pathes->setModel(myModel);
LibraryItemDelegate *delegate = new LibraryItemDelegate(ui->tableView_pathes);
ui->tableView_pathes->setItemDelegate(delegate);
// Stretch only the first column
ui->tableView_pathes->horizontalHeader()->setSectionResizeMode(0,QHeaderView::Stretch);
ui->tableView_pathes->horizontalHeader()->setSectionResizeMode(1,QHeaderView::Fixed);
编辑:这是 tableView 中按钮的代码。将 createEditor 中的信号与 connect(btn, SIGNAL(pressed()), this, SLOT(buttonPressed()));
连接,并设置为委托提供对 QStandardItemModel 的引用。
void LibraryItemDelegate::buttonPressed()
QString dir = QFileDialog::getExistingDirectory(new QWidget(), tr("Wähle die bibliotheks Ordner"), "/home", QFileDialog::ShowDirsOnly);
qDebug() << "Test: " << dir;
if(!dir.isEmpty())
QModelIndex ind = currentEditedCellIndex.model()->index(currentEditedCellIndex.row(), 0);
myModel->setData(ind, dir, Qt::DisplayRole);
【讨论】:
我曾这样使用 Widget::grab():painter->drawPixmap(r.left(),r.right(), r.width(), r.height(), widget ->抓取());但那是很久以前的事了,我不记得它是如何工作的。 我想你现在只是做 widget->render() ?它返回那个小部件的像素图......我可能错了。【参考方案2】:-
使用 QStyledItemDelegate,而不是 QItemDelegate。
阅读 Qt 手册
QStyledItemDelegate class
Spinboxdelegate Example
-
子类 QStyledItemDelegate 的代码示例(精简):
头文件
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QStyledItemDelegate>
class KontaktForm;
class MyItemDelegate : public QStyledItemDelegate
Q_OBJECT
mutable SubscriberForm *subscriberForm;
public:
explicit MyItemDelegate(QObject *parent = 0);
~MyItemDelegate();
////////!Methods - You don't need all of them
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
void editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
;
#endif // MYITEMDELEGATE_H
源文件
#include "myitemdelegate.h"
#include "mytreeview.h"
#include <QModelIndex>
#include <QSize>
MyItemDelegate::MyItemDelegate(QObject *parent) : QStyledItemDelegate(parent),
subscriberForm(Q_NULLPTR),
QSize MyItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
//// return the QSize of the item in Your view
void MyItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
////optional : implement custom painting - text, images, drawings, and such
QWidget *MyItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
subscriberForm = new SubscriberForm(parent);
////optional additional settings for Your editor
return subscriberForm;
void MyItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
editor->setGeometry(option.rect);
void MyItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
////setup the editor - your data are in index.data(Qt::DataRoles) - stored in a QVariant;
QString value = index.model()->data(index,Qt::EditRole).toString();
SubscriberForm *subscriberForm = static_cast<SubscriberForm*>(editor);
void MyItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
////optional - if needed - return changed data, from editor to the model in a custom matter
SubscriberForm *subscriberForm = static_cast<SubscriberForm*>(editor);
model->setData(index,QVariant(subscriberForm->getData()),Qt::EditRole);
【讨论】:
这真的有效吗?例如,编辑完成后控件消失。 @Mikhail 此代码来自我的应用程序,因此它应该可以工作。但是我有可能忘记粘贴一些东西,所以如果你尝试过,但没有成功,请告诉我。 是的,所以当它的单元格未激活时,按钮会消失。或者具体来说,paint()
被调用,它什么都不做。如何在未激活时保持按钮存在并非易事,您可以看到另一个答案使用了已弃用的屏幕抓取方法。
正如我在回答中所写,这不是全部代码。我没有在这里写我的paint() 方法,因为它很大。至于要激活的编辑器,我从我的自定义 treeView 类(继承自 QTreeView)中调用 openPersistantEditor()。你是对的,我忘了在这里提到这一点。明天我会尽量记住添加代码。
很遗憾,我现在没有太多时间,问题不是让编辑器处于打开状态。这就是为什么我不会改变我的答案。如果您需要更多信息,请在 Stack Overflow 上提出单独的问题,并通过私信告诉我。在接下来的一周我会有更多的时间,所以我可以回答(如果我知道的话)。以上是关于使用 QStyledItemDelegate 子类在 QTableView 中创建 PushButtons的主要内容,如果未能解决你的问题,请参考以下文章
两次实例化 QStyledItemDelegate 的子类时出现“Python 已停止工作”错误
QStyledItemDelegate 部分选择默认 QLineEdit 编辑器的文本
没有为 QTableView 行调用 QStyledItemDelegate 的 sizeHint 方法
使用 QStyledItemDelegate paint() 在表格中绘制图标
如何使用 QStyledItemDelegate 在 QTreeWidget 中拥有不同高度的 QTreeWidgetItems?