Using various custom delegates in QTableView

QT's MVC (View/Delegate) model is very powerful and can use various controls to limit table input.

Ideas:
1: Define delegation for each column:
A: The first column is the number column, use a read-only delegation to make the cells in this column read-only
B: The third column is the ID column, only 1-12 can be entered Numbers, use the QLineEdit delegate and regular expressions to limit the input.
C: The fourth age column, use the QSpinBox delegate to limit the input. Only numbers between 1 and 100 can be entered.
D: The fifth column is the gender column, use the QComboBox delegate to limit the input. The input is restricted. Only Male or Female can be entered in the cells of this column.
E: The sixth column is the avatar column. Place an avatar in the center of the cell in this column.
2: Define the agent class and display the characters in all cells in the center. .
3: Use QSS to change the background color of the table to yellow and blue.

Above code:

#include <QtGui>
#include <QItemDelegate>
#include <QSpinBox>
//编号列,只读委托
//这个方法我还真想不到,呵呵
class ReadOnlyDelegate : public QItemDelegate
{
Q_OBJECT
public:
ReadOnlyDelegate(QObject *parent = 0): QItemDelegate(parent) { }
QWidget *createEditor(QWidget*parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
return NULL;
}
};
//ID列,只能输入1-12个数字
//利用QLineEdit委托和正则表达式对输入进行限制
class UserIDDelegate : public QItemDelegate
{
Q_OBJECT
public:
UserIDDelegate(QObject *parent = 0): QItemDelegate(parent) { }
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QLineEdit *editor = new QLineEdit(parent);
QRegExp regExp("[0-9]{0,10}");
editor->setValidator(new QRegExpValidator(regExp, parent));
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString text = index.model()->data(index, Qt::EditRole).toString();
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(text);
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
QString text = lineEdit->text();
model->setData(index, text, Qt::EditRole);
}
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
};
//年龄列,利用QSpinBox委托进行输入限制,只能输入1-100之间的数字
class AgeDelegate : public QItemDelegate
{
Q_OBJECT
public:
AgeDelegate(QObject *parent = 0): QItemDelegate(parent) { }
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QSpinBox *editor = new QSpinBox(parent);
editor->setMinimum(1);
editor->setMaximum(100);
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
};
//性别列,利用QComboBox委托对输入进行限制
//这一列的单元格只能输入Male或Female
class SexDelegate : public QItemDelegate
{
Q_OBJECT
public:
SexDelegate(QObject *parent = 0): QItemDelegate(parent) { }
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItem("Female");
editor->addItem("Male");
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString text = index.model()->data(index, Qt::EditRole).toString();
QComboBox *comboBox = static_cast<QComboBox*>(editor);
int tindex = comboBox->findText(text);
comboBox->setCurrentIndex(tindex);
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
QString text = comboBox->currentText();
model->setData(index, text, Qt::EditRole);
}
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
};
//头像列,只是在单元格中央放一张小图而已
class IconDelegate : public QItemDelegate
{
Q_OBJECT
public:
IconDelegate(QObject *parent = 0): QItemDelegate(parent) { }
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex & index ) const
{
//show.bmp是在工程目录中的一张图片(其实就是QQ的图标啦,呵呵)
QPixmap pixmap = QPixmap("show.bmp").scaled(24, 24);
qApp->style()->drawItemPixmap(painter, option.rect, Qt::AlignCenter, QPixmap(pixmap));
}
};
//代理类,把所有单元格中的字符居中显示
class VIPModel : public QStandardItemModel
{
Q_OBJECT
public:
VIPModel(QObject *parent=NULL) : QStandardItemModel(parent) { }
VIPModel(int row, int column, QObject *parent=NULL)
: QStandardItemModel(row, column, parent) { }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
{
if( Qt::TextAlignmentRole == role )
return Qt::AlignCenter;
return QStandardItemModel::data(index, role);
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
VIPModel *model = new VIPModel(5, 5);
QTableView *tableView = new QTableView;
//把表格的背景调成黄蓝相间
//这种方法是在网上看到的,用起来还真方便啊
tableView->setAlternatingRowColors(true);
tableView->setStyleSheet("QTableView{background-color: rgb(250, 250, 115);"
"alternate-background-color: rgb(141, 163, 215);}");
tableView->setWindowTitle("VIP List");
tableView->resize(700, 400);
tableView->setModel(model);
QStringList headerList;
headerList << "No." << "ID" << "Name" << "Age" << "Sex" << "Show";
model->setHorizontalHeaderLabels(headerList);
tableView->verticalHeader()->setVisible(false);
tableView->horizontalHeader()->setStretchLastSection(true);
//为每一列加载委托
ReadOnlyDelegate readOnlyDelegate;
//tableView->setItemDelegateForColumn(0, &readOnlyDelegate); //我用了会报错
tableView->setItemDelegateForColumn(0, new ReadonlyDelegate(this)); //我用的是这种
UserIDDelegate userIDDelegate;
tableView->setItemDelegateForColumn(1, &userIDDelegate);
AgeDelegate spinBoxDelegate;
tableView->setItemDelegateForColumn(3, &spinBoxDelegate);
SexDelegate comboBoxDelegate;
tableView->setItemDelegateForColumn(4, &comboBoxDelegate);
IconDelegate iconDelegate;
tableView->setItemDelegateForColumn(5, &iconDelegate);
for(int i=0; i<10; i++)
{
QModelIndex index = model->index(i, 0, QModelIndex());
model->setData(index, i);
}
tableView->show();
return app.exec();
}

QTableView、QStandardItemModel/QAbstractItemModel、QStyledItemDelegate/QItemDelegate和QModelIndex;

Let’s take a look at the picture first to see the effect.

The next line is the full path of the selected file displayed.

Line 2 "C:" is the value set during initialization, just for test display.

The last line is no file selected.

This SelectFileButton method I implemented:

QPushButton;

Implement a slot function in SelectFileButton: function, select the file and return the path when clicked.

cQItemDelegate;

d. Then rewrite createEditor(); return the pointer of SelectFileButton

e. Rewrite setModelData(), setEditorData(); set model data and set editing data.

As a benefit for this article, you can receive a Qt development learning package and technical videos for free, including (C++ language basics, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project practice , QT embedded development, Quick module, etc.) ↓↓↓↓↓↓See below↓↓Click at the bottom of the article to receive the fee↓↓

#ifndef SELECTFILEBUTTONDELEGATE_H
#define SELECTFILEBUTTONDELEGATE_H
#include <QItemDelegate>
#include <QPushButton>
#include <QModelIndex>
#include <QFileDialog>
#include <QAbstractItemModel>
#include <QDebug>
class TableModel;
class SelectFileButton: public QPushButton
{
Q_OBJECT
public:
SelectFileButton(const QModelIndex & index, QWidget *parent = 0)
: QPushButton(parent)
{
Init(index);
}
SelectFileButton(const QModelIndex & index, const QString &text, QWidget *parent = 0)
: QPushButton(text, parent)
{
Init(index);
}
SelectFileButton(const QModelIndex & index, const QIcon& icon, const QString &text, QWidget *parent = 0)
:QPushButton(icon, text, parent)
{
Init(index);
}
~SelectFileButton() {
qDebug() << "~SelectFileButton";
}
protected:
void Init(const QModelIndex & index)
{
m_index = index;
connect(this, SIGNAL(clicked()), this, SLOT(btnClick_slot()));
}
public slots:
void btnClick_slot()
{
QString strValue;
QModelIndex index = this->m_index;
QFileDialog * dlg = new QFileDialog(0);
int res =dlg->exec(); //这里开启新的线程,顶层会释放这个按钮,所以要把Index 保存一份
if (res==QFileDialog::Accepted)
{
strValue = dlg->selectedFiles()[0];
QAbstractItemModel* model = const_cast<QAbstractItemModel*>(index.model());
model->setData(index, strValue);
}
}
private:
QModelIndex m_index;
};
/**********************************************************************/
class SelectFileButtonDelegate : public QItemDelegate
{
Q_OBJECT
public:
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
SelectFileButton* btn = new SelectFileButton(index,QStringLiteral("查找文件路径"), parent); //这个是其他线程里的 所以不能保存下来。每次的btn还不一样
return btn;
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
public:
SelectFileButtonDelegate(QObject *parent);
~SelectFileButtonDelegate();
private:
QPushButton* m_btn;
};
#endif // SELECTFILEBUTTONDELEGATE_H

Draw custom controls through painter

QStyleOption subclasses:

QStyleOptionButton, QStyleOptionComplex, QStyleOptionDockWidget, QStyleOptionFocusRect, QStyleOptionFrame, QStyleOptionGraphicsItem, QStyleOptionHeader, QStyleOptionMenuItem, QStyleOptionProgressBar, QStyleOptionRubberBand, QStyleOptionTab, QStyleOptionTabBarBase, QStyleOptionTabWidgetFrame, QStyleOptionToolBar, QStyleOptionToolBox, and QStyleOptionViewItem

1. Painter custom progress bar

//自定义进度条
BarDelegate::BarDelegate( QObject *parent ) : QAbstractItemDelegate( parent ) { }

void BarDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
  if( option.state & QStyle::State_Selected )
    painter->fillRect( option.rect, option.palette.highlight() );

