提要
最近想要完成的一个项目需要显示一个列表,可以动态增减,可编辑,有checkbox。类似于这样
或者这样
但网上的例子都是这样
和这样
...
最后实现的效果:
QListWidget解决方案
在Android实现这样的列表是非常简单的,首先定义布局XML,然后再定义一个Adapter就可以了。
Qt中类似的解决方案就是QListWidget。
自定义一个Widget类作为Item,比如
class UsersListItem : public QWidget { Q_OBJECT public: explicit UsersListItem(QWidget *parent = 0); virtual QSize sizeHint() const { return QSize(100,48); } private: QLabel * m_labAddress; QLabel * m_labHeaderPic; QLabel * m_labUser; signals: public slots: };
然后用
void QListWidget::setItemWidget ( QListWidgetItem * item, QWidget * widget )
将widget作为item显示。
这种方法最为简单,但是仔细看下官网的函数说明....
This function should only be used to display static content in the place of a list widget item. If you want to display custom dynamic content or implement a custom editor widget, use QListView and subclass QItemDelegate instead.
只能用来显示静态数据,如果要显示动态数据,请使用QListView和QItemDelegate。
自定义Model自定义View 自定义Delegate
首先要明白这几个东西的概念和关系
与MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件。一般来讲, view负责把数据展示给用户,也处理用户的输入。为了获得更多的灵性性,交互通过delegagte执行。它既提供输入功能又负责渲染view中的每个数据项。
使用Delegate的原因 Qt中当用到QTreeView和QTableView等用于显示item的视图时,你要编辑一个item用到的编辑工具可能是除了默认文字编辑lineEdit以外的工具,例如button,spinBox,甚至Slider,ProgressBar,也有可能是自定义的widget。所以Qt提供了一个委托类,用来处理View中的数据展示方式。
Qt提供的标准views都使用QItemDelegate的实例来提供编辑功能。它以普通的风格来为每个标准view渲染数据项。这些标准的views包括:QListView,QTableView,QTreeView。所有标准的角色都通过标准views包含的缺省delegate进行处理。一个view使用的delegate可以用itemDelegate()函数取得,而setItemDelegate() 函数可以安装一个定制delegate。
简单的说就是需要自己实现的东西变多了,但是可以定制的地方也就多了。下面主要是贴代码,慎入!
首先是model,
layertablemodel.h
#ifndef LAYERLISTMODEL_H #define LAYERLISTMODEL_H #include <QAbstractTableModel> #include <QStringList> #include <QList> #include <QPixmap> #include <QImage> #include <QIcon> #include <QDebug> #include <QItemSelectionModel> class LayerTableModel : public QAbstractTableModel { Q_OBJECT public: LayerTableModel(QObject *parent = 0); ~LayerTableModel(); int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value, int role); void deleteItem(int index); void addItem(QString &layerName, QImage &thumbnail, bool isShow = true); void refreshModel(); QModelIndex& selecttedIndex(int row); void setSelecttedRow(int row); int getSelecttedRow() const; public slots: void changeLayerVisibility(const QModelIndex&); private: struct LayerItem { QString layerName; QImage thumbnail; float transparence; bool isShow; }; QList<LayerItem> layerList; int selectedRow; }; #endif // LAYERLISTMODEL_H
layertablemodel.cpp
#include "layertablemodel.h" LayerTableModel::LayerTableModel(QObject *parent) : QAbstractTableModel(parent) { QImage image("images\\sample.jpg"); layerList.reserve(3); selectedRow = 0; for (int i = 0; i < 3; i++) { addItem(QString(), image, true); } } LayerTableModel::~LayerTableModel() { } QVariant LayerTableModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); int column = index.column(); if (column == 0) { if(role == Qt::CheckStateRole) { return layerList.at(index.row()).isShow ? Qt::Checked : Qt::Unchecked; } if (role == Qt::SizeHintRole) { return QSize(20, 50); } } else { if (role == Qt::EditRole) { return QVariant(layerList.at(index.row()).layerName); } if (role == Qt::DisplayRole) { return QVariant(layerList.at(index.row()).layerName); } if (role == Qt::DecorationRole) { if (layerList.at(index.row()).thumbnail.isNull()) { return layerList.at(index.row()).thumbnail; }else { return layerList.at(index.row()).thumbnail.scaledToHeight(40); } } if (role == Qt::SizeHintRole) { return QSize(200, 50); } if (role == Qt::TextAlignmentRole) { return int(Qt::AlignVCenter); } } return QVariant(); } int LayerTableModel::rowCount(const QModelIndex &parent) const { return (parent.isValid() && parent.column() != 0) ? 0 : layerList.size(); } int LayerTableModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } QVariant LayerTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole) return QString::number(section); //if (role == Qt::DecorationRole) //return QVariant::fromValue(services); return QAbstractItemModel::headerData(section, orientation, role); } Qt::ItemFlags LayerTableModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; if (index.column() == 0) return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; } bool LayerTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) { return false; } if (role == Qt::CheckStateRole && value.type() == QVariant::Bool) { layerList[index.row()].isShow = value.toBool(); emit(dataChanged(index, index)); return true; } if (role == Qt::EditRole && index.column() == 1) { layerList[index.row()].layerName = value.toString(); emit(dataChanged(index, index)); return true; } return false;; } void LayerTableModel::changeLayerVisibility(const QModelIndex& index) { if (index.isValid()&&index.column() == 0) { setData(index, !(layerList.at(index.row()).isShow), Qt::CheckStateRole); } } void LayerTableModel::deleteItem(int index) { QList<LayerItem>::iterator it = layerList.begin(); layerList.erase(it + index); } void LayerTableModel::addItem(QString &name, QImage &thumb, bool show) { LayerItem item; if (name.size() == 0) { item.layerName = QString("Layer ") + QString::number(layerList.size()); }else{ item.layerName = name; } item.isShow = show; item.thumbnail = thumb; layerList.append(item); //this->insertRow() //emit(dataChanged(index, index)); qDebug()<<layerList.size(); } void LayerTableModel::refreshModel() { beginResetModel(); endResetModel(); //emit updateCount(this->rowCount(QModelIndex())); } QModelIndex& LayerTableModel::selecttedIndex(int row) { return this->createIndex(row, 1); } void LayerTableModel::setSelecttedRow(int row) { selectedRow = row; } int LayerTableModel::getSelecttedRow() const { return selectedRow; }
然后是delegate
layeritemdelegate.h
#ifndef LAYERITEMDELEGATE_H #define LAYERITEMDELEGATE_H #include <QStyledItemDelegate> #include <QLineEdit> #include <QDebug> #include <QPainter> class LayerItemDelegate : public QStyledItemDelegate { Q_OBJECT public: LayerItemDelegate(QObject *parent=0); ~LayerItemDelegate(); void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index); void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; private: QPixmap m_gridPixmap; }; #endif // LAYERITEMDELEGATE_H
layeritemdelegate.cpp
#include "layeritemdelegate.h" LayerItemDelegate::LayerItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { QImage gridImage(200, 200, QImage::Format_RGB32); QRgb grey = QColor(204, 204, 204).rgb(); QRgb white = QColor(255, 255, 255).rgb(); for (int i = 0; i < 200; i++) for (int j = 0; j < 200; j++) { int tmpX = i % 10; int tmpY = j % 10; if (tmpX < 5) { gridImage.setPixel(i, j, tmpY < 5 ? grey : white); } else { gridImage.setPixel(i, j, tmpY < 5 ? white : grey); } } m_gridPixmap = QPixmap::fromImage(gridImage); } LayerItemDelegate::~LayerItemDelegate() { } void LayerItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { if (index.column() == 1) // value column { if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); QImage image = qvariant_cast<QImage>(index.data(Qt::DecorationRole)); //QImage image = index.model()->data(index, Qt::DecorationRole).toString(); QRect rect = option.rect; int x = rect.x() + 10; int y = rect.y() + 5; QBrush brush; //Draw grid background brush.setTexture(m_gridPixmap); painter->fillRect(x, y, 40, 40, brush); //Draw image painter->drawImage(x, y, image); QRect textRect(rect.x() + 60, rect.y(), rect.width() - 60, rect.height()); QString layerName = index.model()->data(index, Qt::DisplayRole).toString(); QTextOption textOption = Qt::AlignLeft | Qt::AlignVCenter; painter->drawText(textRect, layerName, textOption); } else { QStyledItemDelegate::paint(painter, option, index); } } bool LayerItemDelegate::editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) { return false; } QWidget *LayerItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { qDebug() << "createEditor"; if (index.column() == 1) // value column { QLineEdit* edit = new QLineEdit(parent); edit->setFixedHeight(33); edit->setContentsMargins(48, 15, 50, 0); return edit; } else return 0; // no editor attached } void LayerItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QString value = index.model()->data(index, Qt::EditRole).toString(); QLineEdit *edit = static_cast<QLineEdit*>(editor); edit->setText(value); qDebug() << "setEditorData"; } void LayerItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & index ) const { editor->setGeometry(option.rect); } void LayerItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { qDebug() << "setModelData"; QLineEdit *edit = static_cast<QLineEdit*>(editor); model->setData(index, edit->text(), Qt::EditRole); }
最后是view
layertableview.h
#ifndef LAYERLISTVIEW_H #define LAYERLISTVIEW_H #include <QTableView> #include <QMouseEvent> #include <QDebug> #include <QHeaderView> #include <QStandardItemModel> #include <QContextMenuEvent> #include <QMenu> #include "layertablemodel.h" #include "layeritemdelegate.h" class LayerTableView : public QTableView { Q_OBJECT public: LayerTableView(QWidget *parent = 0); ~LayerTableView(); void setLayerSize(QSize s); public slots: void addNewLayer(); void deleteLayer(); protected: void mouseMoveEvent(QMouseEvent * event); void contextMenuEvent(QContextMenuEvent * event); private: LayerItemDelegate *delegate; LayerTableModel *model; QSize layerSize; private slots: void itemClicked(const QModelIndex&); }; #endif // LAYERLISTVIEW_H
layertableview.cpp
#include "layertableview.h" LayerTableView::LayerTableView(QWidget *parent) : QTableView(parent) { delegate = new LayerItemDelegate(); model = new LayerTableModel(); this->setContentsMargins(0, 0, 0, 0); this->setModel(model); this->setItemDelegate(delegate); this->horizontalHeader()->setStretchLastSection(true); this->horizontalHeader()->setHighlightSections(false); this->setFrameShape(QFrame::NoFrame); this->setColumnWidth(0, 30); this->setColumnWidth(1, 170); this->verticalHeader()->setVisible(false); this->horizontalHeader()->setVisible(false); this->resizeColumnsToContents(); this->resizeRowsToContents(); /*this->setEditTriggers(QAbstractItemView::NoEditTriggers); this->setSelectionBehavior(QAbstractItemView::SelectRows);*/ this->setMouseTracking(true);//important //When click on the checkbox it will emit signal twice.Click on the cell emit only once. connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(itemClicked(const QModelIndex&))); } LayerTableView::~LayerTableView() { } void LayerTableView::mouseMoveEvent(QMouseEvent * event) { } void LayerTableView::contextMenuEvent(QContextMenuEvent * event) { QMenu *pMenu = new QMenu(this); QAction *pAddGroupAct = new QAction(tr("Delete"), pMenu); pMenu->addAction(pAddGroupAct); pMenu->popup(mapToGlobal(event->pos())); } void LayerTableView::addNewLayer() { model->addItem(QString(), QImage(layerSize, QImage::Format_RGB32), true); //model->addItem(QString(), QImage("images\\sample.jpg"), true); model->refreshModel(); this->resizeRowsToContents(); } void LayerTableView::itemClicked(const QModelIndex& index) { if (index.isValid() ) { //When click in column 0. if (index.column() == 0) { model->changeLayerVisibility(index); QModelIndex tmp = model->selecttedIndex(model->getSelecttedRow()); this->selectionModel()->select(tmp, QItemSelectionModel::Select); } //When click in column 1. else if (index.column() == 1) { model->setSelecttedRow(index.row()); } } } void LayerTableView::deleteLayer() { model->deleteItem(model->getSelecttedRow()); model->refreshModel(); QModelIndex tmp = model->selecttedIndex(0); this->selectionModel()->select(tmp, QItemSelectionModel::Select); } void LayerTableView::setLayerSize(QSize s) { layerSize = s; }
参考
Qt Delgate的使用 简单说明 - http://qimo601.iteye.com/blog/1536444
Qt Model/View 学习笔记 - http://www.cppblog.com/yuanyajie/archive/2007/06/19/26641.html
Model/View Programming - https://qt-project.org/doc/qt-4.7/model-view-programming.html#an-editable-model
Qt实现QQ好友下拉列表 - http://blog.csdn.net/hai200501019/article/details/10283553
QTableView使用自定义委托 - http://www.haogongju.net/art/2811045