QComboBox自定义绘制下拉列表


前言

最近一直在对QComboBox的模型和视图委托进行了一些深入的研究,因此在这里做一个记录,总结一下。前两篇是对QCombobox的下拉表格的说明,以及对自定义委托和排序的讲解。本篇是对QComboBox的委托样式进行一些说明,然后做一个类似于QQ登录时的下拉框,会显示历史登录的账号头像等信息。


提示:以下是本篇文章正文内容,下面案例可供参考

一、子类化QAbstractListModel

由于是列表模型,因此选择QAbstractListModel进行子类化,重新实现。首先是data()和rowcount(),columnCount()不需要,因为是list模型内部有默认实现,原型如下:


virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0

virtual int rowCount(const QModelIndex &parent = QModelIndex()) const = 0

我们不需要提供编辑功能,因此setData()不需要重新实现,只要重新实现上面两个函数即可。下面是我的代码:

头文件:

#ifndef Q_LISTMODEL_H
#define Q_LISTMODEL_H

#include <QAbstractListModel>
#include <QObject>
#include "__global.h"
class QListmodel : public QAbstractListModel
{
    Q_OBJECT
public:
    explicit QListmodel(QObject *parent = 0);
    ~QListmodel();

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    void setModelData(const QList<UserInfo>& );
private:
    QList<UserInfo> InfoList;
};
#endif // Q_LISTMODEL_H

cpp文件:

#include "q_listmodel.h"


QListmodel::QListmodel(QObject *parent)
{

}

QListmodel::~QListmodel()
{

}

QVariant QListmodel::data(const QModelIndex &index, int role) const
{
    if(Qt::DisplayRole == role){
        return InfoList.at(index.row()).name;

    }else if(Qt::DecorationRole == role){
        return  InfoList.at(index.row()).icon;

    }else if(Qt::UserRole == role){
        return  InfoList.at(index.row()).number;

    }else if(Qt::UserRole+1 == role){
        return  InfoList.at(index.row()).isonline;
    }

    return QVariant();
}

int QListmodel::rowCount(const QModelIndex &parent) const
{
    return InfoList.size();
}

void QListmodel::setModelData(const QList<UserInfo> &data)
{
    if(!InfoList.isEmpty())
        InfoList.clear();

    InfoList = data;
}

其中setModelData函数是由外部调用,设置模型数据,UserInfo是自定义的一个结构。定义在__global.h文件中,如下:

__global.h文件:

#ifndef __GLOBAL_H
#define __GLOBAL_H

#include <QIcon>

class QString;

typedef struct infomation{
    QString name;
    QString number;
    QIcon icon;
    bool isonline;
}UserInfo;


#endif // __GLOBAL_H

注:其中name是用户名,number是号码,icon是头像,isonline为是否在线

二、子类化QStyledItemDelegate

QStyledItemDelegate是QAbstractItemDelegate的子类,由QAbstractItemDelegate派生出来有两个类,一个是QItemDelegate,另一个就是QStyledItemDelegate。

QStyledItemDelegate类为模型中的数据项提供显示和编辑功能。 而QItemDelegate可用于为基于QAbstractItemView子类的项目视图提供自定义显示功能和编辑器小部件。二者大同小异,二者的区别在于绘制和向视图提供编辑器的方式,默认的是QStyledItemDelegate,QStyledItemDelegate使用当前样式绘制,并且能够使用 Qt Style Sheet

代码如下(示例):
头文件:

#ifndef COMBOBOXDELEGATE_H
#define COMBOBOXDELEGATE_H

#include <QObject>
#include <QStyledItemDelegate>
#include <QModelIndex>
#include <QStandardItemModel>
#include <QPainter>
#include <QLineEdit>
#include <QComboBox>
#include <QDebug>

class comboboxdelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    explicit comboboxdelegate(QObject *parent = 0);
    ~comboboxdelegate();

    void paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const;
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
    qreal radius = 8;
};

#endif // COMBOBOXDELEGATE_H

CPP文件如下:

#include "comboboxdelegate.h"

comboboxdelegate::comboboxdelegate(QObject *parent)
{

}

comboboxdelegate::~comboboxdelegate()
{

}

void comboboxdelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.isValid())
    {
        painter->save();
        QString name = index.data(Qt::DisplayRole).toString();
        QVariant icondata = index.data(Qt::DecorationRole);
        QIcon icon = icondata.value<QIcon>();
        QString number = index.data(Qt::UserRole).toString();
        bool isonline  = index.data(Qt::UserRole+1).toBool();
        qDebug()<<name << number << isonline;

        QRectF rect;
        rect.setX(option.rect.x());
        rect.setY(option.rect.y());
        rect.setWidth( option.rect.width()-1);
        rect.setHeight(option.rect.height()-1);

        QPainterPath path;
        path.moveTo(rect.topRight() - QPointF(radius, 0));
        path.lineTo(rect.topLeft() + QPointF(radius, 0));
        path.quadTo(rect.topLeft(), rect.topLeft() + QPointF(0, radius));
        path.lineTo(rect.bottomLeft() + QPointF(0, -radius));
        path.quadTo(rect.bottomLeft(), rect.bottomLeft() + QPointF(radius, 0));
        path.lineTo(rect.bottomRight() - QPointF(radius, 0));
        path.quadTo(rect.bottomRight(), rect.bottomRight() + QPointF(0, -radius));
        path.lineTo(rect.topRight() + QPointF(0, radius));
        path.quadTo(rect.topRight(), rect.topRight() + QPointF(-radius, -0));

        if(option.state.testFlag(QStyle::State_Selected))
        {
            painter->setPen(QPen(Qt::blue));
            painter->setBrush(QColor(0,255,127));
            painter->drawPath(path);
        }
        else if(option.state.testFlag(QStyle::State_Raised))
        {
            painter->setPen(QPen(Qt::green));
            painter->setBrush(QColor(0,127,255));
            painter->drawPath(path);
        }
        else{
            painter->setPen(QPen(Qt::gray));
            painter->setBrush(QColor(255,255,255));
            painter->drawPath(path);
        }

        QRect iconRect = QRect(rect.left()+3,rect.top()+3,30,30);
        QRect nameRect = QRect(iconRect.right()+5, rect.top()+3, rect.width()-80, 20);
        QRect circle = QRect(nameRect.right()+5,nameRect.top(),10,10);
        QRect numberRect = QRect(nameRect.x(),nameRect.y()+20,100,20);
        qDebug()<<iconRect<<nameRect<<circle<<numberRect;

        painter->drawEllipse(circle);
        painter->setPen(QPen(QColor(0,0,0)));
        painter->setFont(QFont("微软雅黑", 9, QFont::Bold));
        painter->drawText(nameRect,Qt::AlignLeft,name);
        painter->drawText(numberRect,Qt::AlignLeft,number);

        painter->drawPixmap(iconRect,icon.pixmap(iconRect.size()));
        painter->restore();
    }else{
        qDebug()<<"index is vaild?";
    }
}

QSize comboboxdelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(option)
    Q_UNUSED(index)
    return QSize(200,40);
}

三、使用

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include <QStringListModel>
#include "comboboxdelegate.h"
#include "q_listmodel.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QListmodel *mListModel;
    comboboxdelegate* m_delegate;
private:
    void initmodel();
    void setModelData();
};
#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "__global.h"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    initmodel();
    ui->comboBox->setCurrentIndex(0);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::initmodel()
{
    mListModel = new QListmodel();
    m_delegate = new comboboxdelegate(this);
    ui->comboBox->setModel(mListModel);//设置模型
    ui->comboBox->setItemDelegate(m_delegate);//设置委托

    setModelData();
}

void MainWindow::setModelData()
{
    QList<UserInfo> InfoList;
    UserInfo info;
    info.icon = QIcon(":/img/1.ico");
    info.isonline = true;
    info.name = "张三";
    info.number = "0123456789";
    InfoList.append(info);

    UserInfo info1;
    info1.icon = QIcon(":/img/2.ico");
    info1.isonline = true;
    info1.name = "李四";
    info1.number = "44564664521";
    InfoList.append(info1);

    UserInfo info2;
    info2.icon = QIcon(":/img/3.ico");
    info2.isonline = true;
    info2.name = "王二";
    info2.number = "9876543210";
    InfoList.append(info2);

    mListModel->setModelData(InfoList);

}


我这边就简单的随便写几个数据做一下演示了,如果需要动态插入,移除模型的项,需要上述一QAbstractListModel子类化重写insertRows() 和 removeRows()函数。我这边就不做演示了,功能已实现。

下面看下效果图:
在这里插入图片描述
如果还有关于QComboBox的其他需求,也可以查看我的另外另外博客:
QComboBox自定义下拉表格
QComboBox自定义委托和IP动态排序


本文demo下载链接:dowmload

总结

以上就是今天要讲的内容,比较简单,文中如有错误或者不妥的地方,欢迎各位指出,一起学习进步。

如需转载,请注明出处。

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在PyQt5中,可以通过自定义QComboBox下拉列表为表格。具体步骤如下: 1. 创建一个QComboBox实例,并将其设置为自定义的表格下拉列表。 2. 创建一个QAbstractItemModel实例,并实现其方法以提供表格数据。 3. 创建一个QTableView实例,并将其设置为QComboBox下拉列表视图。 4. 将QTableView设置为下拉列表的大小,并将其附加到QComboBox下拉列表中。 下面是一个简单示例代码: ```python import sys from PyQt5.QtWidgets import QApplication, QComboBox, QTableView, QAbstractItemModel, QVBoxLayout, QWidget class TableModel(QAbstractItemModel): def __init__(self, data): super(TableModel, self).__init__() self.data = data def rowCount(self, parent): return len(self.data) def columnCount(self, parent): return len(self.data[0]) def data(self, index, role): if role == Qt.DisplayRole: return self.data[index.row()][index.column()] def index(self, row, column, parent): return self.createIndex(row, column) def parent(self, index): return QModelIndex() if __name__ == '__main__': app = QApplication(sys.argv) widget = QWidget() combo_box = QComboBox() data = [ [1, "Apple"], [2, "Banana"], [3, "Orange"], ] model = TableModel(data) table_view = QTableView() table_view.setModel(model) combo_box.setView(table_view) layout = QVBoxLayout(widget) layout.addWidget(combo_box) widget.setLayout(layout) widget.show() sys.exit(app.exec_()) ``` 在上述代码中,通过自定义TableModel类,我们可以提供表格的数据。然后,我们将这个TableModel实例应用到QTableView中,利用QTableView来实现下拉列表的表格视图。 最后,我们将QTableView设置为QComboBox下拉列表视图。在表格视图中,我们可以显示相应的表格数据。 这样,我们就可以在PyQt5中自定义QComboBox下拉列表为表格。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值