  // 数据是存储在QStandardItemModel的QStandardItem,会自动根据当前行进行匹配(我认为)
  int value = index.model()->data( index, Qt::DisplayRole ).toInt(); // 这句,取得当前行的数据
  qDebug() << value;
  double factor = double(value)/100.0; // 计算比例因子

  painter->save(); // 保存旧画板(我认为)
  // 进度条背景色
  if( factor > 0.8 )
  {
    painter->setBrush( Qt::red ); // 超过0.8画纯红色
    factor = 1;
  }
  else
    painter->setBrush( QColor( 0, int(factor*255), 255-int(factor*255) ) ); // 否则颜色依次变淡

  int n = factor*100;
  QString str1 = QString("%1\%").arg(n);
  painter->setPen( Qt::red ); // 画笔颜色(这里没用到,我认为)
  // 前面都是准备工作,这里才是真正画进度条
  painter->drawRect( option.rect.x()+2, option.rect.y()+2, int(factor*(option.rect.width()-5)), option.rect.height()-5 );
  painter->drawText(option.rect.x()+int(factor*(option.rect.width()))+2, option.rect.y()+option.rect.height()/2, str1);
  painter->restore(); // 恢复新画板(我认为)
}

QSize BarDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
  return QSize( 45, 15 ); // 随便改,不影响(我认为)
}
class BarDelegate : public QAbstractItemDelegate
{
public:
  BarDelegate( QObject *parent = 0 );
  // 覆盖两个函数就可以显示进度条
  void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
  QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const;
};

