listView 对比 tableView 、 treeView来说,最大的不同就是数据结构的不同。treeView是像树一样的层次结构,而listView则就是像链表一样的结构
跟之前的treeView,tableView一样,使用一个简单的demo来演示一下这个过程,同样的我们使用自定义的model定义。
实现一个简单的demo,一个item里有图片、按钮、文字三个部分组成,双击文字出现combobox下拉框。按钮分为选中和非选中,单机修改按钮状态,通过一个额外的按钮将所有按钮为选中的那行item删除
listViewDemo
listView的数据结构似乎比较简单,这里直接用一个List链表来当做数据源了
struct ListData
{
bool m_isSelect;
QString m_txt;
};
class ListModel : public QAbstractListModel
{
Q_OBJECT
public:
ListModel();
~ListModel();
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
QModelIndex index(int row, int column, const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
Qt::ItemFlags flags(const QModelIndex& index) const override;
void insertData(const ListData &data);
QList getData() {return m_listData;}
void SetData(const QList &data){
beginResetModel();
m_listData = data;
endResetModel();
}
private:
QList m_listData;
};
从demo的定义来看,我们需要在一个item上绘制按钮、文本框、图片,这里我们需要使用到委托。
委托中的关键函数:
class MyDelegate : public QStyledItemDelegate
{
Q_OBJECT;
public:
MyDelegate();
~MyDelegate();
//描绘画面显示
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
//处理鼠标事件
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const;
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const ; //为editor提供编辑的原始数据
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const ;
};
我们需要在paint中绘制我们需要的item
void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QRect retc = option.rect;
//这里取的数据 对应model里面的role
bool isSelect = index.data(Qt::UserRole + 1).toBool();
QString txt = index.data(Qt::UserRole + 2).toString();
{
// picture begin
QPixmap pix;
painter -> save();
if (pix.isNull())
{
auto success = pix.load("D:/3.ico");
}
painter->setPen(QColor("#Ff0000"));
painter->setPen(Qt::DashDotLine);
painter->drawRect(retc);
int xoffset = 10;
auto szPix = pix.size();
auto dy = szPix.height() * 1.0 / retc.height();
auto dx = szPix.width() * 1.0 / retc.width();
auto d = std::max(dy, dx);
int w = szPix.width() / d;
int h = szPix.height() /d;
auto x = retc.left() + xoffset;
auto y = retc.top() + (retc.height() - h) / 2;
painter->drawPixmap(x, y, w, h, pix);
// picture end
// buttom begin
g_hight = h;
//p11.setX(x);
p11 = QPoint(x, (retc.height() - h) / 2);
x = x + w + 50;
//绘制按钮
QRect butRect(x, y, 100, h);
p22 = QPoint(x, p11.y());
if(isSelect)
{
painter->setPen(QPen(QColor("#8f9ae6")));
painter->setBrush(QColor("#8f9ae6"));
painter->setPen(QPen(QColor("#000000")));
painter->drawRoundedRect(butRect,0,0);
painter->drawText(butRect,Qt::AlignCenter,"选中");
}else
{
painter->setPen(QPen(QColor("#FFFFFF")));
painter->setBrush(QColor("#FFFFFF"));
painter->setPen(QPen(QColor("#000000")));
painter->drawRoundedRect(butRect,0,10);
painter->drawText(butRect,Qt::AlignCenter,"未选");
}
//buttom end
//text begin
butRect = QRect(w * 3 + 150, y, 200, h);
p33 = QPoint(butRect.left(), p11.y());
painter->drawRoundedRect(butRect,0,10);
painter->drawText(butRect,Qt::AlignCenter,txt);
painter->restore();
return;
}
}
我们需要一个Combobox的Editor来做下拉框
QWidget * MyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStringList m_ls_itemtext;
m_ls_itemtext<<"第一行"<<"第二行"<<"第三行"<<"第四行"<<"第五行"<<"第六行"<<"第七行"<<"第八行"<<"第九行";
QComboBox *editor = new QComboBox(parent);
editor->setEditable(true);
editor->addItems(m_ls_itemtext);
return editor;
}
void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
if (!index.isValid())
{
return;
}
QString value = index.model()->data(index, Qt::EditRole).toString();
QComboBox *comboBox = static_cast(editor);
int icurIndex = comboBox->findText(value);
if (icurIndex <= -1)
{
icurIndex = 0;
}
comboBox->setCurrentIndex(icurIndex);
}
双击选中的下拉框应该在此item的那一行位置
void MyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
auto xx = option.rect;
auto yy = editor->rect();
auto x = p33.x() + (200 - editor->geometry().width()) / 2;
editor->move(x, xx.top());
}
我们需要把选中下拉框的值给到model
void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
if (dynamic_cast(editor)->currentText() == "")
{
return;
}
model->setData(index, dynamic_cast(editor)->currentText(), Qt::UserRole+2);
}
我们通过直接通过一个 List 实现 model 的数据源,通过 paint 来绘制每一个item(每个item里可以放一些内容如按钮),通过 editorEvent 来处理鼠标的点击事件,通过 updateEditorGeometry 来处理更新编辑器的位置, createEditor、setEditorData 来创建和设置一个需要的Editor(下拉框、文本行等等),通过 setModelData 来将editor修改的值返回给model来交互,model值修改了又会反馈给View,达到修改视图的效果