学习QT有两年多了,第一次使用时在数据库课程设计上,发现用QT开发界面来操作数据库,第一次使用的环境是在QtCreator,Qt Designer使开发者能快速的对界面进行大概的布局,非常省时间。
这次主题是对ComboBox进行自定义,让我想做这个是在使用QQ界面进行登录时发现那个下拉框好炫酷,鼠标所在的单元会缓慢变大,所以我尝试写了一下,因为开始学得知识不够,只能用简单的方法来实现,一年后回来看那个代码,实在是惨不忍睹,虽然说还能凑合用,所以我就用比较合理的方法重新写了一遍。
开始时是使用了View的一个函数:
void setIndexWidget(const QModelIndex &index, QWidget *widget);
后面改成重载QStyledItemDelegate,paint函数进行重绘
XIListDelegate类,继承自QStyledItemDelegate,对paint函数和editorEvent函数进行了重写。
//xiilistdelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include
#define ORIGINAL_ICON_WD 60 //ICON原始大小
#define ORIGINAL_ICON_HT 60
#define SMALL_ICON_WD 40 //ICON缩小后
#define SMALL_ICON_HT 40
#define BTN_WD 20 //按钮大小
#define BTN_HT 20
#define SPACE_WD 5 //空间调控,调试用
#define USER_DATA_ROLE Qt::UserRole +1 // 账号
#define USER_ICON_ROLE USER_DATA_ROLE + 1 //图标
#define USER_ITEM_HT_ROLE USER_ICON_ROLE + 1 //单元的高
#define USER_BTN_STATE_ROLE USER_ITEM_HT_ROLE + 1 //按钮状态
class QPushButton;
class QTimer;
class XIListDelegate :public QStyledItemDelegate
{
Q_OBJECT
public:
explicit XIListDelegate(QObject *parent = 0);
~XIListDelegate();
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)const;
enum ButtonState:int{
Normal = 0,
Pressed = 1,
Hover = 2
};
Q_ENUM(ButtonState)
protected:
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
Q_SIGNALS:
void currentEditBtnRectUpdata(const QRect &rect);
void clickedEditBtn(int index);
private:
QPushButton *btnWdiget;
int mBtnCurrentState;
QModelIndex mCurrentModelIndex;
QRect mouseOnCurrentRect;
};
#endif // MYITEMDELEGATE_H
//xiilistdelegate.cpp
#include "xiitemdelegate.h"
#include "xilistmodel.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
XIListDelegate::XIListDelegate(QObject *parent)
:QStyledItemDelegate(parent),
mBtnCurrentState(0)
{
btnWdiget = new QPushButton;
//设置按钮样式
btnWdiget->setStyleSheet("QPushButton {"
"background-color: transparent;"
"image:url(:/picture/X_leave.png);"
"}"
"QPushButton:hover {"
"image:url(:/picture/X_hover.png);"
"}"
"QPushButton:pressed {"
"image:url(:/picture/X_press.png);"
"}");
}
XIListDelegate::~XIListDelegate()
{
delete btnWdiget;
}
void XIListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)const
{
#define GET_CUSTOM_ROLE_VARIABLE(role,type) index.data(role).value()
#define GET_ITEM_SIZE GET_CUSTOM_ROLE_VARIABLE(Qt::SizeHintRole,QSize)
#define GET_BTN_STATE GET_CUSTOM_ROLE_VARIABLE(USER_BTN_STATE_ROLE,ButtonState)
QStyleOptionViewItem opt(option);
initStyleOption(&opt, index);
QStyledItemDelegate::paint(painter, opt, index);
//计算按钮位置
int btnX = opt.rect.x() + opt.rect.width() - BTN_WD - SPACE_WD;
int btnY = opt.rect.y() + (GET_ITEM_SIZE.height() - BTN_HT)/2;
int pixmapLenght = GET_ITEM_SIZE.height()-5;
//设置图标矩形,根据需要可更改
QRect iconRect(opt.rect.x()+SPACE_WD, opt.rect.y() + (GET_ITEM_SIZE.height() - pixmapLenght)/2, pixmapLenght,pixmapLenght);
//设置文字矩形
QRect textRect(opt.rect.x()+pixmapLenght+SPACE_WD*2, opt.rect.y(), opt.rect.width()-BTN_WD-pixmapLenght, pixmapLenght);
QStyleOptionButton butOpt;
//设置按钮矩形
butOpt.rect = QRect(btnX, btnY, BTN_WD, BTN_HT);
//使能按钮
butOpt.state |= QStyle::State_Enabled;
//根据model数据更新按钮状态
if(GET_BTN_STATE == Pressed)
{
butOpt.state |= QStyle::State_Sunken;
}
else if(GET_BTN_STATE == Hover)
{
butOpt.state |= QStyle::State_MouseOver;
}
//通用样式类
QCommonStyle style;
//绘画按钮
btnWdiget->style()->drawControl(QStyle::CE_PushButton, &butOpt, painter, btnWdiget);
//画图标
style.drawItemPixmap(painter,iconRect,Qt::AlignLeft|Qt::AlignVCenter,index.data(USER_ICON_ROLE).value<QPixmap>()
.scaled(pixmapLenght-5,pixmapLenght-5,Qt::KeepAspectRatio,Qt::SmoothTransformation));
//调色板,用于设置文字背景颜色
QPalette palette;
//文字黑色
palette.setBrush(QPalette::Active, QPalette::Text, Qt::black);
//背景白色
palette.setBrush(QPalette::Active, QPalette::Background, Qt::white);
//画文字
style.drawItemText(painter,textRect,Qt::AlignLeft|Qt::AlignVCenter,palette,true,index.data(USER_DATA_ROLE).toString());
}
/*
* 编辑事件函数,根据编辑操作更新按钮状态
*/
bool XIListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
QStyleOptionViewItem opt(option);
initStyleOption(&opt, index);
int btnX = opt.rect.x() + opt.rect.width() - BTN_WD - SPACE_WD;
int btnY = opt.rect.y() + (opt.rect.height() - BTN_HT)/2;
QStyleOptionButton butOpt;
butOpt.rect = QRect(btnX, btnY, BTN_WD, BTN_HT);
//该信号发送按钮位置,用于按钮按下相关部分的判断
emit currentEditBtnRectUpdata(butOpt.rect);
//根据鼠标事件更改按钮状态
if(event->type() == QEvent::MouseMove)
{
if(butOpt.rect.contains(static_cast<QMouseEvent*>(event)->pos()))
{
model->setData(index, Hover, USER_BTN_STATE_ROLE);
return true;
}
else
{
model->setData(index, Normal, USER_BTN_STATE_ROLE);
return true;
}
}
if(butOpt.rect.contains(static_cast<QMouseEvent*>(event)->pos()))
{
if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick)
{
model->setData(index, Pressed, USER_BTN_STATE_ROLE);
return true;
}
if(event->type() == QEvent::MouseButtonRelease)
{
model->setData(index, Normal, USER_BTN_STATE_ROLE);
emit clickedEditBtn(index.row());
return true;
}
}
}
XIListModel类,对data函数进行重写,其他地方调用data函数时将得到我们所期望的数值。
//xiilistmodel.h
#ifndef MYSTRINGLISTMODEL_H
#define MYSTRINGLISTMODEL_H
#include
class QTimer;
class XIListModel :public QStandardItemModel
{
Q_OBJECT
public:
explicit XIListModel(QObject *parent = 0);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
public Q_SLOTS:
void setCurrentHighlight(int cIndex);
void updateItemSize();
Q_SIGNALS:
void sizeHintChanged(const QModelIndex &index);
private:
QModelIndex mCurrentHighlightIndex;
QModelIndex mPreviousHighlightIndex;
QTimer *mTimer;
};
#endif // MYSTRINGLISTMODEL_H
//xiilistmodel.cpp
#include "xilistmodel.h"
#include "xiilistdelegate.h"
#include
#include
#include
XIListModel::XIListModel(QObject *parent)
:QStandardItemModel(parent),
mCurrentHighlightIndex(index(0,0)),
mPreviousHighlightIndex(index(0,0)),
mTimer(new QTimer(this))
{
mTimer->setInterval(8);//8ms
connect(mTimer,&QTimer::timeout,this,&XIListModel::updateItemSize);
}
QVariant XIListModel::data(const QModelIndex &index, int role) const
{
//qDebug() << role;
//控制单元格大小
if (role == Qt::SizeHintRole)
{
return QSize(0,index.data(USER_ITEM_HT_ROLE).toInt());
}
//编辑角色,当点击某个单元,返回该数据给编辑框(QLineEdit)
if(role == Qt::EditRole)
{
return index.data(USER_DATA_ROLE);
}
else
return QStandardItemModel::data(index,role);
}
void XIListModel::setCurrentHighlight(int cIndex)
{
mPreviousHighlightIndex = mCurrentHighlightIndex;//调试用
mCurrentHighlightIndex = index(cIndex,0);
if(!mTimer->isActive())
{
mTimer->start();
}
}
/*
* 该函数实现单元格变大变小的动画效果
*/
void XIListModel::updateItemSize()
{
int notWorkCount = 0;
for(int i = 0 ; i<rowCount();i++)
{
int itemHeight = index(i,0).data(USER_ITEM_HT_ROLE).toInt();
if(index(i,0) == mCurrentHighlightIndex)
{
if(mCurrentHighlightIndex.data(USER_ITEM_HT_ROLE) <= ORIGINAL_ICON_HT)
{
setData(mCurrentHighlightIndex,itemHeight+1,USER_ITEM_HT_ROLE);
emit sizeHintChanged(mCurrentHighlightIndex);//大小发生变化,发送该信号
}
else
{
notWorkCount++;
}
}
else
{
if(index(i,0).data(USER_ITEM_HT_ROLE) >= SMALL_ICON_HT)
{
setData(index(i,0),itemHeight-1,USER_ITEM_HT_ROLE);
emit sizeHintChanged(index(i,0));
}
else
{
notWorkCount++;
}
}
}
if(notWorkCount == 0 && mTimer->isActive())
{
mTimer->stop();
}
}
XIListView类,继承自QListView,Qt MVD中用于显示部分,主要增加了一些信号。
//xilistview.h
#ifndef MYLISTVIEW_H
#define MYLISTVIEW_H
#include
class XIListModel;
class XIListDelegate;
class XIListView :public QListView
{
Q_OBJECT
public:
explicit XIListView(QWidget *parent = Q_NULLPTR);
void setItemDelegate(XIListDelegate *delegate);
bool eventFilter(QObject *object, QEvent *e);
QRect currentbtnRect()const;
void setModel(XIListModel *model);
XIListModel *model() const;
XIListDelegate *itemDelegate() const;
public Q_SLOTS:
void mouseReleaseEvent(QMouseEvent *e);
Q_SIGNALS:
void clickData(const QString&);
void mouseReleased(int index);
void clickedEditBtn(int index);
private:
QRect mCurrentbtnRect;
};
#endif // MYLISTVIEW_H
//xilistview.cpp
#include "xilistview.h"
#include "xiilistdelegate.h"
#include "xilistmodel.h"
#include
#include
#include
#include "xilistmodel.h"
XIListView::XIListView(QWidget *parent)
:QListView(parent)
{
QListView::setModel(new XIListModel(this));
QListView::setItemDelegate(new XIListDelegate(this));
}
void XIListView::setItemDelegate(XIListDelegate *delegate)
{
XIListDelegate *oldDelegate = itemDelegate();
if(oldDelegate)
{
disconnect(oldDelegate,SIGNAL(clickedEditBtn(int)),this,SIGNAL(clickedEditBtn(int)));
disconnect(model(),SIGNAL(sizeHintChanged(QModelIndex)),oldDelegate,SIGNAL(sizeHintChanged(QModelIndex)));
}
connect(delegate,&XIListDelegate::currentEditBtnRectUpdata,[this](const QRect &rect){mCurrentbtnRect = rect;});
connect(delegate,&XIListDelegate::clickedEditBtn,this,&XIListView::clickedEditBtn);
connect(model(),&XIListModel::sizeHintChanged,delegate,&XIListDelegate::sizeHintChanged);
return QListView::setItemDelegate(delegate);//QListView::setItemDelegate will call delete oldDelegate
}
void XIListView::mouseReleaseEvent(QMouseEvent *e)
{
(void*)e;
QListView::mouseReleaseEvent(e);
}
bool XIListView::eventFilter(QObject *object, QEvent *e)
{
return QListView::eventFilter(object, e);
}
QRect XIListView::currentbtnRect() const
{
return mCurrentbtnRect;
}
void XIListView::setModel(XIListModel *model)
{
connect(model,&XIListModel::sizeHintChanged,itemDelegate(),&XIListDelegate::sizeHintChanged);
return QListView::setModel(model);
}
XIListModel *XIListView::model() const
{
return static_cast<XIListModel*>(QListView::model());
}
XIListDelegate *XIListView::itemDelegate() const
{
return static_cast<XIListDelegate*>(QListView::itemDelegate());
}
完整工程代码下载