QStyledItemDelegate 是 Qt 框架中用于为模型/视图框架提供数据项显示和编辑的一个类。
1. 创建 QStyledItemDelegate 实例
通常,你不需要直接实例化 QStyledItemDelegate,因为它是默认的委托。但如果你需要自定义显示和编辑行为,你可以继承 QStyledItemDelegate 并创建自己的委托类。
2. 继承 QStyledItemDelegate 并重写必要的方法
createEditor:返回用于编辑由索引指定的项的小部件。你需要根据数据类型创建适当的编辑器(如 QLineEdit、QSpinBox 等)。
setEditorData:从模型索引指定的数据模型项设置要由编辑器显示和编辑的数据。
setModelData:从编辑器小部件获取数据,并将其存储在项目索引处的指定模型中。
updateEditorGeometry:更新由索引指定的项的编辑器几何形状。
3. 设置委托属性(可选)
如果你自定义了 QStyledItemDelegate 的子类,并希望设置一些额外的属性(如字体、颜色等),你可以在子类中提供相应的设置方法。
4. 应用委托到视图
使用视图(如 QTableView、QListView)的 setItemDelegate 方法来应用你的委托。例如:
MyStyledItemDelegate *delegate = new MyStyledItemDelegate(this);
ui->tableView->setItemDelegate(delegate);
5. 自定义绘制和数据处理(可选)
如果你需要更精细地控制项的绘制或数据处理,你可以重写 paint、sizeHint 等方法。
你也可以使用 setItemEditorFactory 来设置自定义的编辑器工厂,以创建特定的编辑器小部件。
6. 注意事项
确保你的模型和视图已经正确设置,并且数据模型中的数据可以被委托正确地读取和写入。
如果你的委托需要处理复杂的数据类型或交互,确保你的实现是健壮的,并处理所有可能的边缘情况。
通过遵循上述步骤,你可以使用 QStyledItemDelegate(或其子类)来定制 Qt 模型/视图框架中的数据项显示和编辑行为。
7、demo示例
在QtreeWidget中通过委托绘制一个组合控件(智能补全编辑框+按钮),先看效果:
#pragma once
#include <QStyledItemDelegate>
#include <QPainter>
#include <QApplication>
class MyStyledDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MyStyledDelegate(QObject *parent);
~MyStyledDelegate();
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private slots:
void onEditFinished();
};
#include "MyStyledDelegate.h"
#include "MyEditor.h"
MyStyledDelegate::MyStyledDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{}
MyStyledDelegate::~MyStyledDelegate()
{}
void MyStyledDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
//此处可以自定义绘制...
return QStyledItemDelegate::paint(painter, option, index);
}
QWidget * MyStyledDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
auto editor = new MyEditor(parent);
connect(editor, &MyEditor::editFinished, this, &MyStyledDelegate::onEditFinished);
return editor;
}
void MyStyledDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const
{
QString value = index.model()->data(index, Qt::EditRole).toString();
auto myEditor = qobject_cast<MyEditor*>(editor);
myEditor->setText(value);
}
void MyStyledDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
auto myEditor = qobject_cast<MyEditor*>(editor);
auto value = myEditor->text();
model->setData(index, value, Qt::EditRole);
}
void MyStyledDelegate::updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
editor->setGeometry(option.rect);
}
void MyStyledDelegate::onEditFinished()
{
QWidget *editor = qobject_cast<QWidget *>(sender());
emit commitData(editor);
emit closeEditor(editor);
}
#pragma once
#include <QWidget>
#include <QPushButton>
#include <QHBoxLayout>
#include "CLineEdit.h"
class MyEditor : public QWidget
{
Q_OBJECT
public:
MyEditor(QWidget *parent);
~MyEditor();
QString text() const;
void setText(const QString &text);
protected:
virtual void keyPressEvent(QKeyEvent *event) override;
virtual void focusInEvent(QFocusEvent *event) override;
virtual void focusOutEvent(QFocusEvent *event) override;
virtual bool eventFilter(QObject*obj, QEvent*ev) override;
signals:
void editFinished();
private:
CLineEdit* m_edit;
QPushButton* m_btn;
};
#include "MyEditor.h"
MyEditor::MyEditor(QWidget *parent)
: QWidget(parent)
{
m_edit = new CLineEdit(this);
m_btn = new QPushButton(this);
QHBoxLayout *pLayout = new QHBoxLayout;
pLayout->addWidget(m_edit);
pLayout->addWidget(m_btn, 0, Qt::AlignVCenter);
pLayout->setSpacing(0);
pLayout->setContentsMargins(0, 0, 0, 0);
setLayout(pLayout);
m_edit->installEventFilter(this);
m_edit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_btn->setText("...");
m_btn->setFixedWidth(20);
m_btn->installEventFilter(this);
connect(m_edit, SIGNAL(editFinished()), this, SIGNAL(editFinished()));
}
MyEditor::~MyEditor()
{}
QString MyEditor::text() const
{
return m_edit->text();
}
void MyEditor::setText(const QString & text)
{
m_edit->setText(text);
m_edit->setFocus();
}
void MyEditor::keyPressEvent(QKeyEvent *event)
{
m_edit->setFocus();
return m_edit->keyPressEvent(event);
}
void MyEditor::focusInEvent(QFocusEvent * event)
{
m_edit->setFocus();
return QWidget::focusInEvent(event);
}
void MyEditor::focusOutEvent(QFocusEvent * event)
{
auto focusWidget = QApplication::focusWidget();
if (focusWidget == m_btn || focusWidget == this)
return;
return QWidget::focusOutEvent(event);
}
bool MyEditor::eventFilter(QObject * obj, QEvent * event)
{
auto type = event->type();
if (obj == m_edit)
{
auto focusWidget = QApplication::focusWidget();
if (type == QEvent::FocusOut)
{
if (focusWidget == m_btn || focusWidget == this)
return true;
}
}
return QWidget::eventFilter(obj, event);
}
#pragma once
#include <QLineEdit>
#include <QlistView>
#include <QStringListModel>
#include <QStringList>
#include <QKeyEvent>
#include <QApplication>
class CLineEdit : public QLineEdit
{
Q_OBJECT
public:
CLineEdit(QWidget *parent);
~CLineEdit();
void setCompleteData(const QStringList& data);
void setCompletePrefix(const QString& prefix);
void showList();
void hideList();
void setListWidth(int w);
void setListHeight(int h);
bool isListVisiable();
public slots:
void onCursorPositionChanged(int, int);
void onTextChanged(const QString&);
void completeText(const QModelIndex&);
public:
virtual bool eventFilter(QObject *obj, QEvent *event);
virtual void keyPressEvent(QKeyEvent *event);
virtual void focusInEvent(QFocusEvent *event);
virtual void focusOutEvent(QFocusEvent *event);
virtual void mouseDoubleClickEvent(QMouseEvent *event);
signals:
void editFinished();
private:
QListView* m_listView; //补全列表视图
QStringListModel* m_listModel; //补全列表模型
QStringList m_listData; //补全数据
};
#include "CLineEdit.h"
CLineEdit::CLineEdit(QWidget *parent)
: QLineEdit(parent)
{
m_listModel = new QStringListModel(this);
m_listView = new QListView(this);
m_listView->setModel(m_listModel);
m_listView->setWindowFlags(Qt::ToolTip);
m_listView->installEventFilter(this);
m_listView->setStyleSheet("QListView::item:selected {background-color: rgb(51,153,255); color: white;}");
m_listView->hide();
connect(m_listView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(completeText(const QModelIndex&)));
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChanged(const QString&)));
connect(this, SIGNAL(cursorPositionChanged(int, int)), this, SLOT(onCursorPositionChanged(int, int)));
//test...
setCompleteData({ "aa", "aab", "aabc", "aabcd" });
}
CLineEdit::~CLineEdit()
{}
void CLineEdit::setCompleteData(const QStringList& data)
{
m_listData = data;
}
void CLineEdit::setCompletePrefix(const QString& prefix)
{
QStringList list;
if (prefix != ".")
{
for (QString data : m_listData)
{
if (data.startsWith(prefix, Qt::CaseInsensitive))
list << data;
}
}
else
{
list = m_listData;
}
m_listModel->removeRows(0, m_listModel->rowCount());
m_listModel->setStringList(list);
showList();
}
void CLineEdit::showList()
{
if (m_listView->isVisible())
return;
int rowCount = m_listModel->rowCount();
if (rowCount == 0 || text().isEmpty())
return;
setListHeight(rowCount * 20);
int h = height();
m_listView->move(mapToGlobal(QPoint(0, height())));
m_listView->show();
}
void CLineEdit::hideList()
{
m_listView->hide();
}
void CLineEdit::setListWidth(int w)
{
m_listView->setFixedWidth(w);
}
void CLineEdit::setListHeight(int h)
{
if (h < 50)
h = 50;
if (h > 200)
h = 200;
m_listView->setFixedHeight(h);
}
bool CLineEdit::isListVisiable()
{
return m_listView->isVisible();
}
void CLineEdit::onCursorPositionChanged(int oldPos, int newPos)
{
QString txt = text();
if (newPos >= txt.length())
return;
txt = txt.mid(0, newPos);
onTextChanged(txt);
}
void CLineEdit::onTextChanged(const QString& text)
{
if (text.isEmpty())
{
hideList();
return;
}
setCompletePrefix(text);
}
void CLineEdit::completeText(const QModelIndex& index)
{
QString allText = text();
QString selectedText = index.data().toString();
if (selectedText.isEmpty())
{
hideList();
return;
}
int cursorPos = cursorPosition();
QString rightText = allText.mid(cursorPos);
int pos = allText.lastIndexOf(QRegExp("[ .]"), cursorPos - 1);
if (pos != -1 && !selectedText.isEmpty())
{
QString leftText = allText.left(pos + 1);
setText(leftText + selectedText + rightText);
}
else if (!selectedText.isEmpty())
{
setText(selectedText + rightText);
}
else
{
setText(allText);
}
hideList();
}
bool CLineEdit::eventFilter(QObject *obj, QEvent *event)
{
if (obj == m_listView)
{
if (event->type() == QEvent::KeyPress)
{
keyPressEvent((QKeyEvent*)event);
return true;
}
if (event->type() == QEvent::FocusOut)
{
if(!m_listView->isHidden())
{
QPoint pos = cursor().pos();
if (m_listView->frameGeometry().contains(pos))
return true;
QModelIndex currentIndex = m_listView->currentIndex();
completeText(currentIndex);
}
emit editFinished();
}
}
return QLineEdit::eventFilter(obj, event);
}
void CLineEdit::keyPressEvent(QKeyEvent *event)
{
if (!m_listView->isHidden())
{
int key = event->key();
QModelIndex currentIndex = m_listView->currentIndex();
if (Qt::Key_Down == key)
{
int row = currentIndex.row() + 1;
QModelIndex index = m_listView->model()->index(row, 0);
m_listView->setCurrentIndex(index);
}
else if (Qt::Key_Up == key)
{
int row = currentIndex.row() - 1;
QModelIndex index = m_listView->model()->index(row, 0);
m_listView->setCurrentIndex(index);
}
else if (Qt::Key_Enter == key || Qt::Key_Return == key)
{
completeText(currentIndex);
}
else
{
hideList();
QLineEdit::keyPressEvent(event);
}
}
else
{
QLineEdit::keyPressEvent(event);
}
}
void CLineEdit::focusInEvent(QFocusEvent *event)
{
QLineEdit::focusInEvent(event);
}
void CLineEdit::focusOutEvent(QFocusEvent *event)
{
if (!m_listView->isHidden())
{
QPoint pos = cursor().pos();
if (m_listView->frameGeometry().contains(pos))
return;
QModelIndex currentIndex = m_listView->currentIndex();
completeText(currentIndex);
}
emit editFinished();
QLineEdit::focusOutEvent(event);
}
void CLineEdit::mouseDoubleClickEvent(QMouseEvent *event)
{
QLineEdit::mouseDoubleClickEvent(event);
}
#pragma once
#include <QtWidgets/QWidget>
#include "ui_Test1.h"
#include "MyStyledDelegate.h"
class Test1 : public QWidget
{
Q_OBJECT
public:
Test1(QWidget *parent = nullptr);
~Test1();
protected slots:
void onItemDoubleClicked(QTreeWidgetItem *item, int column);
private:
Ui::Test1Class ui;
MyStyledDelegate *m_delegate = nullptr;
};
#include "Test1.h"
Test1::Test1(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
m_delegate = new MyStyledDelegate(this);
connect(ui.treeWidget, &QTreeWidget::itemDoubleClicked, this, &Test1::onItemDoubleClicked);
}
Test1::~Test1()
{}
void Test1::onItemDoubleClicked(QTreeWidgetItem *item, int column)
{
item->setFlags(item->flags() | Qt::ItemIsEditable);
ui.treeWidget->setItemDelegateForColumn(column, m_delegate);
}