Qt自定义列表项与QListWidget学习
1. 项目概述
本项目实现了一个模拟QQ群成员列表的界面,主要展示了如何使用Qt中的QListWidget控件结合自定义控件来创建复杂的列表项。项目的主要特点包括:
- 使用QListWidget作为列表容器
- 创建自定义的QQItem控件作为列表项
- 使用Qt样式表(QSS)美化界面
- 使用Qt资源系统管理图片资源
2. 项目结构
42/
├── 42.pro # 项目文件
├── main.cpp # 主函数入口
├── widget.h # 主窗口头文件
├── widget.cpp # 主窗口实现
├── widget.ui # 主窗口界面设计
├── qqitem.h # 自定义列表项头文件
├── qqitem.cpp # 自定义列表项实现
├── qqitem.ui # 自定义列表项界面设计
├── res.qrc # 资源文件
└── icons/ # 图标资源目录
├── icon0.jpg # 头像图片
├── icon1.jpg # 头像图片
├── icon2.jpg # 头像图片
├── icon3.jpg # 头像图片
├── phone.png # 电话图标
└── search.png # 搜索图标
3. 主窗口设计
3.1 界面设计 (widget.ui)
主窗口界面使用Qt Designer设计,主要包含以下元素:
- 一个标题标签,显示群成员数量信息
- 一个搜索框,包含搜索图标按钮和文本输入框
- 一个QListWidget控件,用于显示群成员列表
界面使用垂直布局(QVBoxLayout)组织这些元素,搜索框内部使用水平布局(QHBoxLayout)。
xml
<!-- widget.ui 主要结构 -->
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<!-- 标题标签属性设置 -->
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton">
<!-- 搜索按钮属性设置 -->
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit">
<!-- 搜索输入框属性设置 -->
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="listWidget">
<!-- 列表控件属性设置 -->
</widget>
</item>
</layout>
3.2 样式设置
主窗口中使用了Qt样式表(QSS)来美化界面元素:
css
/* 搜索按钮样式 */
QPushButton { border-image: url(:/icons/search.png) }
/* 搜索输入框样式 */
QLineEdit { background: transparent; border: none }
/* 列表项样式 */
QListWidget::item { height: 65px }
QListWidget::item:selected { background-color: rgb(200, 200, 200) }
QListWidget::item:hover { background-color: rgb(220, 220, 220) }
这些样式设置了:
- 搜索按钮使用图片作为背景
- 搜索输入框透明无边框
- 列表项高度固定为65px
- 列表项选中和悬停时的背景颜色
4. 自定义列表项设计
4.1 界面设计 (qqitem.ui)
自定义列表项使用Qt Designer设计,包含以下元素:
- 一个头像图片标签(QLabel)
- 一个电话图标标签(QLabel),可选显示
- 一个名称标签(QLabel)
这些元素使用水平布局(QHBoxLayout)组织,并添加了适当的间距。
xml
<!-- qqitem.ui 主要结构 -->
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<!-- 左侧间距 -->
</spacer>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<!-- 头像容器 -->
<widget class="QLabel" name="icon">
<!-- 头像标签属性设置 -->
</widget>
<widget class="QLabel" name="phone">
<!-- 电话图标标签属性设置 -->
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="name">
<!-- 名称标签属性设置 -->
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<!-- 右侧间距 -->
</spacer>
</item>
</layout>
4.2 样式设置
自定义列表项中也使用了Qt样式表(QSS)来美化界面元素:
css
/* 头像容器样式 */
QWidget { background: transparent; border-radius: 10px }
/* 头像标签样式 */
QLabel { background-color: darkgray; border-radius: 10px }
/* 名称标签样式 */
QLabel { color: darkgray; font-size: 20px }
这些样式设置了:
- 头像容器透明背景,圆角边框
- 头像标签深灰色背景,圆角边框
- 名称标签深灰色文字,20px字体大小
5. 代码实现
5.1 自定义列表项类 (QQItem)
5.1.1 类定义 (qqitem.h)
cpp
#ifndef QQITEM_H
#define QQITEM_H
#include <QWidget>
namespace Ui {
class QQItem;
}
class QQItem : public QWidget
{
Q_OBJECT
public:
explicit QQItem(QString icon, bool flag, QString name, QWidget *parent = nullptr);
~QQItem();
private:
Ui::QQItem *ui;
};
#endif // QQITEM_H
自定义列表项类继承自QWidget,构造函数接受三个参数:
icon
:头像图片路径flag
:是否显示电话图标name
:成员名称
5.1.2 类实现 (qqitem.cpp)
cpp
#include "qqitem.h"
#include "ui_qqitem.h"
QQItem::QQItem(QString icon, bool flag, QString name, QWidget *parent) :
QWidget(parent),
ui(new Ui::QQItem)
{
ui->setupUi(this);
QImage image(icon);
ui->icon->setPixmap(QPixmap::fromImage(image.scaled(ui->icon->width(), ui->icon->height())));
QImage image2(":/icons/phone.png");
ui->phone->setPixmap(QPixmap::fromImage(image2.scaled(ui->phone->width(), ui->phone->height())));
ui->phone->setVisible(flag);
ui->name->setText(name);
}
QQItem::~QQItem()
{
delete ui;
}
构造函数中的主要操作:
- 加载头像图片,并设置到icon标签
- 加载电话图标,并根据flag参数决定是否显示
- 设置成员名称到name标签
5.2 主窗口类 (Widget)
5.2.1 类定义 (widget.h)
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
5.2.2 类实现 (widget.cpp)
cpp
#include "widget.h"
#include "ui_widget.h"
#include "qqitem.h"
#include <QListWidgetItem>
class QQItem;
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setLayout(ui->verticalLayout);
QQItem *qqItem = new QQItem(":/icons/icon1.jpg", true, "风子兰特");
QQItem *qqItem1 = new QQItem(":/icons/icon0.jpg", false, "LIYOU");
QQItem *qqItem2 = new QQItem(":/icons/icon2.jpg", false, "简单一点");
QQItem *qqItem3 = new QQItem(":/icons/icon3.jpg", true, "Nov");
QListWidgetItem *item0 = new QListWidgetItem;
QListWidgetItem *item1 = new QListWidgetItem;
QListWidgetItem *item2 = new QListWidgetItem;
QListWidgetItem *item3 = new QListWidgetItem;
ui->listWidget->addItem(item0);
ui->listWidget->addItem(item1);
ui->listWidget->addItem(item2);
ui->listWidget->addItem(item3);
ui->listWidget->setItemWidget(item0, qqItem);
ui->listWidget->setItemWidget(item1, qqItem1);
ui->listWidget->setItemWidget(item2, qqItem2);
ui->listWidget->setItemWidget(item3, qqItem3);
}
Widget::~Widget()
{
delete ui;
}
主窗口构造函数中的主要操作:
- 创建4个自定义列表项(QQItem),设置不同的头像、电话图标显示状态和名称
- 创建4个列表项(QListWidgetItem)
- 将列表项添加到列表控件(QListWidget)
- 将自定义列表项设置为列表项的控件
5.3 主函数 (main.cpp)
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
主函数创建并显示主窗口,然后进入应用程序事件循环。
6. 资源管理
6.1 资源文件 (res.qrc)
xml
<RCC>
<qresource prefix="/">
<file>icons/icon0.jpg</file>
<file>icons/icon1.jpg</file>
<file>icons/icon2.jpg</file>
<file>icons/icon3.jpg</file>
<file>icons/phone.png</file>
<file>icons/search.png</file>
</qresource>
</RCC>
资源文件定义了项目中使用的图片资源,包括头像图片和图标。这些资源可以通过:/icons/xxx
的路径在代码中访问。
7. QListWidget使用技巧
7.1 基本用法
cpp
// 创建列表项
QListWidgetItem *item = new QListWidgetItem;
// 添加列表项到列表控件
listWidget->addItem(item);
// 设置列表项的文本
item->setText("列表项文本");
// 设置列表项的图标
item->setIcon(QIcon("图标路径"));
// 设置列表项的工具提示
item->setToolTip("工具提示文本");
// 设置列表项的数据
item->setData(Qt::UserRole, QVariant("自定义数据"));
7.2 自定义列表项
cpp
// 创建自定义控件
MyCustomWidget *widget = new MyCustomWidget;
// 创建列表项
QListWidgetItem *item = new QListWidgetItem;
// 添加列表项到列表控件
listWidget->addItem(item);
// 设置列表项的大小
item->setSizeHint(widget->sizeHint());
// 将自定义控件设置为列表项的控件
listWidget->setItemWidget(item, widget);
7.3 样式设置
css
/* 列表控件样式 */
QListWidget {
background-color: white;
border: 1px solid gray;
outline: none; /* 去除焦点边框 */
}
/* 列表项样式 */
QListWidget::item {
height: 50px; /* 固定高度 */
padding: 5px; /* 内边距 */
border-bottom: 1px solid lightgray; /* 底部边框 */
}
/* 选中列表项样式 */
QListWidget::item:selected {
background-color: #e0e0e0; /* 选中背景色 */
color: black; /* 选中文字颜色 */
}
/* 悬停列表项样式 */
QListWidget::item:hover {
background-color: #f0f0f0; /* 悬停背景色 */
}
7.4 信号与槽
cpp
// 连接列表项点击信号
connect(listWidget, &QListWidget::itemClicked, this, &MyClass::onItemClicked);
// 连接列表项双击信号
connect(listWidget, &QListWidget::itemDoubleClicked, this, &MyClass::onItemDoubleClicked);
// 连接当前项改变信号
connect(listWidget, &QListWidget::currentItemChanged, this, &MyClass::onCurrentItemChanged);
// 槽函数实现
void MyClass::onItemClicked(QListWidgetItem *item)
{
// 处理列表项点击事件
int row = listWidget->row(item); // 获取行号
QString text = item->text(); // 获取文本
QVariant data = item->data(Qt::UserRole); // 获取自定义数据
}
8. 实现自定义列表项的步骤
-
创建自定义控件类
- 继承QWidget或其他适合的控件类
- 设计控件的界面(使用Qt Designer或代码)
- 实现必要的功能和交互
-
在主窗口中使用自定义列表项
- 创建QListWidgetItem对象
- 创建自定义控件对象
- 将列表项添加到QListWidget
- 使用setItemWidget将自定义控件设置为列表项的控件
-
设置样式和交互
- 使用Qt样式表(QSS)美化界面
- 实现必要的信号与槽连接
- 处理用户交互事件
10. 扩展功能示例
10.1 添加搜索功能
cpp
// 连接搜索输入框的文本变化信号
connect(ui->lineEdit, &QLineEdit::textChanged, this, &Widget::onSearchTextChanged);
// 搜索功能实现
void Widget::onSearchTextChanged(const QString &text)
{
// 遍历所有列表项
for (int i = 0; i < ui->listWidget->count(); ++i)
{
QListWidgetItem *item = ui->listWidget->item(i);
QWidget *widget = ui->listWidget->itemWidget(item);
QQItem *qqItem = qobject_cast<QQItem*>(widget);
// 获取名称标签的文本(假设有getName方法)
QString name = qqItem->getName();
// 根据搜索文本显示或隐藏列表项
if (text.isEmpty() || name.contains(text, Qt::CaseInsensitive))
{
item->setHidden(false);
}
else
{
item->setHidden(true);
}
}
}
10.2 添加右键菜单
cpp
// 重写上下文菜单事件
void Widget::contextMenuEvent(QContextMenuEvent *event)
{
QListWidgetItem *item = ui->listWidget->itemAt(ui->listWidget->mapFromGlobal(event->globalPos()));
if (item)
{
QMenu menu(this);
QAction *viewAction = menu.addAction("查看资料");
QAction *chatAction = menu.addAction("发送消息");
QAction *deleteAction = menu.addAction("删除好友");
QAction *selectedAction = menu.exec(event->globalPos());
if (selectedAction == viewAction)
{
// 查看资料
}
else if (selectedAction == chatAction)
{
// 发送消息
}
else if (selectedAction == deleteAction)
{
// 删除好友
ui->listWidget->takeItem(ui->listWidget->row(item));
delete item;
}
}
}
10.3 拖放排序
cpp
// 在构造函数中启用拖放
ui->listWidget->setDragEnabled(true);
ui->listWidget->setDragDropMode(QAbstractItemView::InternalMove);
ui->listWidget->setDefaultDropAction(Qt::MoveAction);