写点什么

Qt|QListWidget 实现自定义 Item 效果

  • 2022-10-17
    河北
  • 本文字数:2878 字

    阅读完需:约 1 分钟

Qt|QListWidget实现自定义Item效果

首先,我们来看以下实现的最终效果吧!

我觉得这并不是一个很难得问题,最近新招了一个应届生,发现在实现上述效果时,被困扰住了,是不是刚刚接触 Qt 的这种稍微有难度的界面时,都会有些无头绪呢?


所以,我打算分享给大家实现的思路,以及会出现的问题,就我一个开发 5 年 C++的员工而言,针对新手会遇到哪些不懂的问题。


当前的开发环境:win10 VS2017 + Qt5.14.2 x64


在实现过程中新手会出现的难点,如下:

1:如何在 QListWidget 中添加带有按钮、文本等其它控件的一条数据?

2:选中每一条之后如何响应?QListWidget 自带的 item 响应为什么不生效?

3:如何选中删除按钮并通知 QListWidget 做出具体的响应?


就根据上述三个问题,边讲述问题边实现带有自定义控件的 Item 内容吧!

在使用 QListWidget 插入一条数据时,默认的方式,如下:

ui.listWidget->insertItem(0, "Text Content"); //方法1ui.listWidget->addItem("Text Content"); //方法2
复制代码

使用上述代码是无法实现的,因为参数中只能添加 QString 类型的字符串,那么该如何实现添加自定义项呢?


在 QListWidget 类中提供了叫做 QListWidgetItem 的子类,用于实现自定义的 item。

这时,我们就需要重新定义一个类,并且将该类与 ListWidgetItem 进行绑定,就可以实现每一行的 item 上展示属于我们自定义的格式了。


例子中展示的 item 中显示了一个选择框、文件名称以及删除按钮。


在这里,采用了 QCheckBox 以及 QPushButton 两个按钮实现的。


有人会询问:文件名称不应该使用 QLabel 控件表示吗?


回答是:当前可以使用 QLabel 控件显示文件名称,这里采用 QCheck 主要是想要展示文件的图标,根据不同的文件名后缀显示不同的图标。


自定义 Widget

该类继承自 QWidget。假设叫做:CustomItem


有些新手会直接创建一个纯的 C++类,这样做肯定是有问题的,当我们在外部使用当前自定义类时,你会发现,为什么新创建的类会单独分出来呢?


如果直接使用纯 C++类,还有另一个至关重要的问题,当前类需要进行消息交互时,你该如何传递给外部调用者呢?回调吗?是不是有点大材小用呢?

class CustomItem : public QWidget{	Q_OBJECT
public: CustomItem(QWidget *parent); ~CustomItem();private: QCheckBox* m_checkName; //文件名 QCheckBox* m_checkSelect; //选择 QPushButton* m_btnDelete; //删除
};
复制代码

自定义类 CustomItem 中创建了三个控件变量,分别表示了:选择框、文件名称以及删除按钮。

就是文章开始显示效果图的三个控件了。

接下来,需要定义一个外部调用接口,插入一条有效数据,假设接口名称是:AddINewtemData

class CustomItem : public QWidget{	Q_OBJECT
public: CustomItem(QWidget *parent); ~CustomItem();public: //对外开放接口 void AddINewtemData(int nRow, QString qsFileName); //添加一条新数据private: int m_nRow; QCheckBox* m_checkName; //附件名 QCheckBox* m_checkSelect; //选择 QPushButton* m_btnDelete; //删除
};
复制代码

AddINewtemData 参数

参数 1:代表的是当前自定义 widget 属于 QListWidget 的行编号,用于后续消息传递使用。

参数 2:需要展示的文件名称


根据用户传入的文件名称,根据文件后缀展示不同的图标。

