IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Qt中显示复杂列表

    qp120291570发表于 2014-12-17 21:47:41
    love 0

    提要

    最近想要完成的一个项目需要显示一个列表,可以动态增减,可编辑,有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 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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 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::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()<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 
    #include 
    #include 
    #include 
    
    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(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(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(editor);
    	model->setData(index, edit->text(), Qt::EditRole);
    }
    
    


    最后是view

    layertableview.h

    #ifndef LAYERLISTVIEW_H
    #define LAYERLISTVIEW_H
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #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



沪ICP备19023445号-2号
友情链接