1 painter progress bar QStyleOptionProgressBar

void ProgressBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.column() == 0) {
        int value = index.model()->data(index).toInt();
        QStyleOptionProgressBarV2 progressBarOption;
        progressBarOption.rect = option.rect.adjusted(4, 4, -4, -4);
        progressBarOption.minimum = 0;
        progressBarOption.maximum = 100;
        progressBarOption.textAlignment = Qt::AlignRight;
        progressBarOption.textVisible = true;
        progressBarOption.progress = value;
        progressBarOption.text = tr("%1%").arg(progressBarOption.progress);

        painter->save();
        if (option.state & QStyle::State_Selected) {
            painter->fillRect(option.rect, option.palette.highlight());
                    painter->setBrush(option.palette.highlightedText());
        }
                QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);

        painter->restore();

    } else {
        return QItemDelegate::paint (painter, option, index);
    }
}

2. Painter custom button QStyTableViewDelegate::TableViewDelegate(int z_Column, int z_NumberButtons,QWidget *parent)

    : QStyledItemDelegate(parent),
      m_pOpenButton(new QPushButton()),
      m_pDeleteButton(new QPushButton()),
      m_nSpacing(5),
      m_nWidth(25),
      m_nHeight(20),
      m_Column(z_Column),
      m_NumberButtons(z_NumberButtons)
{if (2 == m_NumberButtons)
    {
        m_list << QStringLiteral("编辑按钮") << QStringLiteral("删除按钮");
    }
    // 设置按钮正常、划过、按下样式
    QString z_Path = QCoreApplication::applicationDirPath();
    m_pOpenButton->setStyleSheet("QPushButton {border: none; background-color: transparent; image:url("+z_Path+"/res/system.png);} \
                                 QPushButton:hover {image:url("+z_Path+"/res/system.png);} \
                                 QPushButton:pressed {image:url("+z_Path+"/res/system.png);}");

     m_pDeleteButton->setStyleSheet("QPushButton {border: none; background-color: transparent; image:url("+z_Path+"/res/delete.png);} \
                                QPushButton:hover {image:url("+z_Path+"/res/delete.png);} \
                                QPushButton:pressed {image:url("+z_Path+"/res/delete.png);}");
}