void CustomItem::AddINewtemData(int nRow, QString qPath){	m_nRow = nRow; //记录当前自定义widget对应的QListWidget的行号        	//根据路径名,获取文件名称,并设置	QFileInfo info(qPath);	QString qsFileName = info.fileName();	m_checkName->setText(qsFileName);	//获取文件后缀	QString qsCheckStyle = "";	if (info.suffix() == "mp4") //视频文件	{            //自定义QCheckBox风格	}	else if (info.suffix() == "png") //图片文件	{            //自定义QCheckBox风格	}	else if (info.suffix() == "xlsx") //表格文件	{            //自定义QCheckBox风格	}	else if (info.suffix() == "pdf")	{            //自定义QCheckBox风格	}	else //文档文件	{            //自定义QCheckBox风格	}	m_checkName->setStyleSheet(qsCheckStyle);}
复制代码

构造完自定义 widget 类之后,接下来就需要将该类与 QListWidgetItem 进行绑定,显示到 QListWidget 上去。

外部调用方法,如下:

int nCount = ui.listWidget->count();
CustomItem* widget = new CustomItem(this);widget->AddINewtemData(nCount, qsFileName);widget->show();
QListWidgetItem* item = new QListWidgetItem;item->setSizeHint(QSize(48, 48));ui.listWidget->addItem(item);ui.listWidget->setItemWidget(item, widget);
复制代码

自定义 CustomItem 响应

上述功能可以实现 QListWidget 中展示自定义的 widget 之后,该如何点击 QListWidget 中的每一条做出不同的响应呢?


此时,我们对每一行的 QListWigetItem 绑定自定义类之后,是无法响应 QListWidget 自身的选择消息的!这一点需要大家记清楚了。


那么,该怎么触发呢?


针对于每一个 QWidget 类,只要是继承自 QWidget,都会有鼠标的四大响应:

virtual void mousePressEvent(QMouseEvent *event);virtual void mouseReleaseEvent(QMouseEvent *event);virtual void mouseDoubleClickEvent(QMouseEvent *event);virtual void mouseMoveEvent(QMouseEvent *event);
复制代码

当鼠标在自定义类做了点击效果后,肯定可以在鼠标按下事件中获取点击响应的。

所以,在自定义类 CustomItem 中需要重写 QWidget 的系统消息:mousePressEvent

void CustomItem::mousePressEvent(QMouseEvent *event){    QWidget::mousePressEvent(event);}
复制代码

断点设在该响应函数中时,肯定是会触发的,如果不可以肯定是当前 widget 处于禁用状态,或者是被遮盖住了。


这里还有一个隐藏问题:有些同学在点击自定义窗口时会发现这样一个奇怪的现象,为什么点击有些区域是响应 mousePressEvent 消息的,而点击有些区域是不响应呢?


以下是重中之重了!!


这就是前一段话提到的内容了,当前 widget 不触发时,肯定是禁用或者是被遮挡住了。

在我们这个自定义 Widget 中有三个活跃的控件,两个 QCheck,一个 QPushButton,当我们的鼠标在任意控件上点击时,此时的点击响应应该是响应到子控件上,而不是自定义的 Widget(CustomItem)上。

为了让鼠标点击任何控件时,所有的响应都响应到父类 Widget,也就是 CustomItem 上时,我们应该对支持鼠标响应操作的控件做特殊处理

setAttribute(Qt::WA_TransparentForMouseEvents)

每个控件都要设置以上的操作,当前控件只是用来显示,不做任何消息处理,是当前窗口做的消息处理

自定义 Widget 控件响应并通知外界处理

下面,来说一说第三个重点问题,如何通知外界处理。


我们使用了自定义的 Widget 之后就不能再使用 QListWidget 的内部选中消息了,为了让外部窗口获取内部 Widget 的消息时,此时我们需要采用发信号的方式,通知外界,模拟 QListWidget 消息。

signals:    void Msg_SendDeleteItemData
复制代码

外部窗口直接操作该消息,使用方法跟普通的方法一致,这里就不再过多介绍 Qt 中消息机制了。


到这里,实现 QListWidget 内嵌自定义窗口的核心功能就说清楚了。


我觉得我说的很明白了,如果有疑问的地方可以留言讨论哟~


我是中国好公民 st,一名 C++开发程序媛~

发布于: 刚刚阅读数: 4
用户头像

书山有路勤为径,学海无涯苦作舟 2022-07-01 加入

擅长语言:C++ 涉及语言:Python

评论

发布
暂无评论
Qt|QListWidget实现自定义Item效果_c++_中国好公民st_InfoQ写作社区