Qt自定义的ComboBox(下拉框)

Qt自定义的ComboBox(下拉框)

前言

学习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 <QStyledItemDelegate>

#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 <QPushButton>
#include <QCommonStyle>
#include <QEvent>
#include <QMouseEvent>
#include <QDebug>
#include <QApplication>
#include <QTimer>
#include <QStandardItemModel>
#include <QAbstractItemModel>

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<type>()
#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 <QStandardItemModel>

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 <QSize>
#include <QDebug>
#include <QTimer>

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 <QListView>
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 <QDebug>
#include <QEvent>
#include <QMouseEvent>
#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());
}

完整工程代码下载

这段代码是一些 HTML 元数据标签,用于设置网页的视口(viewport)和浏览器的行为。让我为您解析每一句代码的含义: 1. `<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />`: - `name="viewport"`:指定该元数据的名称为 "viewport",用于设置网页的视口。 - `content="width=device-width,initial-scale=1.0,maximum-scale=1.0, user-scalable=no, viewport-fit=cover"`:指定视口的具体设置。 - `width=device-width`:将视口宽度设置为设备的宽度,使网页在不同设备上显示的宽度一致。 - `initial-scale=1.0`:初始缩放比例为 1.0,即默认不进行缩放。 - `maximum-scale=1.0`:最大缩放比例为 1.0,限制用户不能放大页面。 - `user-scalable=no`:禁用用户缩放页面的能力。 - `viewport-fit=cover`:将视口填充满整个设备屏幕,确保内容不会因设备的原生UI(如系统状态栏)而被遮挡。 2. `<meta name="format-detection" content="telephone=no" />`: - `name="format-detection"`:指定该元数据的名称为 "format-detection",用于控制浏览器对特定格式内容的自动识别。 - `content="telephone=no"`:禁止浏览器自动识别并呈现电话号码,避免电话号码被自动转换成拨号链接。 3. `<meta http-equiv="X-UA-Compatible" content="ie=edge" />`: - `http-equiv="X-UA-Compatible"`:指定该元数据的名称为 "X-UA-Compatible",用于指示浏览器使用何种版本的渲染引擎进行页面渲染。 - `content="ie=edge"`:指定使用最新版本的 IE 渲染引擎(Edge)来渲染页面,确保在较新的 IE 浏览器中获得最佳兼容性。 4. `<meta name="renderer" content="webkit" />`: - `name="renderer"`:指定该元数据的名称为 "renderer",用于指定浏览器的渲染方式。 - `content="webkit"`:将浏览器的渲染方式设置为 WebKit 内核,以确保在 WebKit 内核的浏览器中获得最佳兼容性。 这些元数据标签提供了关于网页视口、浏览器行为、兼容性等方面的设置,以确保网页在不同设备和浏览器上的显示效果和行为的一致性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值