TableViewDelegate::~TableViewDelegate()
{

}

// 绘制按钮
void TableViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItem viewOption(option);
    initStyleOption(&viewOption, index);
    if (option.state.testFlag(QStyle::State_HasFocus))
        viewOption.state = viewOption.state ^ QStyle::State_HasFocus;

    QStyledItemDelegate::paint(painter, viewOption, index);

    if (index.column() == m_Column)
    {
        // 计算按钮显示区域
        int nCount = m_list.count();
        int nHalf = (option.rect.width() - m_nWidth * nCount - m_nSpacing * (nCount - 1)) / 2;
        int nTop = (option.rect.height() - m_nHeight) / 2;

        for (int i = 0; i < nCount; ++i)
        {
            // 绘制按钮
            QStyleOptionButton button;
            button.rect = QRect(option.rect.left() + nHalf + m_nWidth * i + m_nSpacing * i,
                                option.rect.top() + nTop,  m_nWidth, m_nHeight);
            button.state |= QStyle::State_Enabled;
            button.iconSize = QSize(20, 20);

            if (button.rect.contains(m_mousePoint))
            {
                if (m_nType == 0)
                {
                    button.state |= QStyle::State_MouseOver;
                    //button.icon = QIcon(QString(":/Images/%1Hover").arg(m_list.at(i)));
                }
                else if (m_nType == 1)
                {
                    button.state |= QStyle::State_Sunken;
                    //button.icon = QIcon(QString(":/Images/%1Pressed").arg(m_list.at(i)));
                }
            }

            //QWidget *pWidget = m_pOpenButton.data();
            QWidget *pWidget = (i == 0) ? m_pOpenButton.data() : m_pDeleteButton.data();
            pWidget->style()->drawControl(QStyle::CE_PushButton, &button, painter, pWidget);
        }
    }
}

// 响应按钮事件 - 划过、按下
bool TableViewDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
    if (index.column() != m_Column)
        return false;

    m_nType = -1;
    bool bRepaint = false;
    QMouseEvent *pEvent = static_cast<QMouseEvent *> (event);
    m_mousePoint = pEvent->pos();

    int nCount = m_list.count();
    int nHalf = (option.rect.width() - m_nWidth * nCount - m_nSpacing * (nCount - 1)) / 2;
    int nTop = (option.rect.height() - m_nHeight) / 2;

    // 还原鼠标样式
    QApplication::restoreOverrideCursor();

    for (int i = 0; i < nCount; ++i)
    {
        QStyleOptionButton button;
        button.rect = QRect(option.rect.left() + nHalf + m_nWidth * i + m_nSpacing * i,
                            option.rect.top() + nTop,  m_nWidth, m_nHeight);

        // 鼠标位于按钮之上
        if (!button.rect.contains(m_mousePoint))
            continue;

        bRepaint = true;
        switch (event->type())
        {
        // 鼠标滑过
        case QEvent::MouseMove:
        {
            // 设置鼠标样式为手型
            //QApplication::setOverrideCursor(Qt::PointingHandCursor);

            m_nType = 0;
            QToolTip::showText(pEvent->globalPos(), m_list.at(i));
            break;
        }
            // 鼠标按下
        case QEvent::MouseButtonPress:
        {
            m_nType = 1;
            break;
        }
            // 鼠标释放
        case QEvent::MouseButtonRelease:
        {
            if (i == 0)
            {
                emit EditData(index);
            }
            else
            {
                emit deleteData(index);
            }
            break;
        }
        default:
            break;
        }
    }

    return bRepaint;
}

