Qt自定义的ComboBox(下拉框)

Qt自定义的ComboBox(下拉框)

  • 前言
    • 方法一
    • 方法二
    • 实现效果
    • 主要代码

前言

学习QT有两年多了,第一次使用时在数据库课程设计上,发现用QT开发界面来操作数据库,第一次使用的环境是在QtCreator,Qt Designer使开发者能快速的对界面进行大概的布局,非常省时间。
  这次主题是对ComboBox进行自定义,让我想做这个是在使用QQ界面进行登录时发现那个下拉框好炫酷,鼠标所在的单元会缓慢变大,所以我尝试写了一下,因为开始学得知识不够,只能用简单的方法来实现,一年后回来看那个代码,实在是惨不忍睹,虽然说还能凑合用,所以我就用比较合理的方法重新写了一遍。

方法一

开始时是使用了View的一个函数:

void setIndexWidget(const QModelIndex &index, QWidget *widget);

方法二

后面改成重载QStyledItemDelegate,paint函数进行重绘

实现效果

Qt自定义的ComboBox(下拉框)_第1张图片

主要代码

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());
}

完整工程代码下载

你可能感兴趣的:(Qt,C++,Qt5.9,QComboBox)