Skip to content

Commit ac02dfd

Browse files
committed
[重构图标按钮组件并完善项目文档]: 将IconButton组件重构为功能更强大的ButtonIconStateManager,优化状态管理逻辑,同时更新项目文档和测试用例
- **组件重构升级**: 将原有IconButton组件重构为ButtonIconStateManager,支持正常、悬停、按下、选中四种状态,提供更完善的图标状态管理功能 - **代码架构优化**: 采用Pimpl设计模式重构实现,增强事件过滤器和状态切换逻辑,支持QPushButton、QToolButton等多种按钮类型 - **项目结构调整**: 更新CMakeLists.txt和.pro项目文件,用新的ButtonIconStateManager替换原有IconButton组件,保持项目构建一致性 - **文档内容更新**: 在README.md中添加新组件介绍,优化HttpClient功能描述,调整GridViewModel标题级别以改善文档结构 - **测试用例完善**: 修复HttpClient单元测试中的未使用参数警告,增强测试代码的健壮性和可维护性 - **示例程序增强**: 提供完整的演示程序,展示多种按钮类型的状态管理,包含禁用状态控制、重置功能和实时状态显示
1 parent 021f27d commit ac02dfd

16 files changed

Lines changed: 488 additions & 191 deletions

README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@
3434
- 可自定义边框、背景颜色和圆角
3535
- <img src="src/Bubble/images/bubble.png" width="600" alt="气泡对话框截图">
3636

37+
### [ButtonIconStateManager](src/ButtonIconStateManager/) - 按钮图标状态管理器
38+
39+
- 智能管理按钮在不同状态下的图标切换
40+
- 支持正常、悬停、按下、选中四种状态
41+
- 基于事件过滤器的自动状态检测
42+
- 可应用于 QPushButton、QToolButton 等多种按钮类型
43+
3744
### [Chart](src/Chart/) - 数据可视化图表(QtCharts 模块 Desprecated)
3845

3946
- 多种图表类型:面积图、折线图、饼图、柱状图
@@ -65,23 +72,20 @@
6572

6673
### [FlowLayout](https://doc.qt.io/qt-6/qtwidgets-layouts-flowlayout-example.html) - Qt 官方Flow Layout Example
6774

68-
# [GridViewModel](src/GridViewModel/) - 自适应网格布局
75+
### [GridViewModel](src/GridViewModel/) - 自适应网格布局
6976

7077
- 基于 Qt Model-View 架构的网格布局组件
7178
- 支持自适应列数和自定义单元格
7279
- 内置多选功能和流畅的交互体验
7380
- <img src="src/GridViewModel/images/grid_view_model.png" width="800" alt="网格布局视图">
7481

75-
### [HttpClient](src/HttpClient/) - HTTP 客户端实现
76-
77-
- 支持 JSON 请求的 HTTP 客户端
78-
- 文件上传/下载功能
79-
- 支持 DELETE 请求
80-
81-
### [IconButton](src/IconButton/) - 状态感知图标按钮
82+
### [HttpClient](src/HttpClient/) - HTTP 客户端
8283

83-
- 具有状态相关图标的按钮
84-
- 事件过滤器实现
84+
- 支持GET/POST/PUT/DELETE方法
85+
- JSON请求和响应自动处理
86+
- 文件上传下载带进度回调
87+
- 超时控制和SSL证书配置
88+
- 同步/异步请求支持
8589

8690
### [ImageCarousel](src/ImageCarousel/) - 图片轮播组件
8791

src/IconButton/IconButton.pro renamed to src/ButtonIconStateManager/ButtonIconStateManager.pro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ QT += core gui
55
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
66

77
SOURCES += \
8-
iconfliter.cc \
8+
buttoniconstatemanager.cc \
99
main.cc \
1010
mainwindow.cc
1111

1212
HEADERS += \
13-
iconfliter.hpp \
13+
buttoniconstatemanager.hpp \
1414
mainwindow.hpp
1515