Three painter settings picture

void ItemdelegateTest::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItem  viewOption(option);
    if (viewOption.state & QStyle::State_HasFocus)
    {
        viewOption.state = viewOption.state ^ QStyle::State_HasFocus;
    }

    QStyledItemDelegate::paint(painter, viewOption, index);

    int height = (viewOption.rect.height()) / 2;
    QPixmap pixmap = QPixmap(":/check.png");
    //QPixmap pixmap = QPixmap("E:/qt/QtDelegateTestT/zhj.png");
    QRect DrawRect = QRect(viewOption.rect.left() + viewOption.rect.width() - 30, viewOption.rect.top() + height, 9, 9);
    painter->drawPixmap(DrawRect, pixmap);
}

bool ItemdelegateTest::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    int height = (option.rect.height()) / 2;
    QRect DrawRect = QRect(option.rect.left() + option.rect.width() - 30, option.rect.top() + height, 9, 9);

    //QMouseEvent *mouseEvent = static_cast(event);
    QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
    if (event->type() == QEvent::MouseButtonPress && DrawRect.contains(mouseEvent->pos()))
    {
       emit deleteItem(index);
    }

    if (event->type() == QEvent::MouseMove && DrawRect.contains(mouseEvent->pos()))
    {
        QCursor cursor(Qt::PointingHandCursor);
        QApplication::setOverrideCursor(cursor);
        QToolTip::showText(mouseEvent->globalPos(), "删除");
    }
    else
    {
        QCursor cursor(Qt::ArrowCursor);
        QApplication::setOverrideCursor(cursor);
    }

    return QStyledItemDelegate::editorEvent(event, model, option, index);
}

panier draws checkbox

CheckBoxDelegate::CheckBoxDelegate(QObject *parent)
    : QStyledItemDelegate(parent)
{

}

CheckBoxDelegate::~CheckBoxDelegate()
{

}

// 绘制复选框
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItem viewOption(option);
    initStyleOption(&viewOption, index);
    if (option.state.testFlag(QStyle::State_HasFocus))
        viewOption.state = viewOption.state ^ QStyle::State_HasFocus;

    QStyledItemDelegate::paint(painter, viewOption, index);

    if (index.column() == CHECK_BOX_COLUMN)
    {
        bool data = index.model()->data(index, Qt::UserRole).toBool();

        QStyleOptionButton checkBoxStyle;
        checkBoxStyle.state = data ? QStyle::State_On : QStyle::State_Off;
        checkBoxStyle.state |= QStyle::State_Enabled;
        checkBoxStyle.iconSize = QSize(20, 20);
        checkBoxStyle.rect = option.rect;

        QCheckBox checkBox;
        QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkBoxStyle, painter, &checkBox);
    }
}

// 响应鼠标事件,更新数据
bool CheckBoxDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    QRect decorationRect = option.rect;

    QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
    if (event->type() == QEvent::MouseButtonPress && decorationRect.contains(mouseEvent->pos()))
    {
        if (index.column() == CHECK_BOX_COLUMN)
        {
            bool data = model->data(index, Qt::UserRole).toBool();
            model->setData(index, !data, Qt::UserRole);
        }
    }

    return QStyledItemDelegate::editorEvent(event, model, option, index);
}

Original link: https://www.cnblogs.com/whwywzhj/p/8883634.html

As a benefit for this article, you can receive a Qt development learning package and technical videos for free, including (C++ language basics, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project practice , QT embedded development, Quick module, etc.) ↓↓↓↓↓↓See below↓↓Click at the bottom of the article to receive the fee↓↓

Guess you like

Origin blog.csdn.net/hw5230/article/details/132763692