首先在介绍QListView之前,先说一下QListView和QListWidget的区别:
1、QListView是model(模型/视图)表格类型,QListWidget它是Item表格类型。
2、QListView使用较复杂,一般需要配合数据模型QAbstractListModel和item代理QStyledItemDelegate来使用。而QListWidget使用简单,只需要配合QListWidgetItem来使用。
3、QListView在大批量数据操作的场景下比QListTable快很多。原因是QListWidget它的数据都绑定了一个Item控件,数据较多时,对Item操作就较多。而QListView的数据就只绑定了一个代理,大批量数据操作时,只在代理中绘制这些数据。
了解它们的区别后,现在我们来详细了解下QListView它的使用方法。
QListView由于它是模型/视图的结构,所以它的数据交给了QAbstractListModel来处理,它的视图交给了QStyledItemDelegate来处理。
QAbstractListModel常用函数:
//获取指定行列的model项,model项可以用来获取数据。
virtual QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const = 0;
//行数
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const = 0;
//列数
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0;
//获取当前index位置的数据
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0;
//设置数据
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
//插入多行
virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
//插入多列
virtual bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex());
//删除多行
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
//删除多列
virtual bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex());
//插入行
bool insertRow(int row, const QModelIndex &parent = QModelIndex());
//插入列
bool insertColumn(int column, const QModelIndex &parent = QModelIndex());
//删除行
bool removeRow(int row, const QModelIndex &parent = QModelIndex());
//删除列
bool removeColumn(int column, const QModelIndex &parent = QModelIndex());
//更新数据
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector());
//指定起始插入位置插入,该方法一般用于重写InsertRows后调用。
// void beginInsertRows(const QModelIndex &parent, int first, int last);
//结束插入
void endInsertRows();
//指定起始插入位置删除,该方法一般用于重写removRows后调用
void beginRemoveRows(const QModelIndex &parent, int first, int last);
//结束删除
void endRemoveRows();
QStyledItemDelegate常用函数
//绘制Index项Ui
void paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const;
//设置index项大小
QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const override;
//捕获index位置上的事件。
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
QListView使用方法,首先需要加载Model和delegate。
m_listView->setItemDelegate(&m_delegate);
m_listView->setModel(&m_model);
然后继承QStyledItemDelegate进行绘制
//头文件
class MStyleItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MStyleItemDelegate();
void paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
};
//cpp文件
MStyleItemDelegate::MStyleItemDelegate()
{
}
void MStyleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (!index.isValid())
return;
//取数据
QString id = index.data(Qt::UserRole + 1).toString();
QString name = index.data(Qt::UserRole + 2).toString();
QString age = index.data(Qt::UserRole + 3).toString();
QStyleOptionViewItem viewoption(option);
initStyleOption(&viewoption, index);
if (option.state.testFlag(QStyle::State_HasFocus))
{
viewoption.state = viewoption.state ^ QStyle::State_HasFocus;
painter->setBrush(QBrush(QColor("#234567")));
painter->drawRect(option.rect);
}
else if (option.state.testFlag(QStyle::State_MouseOver))
{
painter->setBrush(QBrush(QColor("#543234")));
painter->drawRect(option.rect);
}
QStyledItemDelegate::paint(painter, viewoption, index);
//绘制文本
painter->save();
QRectF rect;
rect.setX(option.rect.x());
rect.setY(option.rect.y());
rect.setWidth(option.rect.width() - 1);
rect.setHeight(option.rect.height() - 1);
//设置字体,颜色
QFont font;
font.setFamily("Microsoft YaHei");
font.setPixelSize(12);
painter->setFont(font);
QRect textRect1(rect.left() + 10, rect.top() + TOP_MARGIN, option.widget->width()/3, HEIGHT);
QRect textRect2(option.widget->width()/4, rect.top() + TOP_MARGIN, option.widget->width()/4, rect.height());
QRect textRect3(option.widget->width()/4*2, rect.top() + TOP_MARGIN, option.widget->width()/4, rect.height());
QRect delRect(option.widget->width()/4*3, rect.top() + TOP_MARGIN, option.widget->width()/4, rect.height());
painter->setPen(QPen(TEXT_COLOR));
painter->setFont(QFont("Arial", TEXT_SIZE));
painter->drawText(textRect1, Qt::AlignLeft, QString::number(index.row()));
painter->drawText(textRect2, Qt::AlignLeft, name);
painter->drawText(textRect3, Qt::AlignLeft, age);
painter->drawText(delRect, Qt::AlignLeft, "删除");
//设置线的颜色
painter->setPen(QPen(QColor(LINE_COLOR)));
painter->drawLine(QPointF(0, rect.bottom() - 1), QPointF(rect.width(), rect.bottom() -1));
painter->restore();
}
//item大小
QSize MStyleItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
return QSize(option.rect.width(), LISTVIEW_ITEM_HIGHT);
}
bool MStyleItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
QRect rect = option.rect;
//获取checkbox的rect
QRect checboxRec(rect.right() - rect.width()/4, rect.top(), rect.height(), rect.width()/4);
//按钮点击事件,点击坐标是否在rect内
QMouseEvent* mevent = static_cast<QMouseEvent*>(event);
if (checboxRec.contains(mevent->pos()) && event->type() == QEvent::MouseButtonPress)
{
// model->setData(index, "No", Qt::UserRole + 1);
// model->dataChanged(index, index);
model->removeRows(index.row(),1,index);
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
然后继承QAbstractListModel进行数据处理
头文件
class MListModel : public QAbstractListModel
{
Q_OBJECT
public:
MListModel();
~MListModel();
void addRow(QVector<ListData> datas, int row, int count, const QModelIndex &parent = QModelIndex());
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
int rowCount(const QModelIndex& parent = QModelIndex()) const;
void setInsertOrder(int type = 0);
int getInsertOrder(){return m_insertOrder;}
private:
QVector<ListData> m_ListDataVec;
int m_insertOrder = 0;
};
cpp文件
MListModel::MListModel()
{
}
MListModel::~MListModel()
{
}
void MListModel::setInsertOrder(int type)
{
m_insertOrder = type;
}
void MListModel::addRow(QVector<ListData> datas, int row, int count, const QModelIndex &parent)
{
for (int i = 0; i < count; ++i) {
if (m_insertOrder)
m_ListDataVec.append(datas[i]);
else
m_ListDataVec.prepend(datas[i]);
}
insertRows(row,count,parent);
}
bool MListModel::insertRows(int row, int count, const QModelIndex &parent)
{
if (count < 1 || row < 0 || row > rowCount(parent))
return false;
beginInsertRows(parent,row,row + count - 1);
endInsertRows();
return true;
}
bool MListModel::removeRows(int row, int count, const QModelIndex &parent)
{
if (count <= 0 || row < 0 || (row + count) > rowCount(parent))
return false;
beginRemoveRows(parent, row, row + count - 1);
const auto it = m_ListDataVec.begin() + row;
m_ListDataVec.erase(it, it + count);
endRemoveRows();
return true;
}
QVariant MListModel::data(const QModelIndex &index, int role) const
{
QVariant bRet;
int nRow = index.row();
if (nRow >= m_ListDataVec.size() || (!index.isValid()))
return false;
ListData data = m_ListDataVec[nRow];
switch (role) {
case Qt::UserRole + 1:
bRet = data.id;
break;
case Qt::UserRole+2:
bRet = data.name;
break;
case Qt::UserRole+3:
bRet = data.age;
break;
default:
break;
}
return bRet;
}
bool MListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
bool bRet = false;
int nRow = index.row();
if (nRow >= m_ListDataVec.size() || (!index.isValid()))
return false;
ListData data = m_ListDataVec[nRow];
switch (role) {
case Qt::UserRole + 1:
data.id = value.toString();
bRet = true;
break;
case Qt::UserRole+2:
data.name = value.toString();
bRet = true;
case Qt::UserRole+3:
data.age = value.toString();
bRet = true;
default:
break;
}
m_ListDataVec.replace(nRow,data);
return bRet;
}
int MListModel::rowCount(const QModelIndex &parent) const
{
return m_ListDataVec.size();
}
注意有些函数后面带有一些const、override的关键字,这些必须带上,否则可能出现paint不绘制的情况。
完整代码
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QAbstractListModel>
#include <QStyledItemDelegate>
#include <QListView>
#include <QPainter>
#include <QMouseEvent>
//文字的高、宽及坐标点
#define LEFT_MARGIN 270
#define TOP_MARGIN 10
#define HEIGHT 50
//每个 item 的高度
#define LISTVIEW_ITEM_HIGHT 50
//分割 item 的线的颜色
#define LINE_COLOR "#ad2233"
//文字的颜色
#define TEXT_COLOR "#130c0e"
//文字的大小
#define TEXT_SIZE 12
struct ListData
{
QString id;
QString name;
QString age;
};
class MListModel : public QAbstractListModel
{
Q_OBJECT
public:
MListModel();
~MListModel();
void addRow(QVector<ListData> datas, int row, int count, const QModelIndex &parent = QModelIndex());
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
int rowCount(const QModelIndex& parent = QModelIndex()) const;
void setInsertOrder(int type = 0); //正逆序
int getInsertOrder(){return m_insertOrder;}
private:
QVector<ListData> m_ListDataVec;
int m_insertOrder = 0;
};
class MStyleItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MStyleItemDelegate();
void paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
};
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
protected:
void timerEvent(QTimerEvent *event);
private:
QListView* m_listView = nullptr;
MListModel m_model;
MStyleItemDelegate m_delegate;
bool m_start = false;
};
#endif // WIDGET_H
cpp文件
#include "widget.h"
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent)
{
QGridLayout* lay = new QGridLayout(this);
m_listView = new QListView(this);
QLabel* lab1 = new QLabel("ID",this);
QLabel* lab2 = new QLabel("NAME",this);
QLabel* lab3 = new QLabel("AGE",this);
QPushButton* btn = new QPushButton("start",this);
m_listView->setItemDelegate(&m_delegate);
m_listView->setModel(&m_model);
m_listView->setViewMode(QListView::ListMode); //设置Item显示模式为列表显示,也可是Icon模式,枚举值
m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_listView->setDragEnabled(false); //控件不允许拖动
m_model.setInsertOrder(0);
lay->addWidget(btn,0,0,1,1);
lay->addWidget(lab1,1,0,1,1);
lay->addWidget(lab2,1,1,1,1);
lay->addWidget(lab3,1,2,1,1);
lay->addWidget(m_listView,2,0,10,4);
lay->setSpacing(0);
setLayout(lay);
connect(btn,&QPushButton::clicked,this,[=]()
{
if (!m_start)
m_start = true;
else
m_start = false;
});
startTimer(500);
}
Widget::~Widget()
{
}
void Widget::timerEvent(QTimerEvent *event)
{
if (m_start)
{
int row = m_model.rowCount();
ListData data;
data.id = QString::number(row);
data.name = "name"+QString::number(row);
data.age = "age"+QString::number(row);
QVector<ListData> datas;
datas.append(data);
m_model.addRow(datas,row,1);
if (m_model.getInsertOrder() == 1)
m_listView->scrollToBottom();
}
}
MListModel::MListModel()
{
}
MListModel::~MListModel()
{
}
void MListModel::setInsertOrder(int type)
{
m_insertOrder = type;
}
void MListModel::addRow(QVector<ListData> datas, int row, int count, const QModelIndex &parent)
{
for (int i = 0; i < count; ++i) {
if (m_insertOrder)
m_ListDataVec.append(datas[i]);
else
m_ListDataVec.prepend(datas[i]);
}
insertRows(row,count,parent);
}
bool MListModel::insertRows(int row, int count, const QModelIndex &parent)
{
if (count < 1 || row < 0 || row > rowCount(parent))
return false;
beginInsertRows(parent,row,row + count - 1);
endInsertRows();
return true;
}
bool MListModel::removeRows(int row, int count, const QModelIndex &parent)
{
if (count <= 0 || row < 0 || (row + count) > rowCount(parent))
return false;
beginRemoveRows(parent, row, row + count - 1);
const auto it = m_ListDataVec.begin() + row;
m_ListDataVec.erase(it, it + count);
endRemoveRows();
return true;
}
QVariant MListModel::data(const QModelIndex &index, int role) const
{
QVariant bRet;
int nRow = index.row();
if (nRow >= m_ListDataVec.size() || (!index.isValid()))
return false;
ListData data = m_ListDataVec[nRow];
switch (role) {
case Qt::UserRole + 1:
bRet = data.id;
break;
case Qt::UserRole+2:
bRet = data.name;
break;
case Qt::UserRole+3:
bRet = data.age;
break;
default:
break;
}
return bRet;
}
bool MListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
bool bRet = false;
int nRow = index.row();
if (nRow >= m_ListDataVec.size() || (!index.isValid()))
return false;
ListData data = m_ListDataVec[nRow];
switch (role) {
case Qt::UserRole + 1:
data.id = value.toString();
bRet = true;
break;
case Qt::UserRole+2:
data.name = value.toString();
bRet = true;
case Qt::UserRole+3:
data.age = value.toString();
bRet = true;
default:
break;
}
m_ListDataVec.replace(nRow,data);
return bRet;
}
int MListModel::rowCount(const QModelIndex &parent) const
{
return m_ListDataVec.size();
}
MStyleItemDelegate::MStyleItemDelegate()
{
}
void MStyleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (!index.isValid())
return;
//取数据
QString id = index.data(Qt::UserRole + 1).toString();
QString name = index.data(Qt::UserRole + 2).toString();
QString age = index.data(Qt::UserRole + 3).toString();
QStyleOptionViewItem viewoption(option);
initStyleOption(&viewoption, index);
if (option.state.testFlag(QStyle::State_HasFocus))
{
viewoption.state = viewoption.state ^ QStyle::State_HasFocus;
painter->setBrush(QBrush(QColor("#234567")));
painter->drawRect(option.rect);
}
else if (option.state.testFlag(QStyle::State_MouseOver))
{
painter->setBrush(QBrush(QColor("#543234")));
painter->drawRect(option.rect);
}
QStyledItemDelegate::paint(painter, viewoption, index);
//绘制文本
painter->save();
QRectF rect;
rect.setX(option.rect.x());
rect.setY(option.rect.y());
rect.setWidth(option.rect.width() - 1);
rect.setHeight(option.rect.height() - 1);
//设置字体,颜色
QFont font;
font.setFamily("Microsoft YaHei");
font.setPixelSize(12);
painter->setFont(font);
QRect textRect1(rect.left() + 10, rect.top() + TOP_MARGIN, option.widget->width()/3, HEIGHT);
QRect textRect2(option.widget->width()/4, rect.top() + TOP_MARGIN, option.widget->width()/4, rect.height());
QRect textRect3(option.widget->width()/4*2, rect.top() + TOP_MARGIN, option.widget->width()/4, rect.height());
QRect delRect(option.widget->width()/4*3, rect.top() + TOP_MARGIN, option.widget->width()/4, rect.height());
painter->setPen(QPen(TEXT_COLOR));
painter->setFont(QFont("Arial", TEXT_SIZE));
painter->drawText(textRect1, Qt::AlignLeft, QString::number(index.row()));
painter->drawText(textRect2, Qt::AlignLeft, name);
painter->drawText(textRect3, Qt::AlignLeft, age);
painter->drawText(delRect, Qt::AlignLeft, "删除");
//设置线的颜色
painter->setPen(QPen(QColor(LINE_COLOR)));
painter->drawLine(QPointF(0, rect.bottom() - 1), QPointF(rect.width(), rect.bottom() -1));
painter->restore();
}
//item大小
QSize MStyleItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
return QSize(option.rect.width(), LISTVIEW_ITEM_HIGHT);
}
bool MStyleItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
QRect rect = option.rect;
//获取checkbox的rect
QRect checboxRec(rect.right() - rect.width()/4, rect.top(), rect.height(), rect.width()/4);
//按钮点击事件,点击坐标是否在rect内
QMouseEvent* mevent = static_cast<QMouseEvent*>(event);
if (checboxRec.contains(mevent->pos()) && event->type() == QEvent::MouseButtonPress)
{
// model->setData(index, "No", Qt::UserRole + 1);
// model->dataChanged(index, index);
model->removeRows(index.row(),1,index);
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}