1616
DESTDIR = $$RUNTIME_OUTPUT_DIRECTORY
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
set(PROJECT_SOURCES buttoniconstatemanager.cc buttoniconstatemanager.hpp
2+
main.cc mainwindow.cc mainwindow.hpp)
3+
4+
qt_add_executable(ButtonIconStateManager ${PROJECT_SOURCES})
5+
target_link_libraries(ButtonIconStateManager PRIVATE Qt::Widgets)
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#include "buttoniconstatemanager.hpp"
2+
3+
#include <QEnterEvent>
4+
#include <QEvent>
5+
#include <QMouseEvent>
6+
#include <QPointer>
7+
8+
namespace {
9+
10+
QIcon createIconFromPaths(const QStringList &paths)
11+
{
12+
QIcon icon;
13+
for (const QString &path : paths) {
14+
if (!path.isEmpty()) {
15+
icon.addFile(path);
16+
}
17+
}
18+
return icon;
19+
}
20+
21+
} // namespace
22+
23+
class ButtonIconStateManager::ButtonIconStateManagerPrivate
24+
{
25+
public:
26+
explicit ButtonIconStateManagerPrivate(ButtonIconStateManager *q)
27+
: q_ptr(q)
28+
{}
29+
30+
ButtonIconStateManager *q_ptr;
31+
32+
QPointer<QAbstractButton> buttonPtr;
33+
34+
QIcon normalIcon;
35+
QIcon hoverIcon;
36+
QIcon pressedIcon;
37+
QIcon checkedIcon;
38+
39+
bool isMousePressed = false;
40+
bool isMouseHover = false;
41+
};
42+
43+
ButtonIconStateManager::ButtonIconStateManager(QAbstractButton *parent)
44+
: QObject(parent)
45+
, d_ptr(new ButtonIconStateManagerPrivate(this))
46+
{
47+
d_ptr->buttonPtr = parent;
48+
if (d_ptr->buttonPtr) {
49+
d_ptr->buttonPtr->installEventFilter(this);
50+
setupConnections();
51+
52+
// 设置初始图标
53+
if (!d_ptr->normalIcon.isNull()) {
54+
d_ptr->buttonPtr->setIcon(d_ptr->normalIcon);
55+
}
56+
}
57+
}
58+
59+
ButtonIconStateManager::~ButtonIconStateManager()
60+
{
61+
if (d_ptr->buttonPtr) {
62+
d_ptr->buttonPtr->removeEventFilter(this);
63+
}
64+
}
65+
66+
void ButtonIconStateManager::setNormalIcon(const QIcon &icon)
67+
{
68+
if (!icon.isNull()) {
69+
d_ptr->normalIcon = icon;
70+
if (d_ptr->buttonPtr && !d_ptr->isMouseHover && !d_ptr->isMousePressed) {
71+
d_ptr->buttonPtr->setIcon(d_ptr->normalIcon);
72+
}
73+
}
74+
}
75+
76+
void ButtonIconStateManager::setNormalIcon(const QStringList &iconPaths)
77+
{
78+
setNormalIcon(createIconFromPaths(iconPaths));
79+
}
80+
81+
void ButtonIconStateManager::setHoverIcon(const QIcon &icon)
82+
{
83+
if (!icon.isNull()) {
84+
d_ptr->hoverIcon = icon;
85+
}
86+
}
87+
88+
void ButtonIconStateManager::setHoverIcon(const QStringList &iconPaths)
89+
{
90+
setHoverIcon(createIconFromPaths(iconPaths));
91+
}
92+
93+
void ButtonIconStateManager::setPressedIcon(const QIcon &icon)
94+
{
95+
if (!icon.isNull()) {
96+
d_ptr->pressedIcon = icon;
97+
}
98+
}
99+
100+
void ButtonIconStateManager::setPressedIcon(const QStringList &iconPaths)
101+
{
102+
setPressedIcon(createIconFromPaths(iconPaths));
103+
}
104+
105+
void ButtonIconStateManager::setCheckedIcon(const QIcon &icon)
106+
{
107+
if (!icon.isNull()) {
108+
d_ptr->checkedIcon = icon;
109+
updateIconBasedOnState();
110+
}
111+
}
112+
113+
void ButtonIconStateManager::setCheckedIcon(const QStringList &iconPaths)
114+
{
115+
setCheckedIcon(createIconFromPaths(iconPaths));
116+
}
117+
118+
void ButtonIconStateManager::resetToNormal()
119+
{
120+
if (d_ptr->buttonPtr) {
121+
d_ptr->buttonPtr->setIcon(d_ptr->normalIcon);
122+
}
123+
}
124+
125+
void ButtonIconStateManager::onButtonToggled(bool checked)
126+
{
127+
Q_UNUSED(checked)
128+
updateIconBasedOnState();
129+
}
130+
131+
bool ButtonIconStateManager::eventFilter(QObject *watched, QEvent *event)
132+
{
133+
if (!d_ptr->buttonPtr || watched != d_ptr->buttonPtr) {
134+
return QObject::eventFilter(watched, event);
135+
}
136+
137+
switch (event->type()) {
138+
case QEvent::Enter:
139+
d_ptr->isMouseHover = true;
140+
updateIconBasedOnState();
141+
break;
142+
case QEvent::Leave:
143+
d_ptr->isMouseHover = false;
144+
updateIconBasedOnState();
145+
break;
146+
case QEvent::MouseButtonPress:
147+
if (static_cast<QMouseEvent *>(event)->button() == Qt::LeftButton) {
148+
d_ptr->isMousePressed = true;
149+
updateIconBasedOnState();
150+
}
151+
break;
152+
case QEvent::MouseButtonRelease:
153+
if (static_cast<QMouseEvent *>(event)->button() == Qt::LeftButton) {
154+
d_ptr->isMousePressed = false;
155+
updateIconBasedOnState();
156+
}
157+
break;
158+
case QEvent::EnabledChange: updateIconBasedOnState(); break;
159+
default: break;
160+
}
161+
162+
return QObject::eventFilter(watched, event);
163+
}
164+
165+
void ButtonIconStateManager::setupConnections()
166+
{
167+
if (d_ptr->buttonPtr) {
168+
connect(d_ptr->buttonPtr,
169+
&QAbstractButton::toggled,
170+
this,
171+
&ButtonIconStateManager::onButtonToggled,
172+
Qt::UniqueConnection);
173+
}
174+
}
175+
176+
void ButtonIconStateManager::updateIconBasedOnState()
177+
{
178+
if (!d_ptr->buttonPtr || !d_ptr->buttonPtr->isEnabled()) {
179+
return;
180+
}
181+
182+
QIcon targetIcon;
183+
184+
if (d_ptr->isMousePressed && !d_ptr->pressedIcon.isNull()) {
185+
// 鼠标按下状态
186+
targetIcon = d_ptr->pressedIcon;
187+
} else if (d_ptr->isMouseHover && !d_ptr->hoverIcon.isNull()) {
188+
// 鼠标悬停状态
189+
targetIcon = d_ptr->hoverIcon;
190+
} else if (d_ptr->buttonPtr->isChecked() && !d_ptr->checkedIcon.isNull()) {
191+
// 选中状态
192+
targetIcon = d_ptr->checkedIcon;
193+
} else {
194+
// 正常状态
195+
targetIcon = d_ptr->normalIcon;
196+
}
197+
198+
if (!targetIcon.isNull()) {
199+
d_ptr->buttonPtr->setIcon(targetIcon);
200+
}
201+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#pragma once
2+
3+
#include <QAbstractButton>
4+
5+
class ButtonIconStateManager : public QObject
6+
{
7+
Q_OBJECT
8+
9+
public:
10+
explicit ButtonIconStateManager(QAbstractButton *parent = nullptr);
11+
~ButtonIconStateManager() override;
12+
13+
// 设置正常状态图标
14+
void setNormalIcon(const QIcon &icon);
15+
void setNormalIcon(const QStringList &iconPaths);
16+
17+
// 设置悬停状态图标
18+
void setHoverIcon(const QIcon &icon);
19+
void setHoverIcon(const QStringList &iconPaths);
20+
21+
// 设置按下状态图标
22+
void setPressedIcon(const QIcon &icon);
23+
void setPressedIcon(const QStringList &iconPaths);
24+
25+
// 设置选中状态图标(可选)
26+
void setCheckedIcon(const QIcon &icon);
27+
void setCheckedIcon(const QStringList &iconPaths);
28+
29+
// 重置为正常状态
30+
void resetToNormal();
31+
32+
private slots:
33+
void onButtonToggled(bool checked);
34+
35+
protected:
36+
bool eventFilter(QObject *watched, QEvent *event) override;
37+
38+
private:
39+
void setupConnections();
40+
void updateIconBasedOnState();
41+
42+
class ButtonIconStateManagerPrivate;
43+
QScopedPointer<ButtonIconStateManagerPrivate> d_ptr;
44+
};

0 commit comments

Comments
 (0)