Skip to content

Commit 418b672

Browse files
Kakueeendeepin-bot[bot]
authored andcommitted
feat: add image classification feature
Added comprehensive image classification system with AI-based categorization 1. Implemented classification view showing categorized images in grid layout 2. Added classification detail view for browsing images within specific categories 3. Integrated with external DBus classification service for AI-powered image recognition 4. Added classification progress tracking and status notifications 5. Extended database schema to store classification metadata 6. Added sidebar navigation entry for classification feature 7. Implemented automatic classification of unclassified images on first access Log: Added image classification feature with AI-based categorization Influence: 1. Test classification view displays correctly when service is available 2. Verify classification detail view shows proper images for each category 3. Test classification progress dialog appears during initial classification 4. Verify sidebar navigation to classification views works correctly 5. Test classification status messages display properly 6. Verify database properly stores and retrieves classification metadata 7. Test handling when classification service is unavailable feat: 添加图片分类功能 新增基于AI的图片分类系统 1. 实现分类视图,以网格形式显示分类后的图片 2. 添加分类详情视图,用于浏览特定分类下的图片 3. 集成外部DBus分类服务,提供AI驱动的图片识别能力 4. 添加分类进度跟踪和状态通知 5. 扩展数据库结构以存储分类元数据 6. 在侧边栏添加分类功能导航入口 7. 实现首次访问时自动对未分类图片进行分类 Log: 新增基于AI的图片分类功能 Influence: 1. 测试分类服务可用时分类视图正确显示 2. 验证分类详情视图正确显示各分类下的图片 3. 测试初始分类时进度对话框正常显示 4. 验证侧边栏导航到分类视图功能正常 5. 测试分类状态消息正确显示 6. 验证数据库正确存储和检索分类元数据 7. 测试分类服务不可用时的处理逻辑
1 parent e22f215 commit 418b672

23 files changed

+1112
-65
lines changed

src/deepin-album.qrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,8 @@
7878
<file>qml/Control/MonthImage.qml</file>
7979
<file>qml/Control/Animation/SwitchViewAnimation.qml</file>
8080
<file>qml/Control/Animation/FadeInoutAnimation.qml</file>
81+
<file>qml/ClassificationView/ClassificationItem.qml</file>
82+
<file>qml/ClassificationView/ClassificationView.qml</file>
83+
<file>qml/ClassificationView/ClassificationDetailView.qml</file>
8184
</qresource>
8285
</RCC>
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
import QtQuick
6+
import QtQuick.Layouts
7+
import QtQuick.Controls
8+
import org.deepin.dtk 1.0
9+
10+
import org.deepin.album 1.0 as Album
11+
12+
import "../Control"
13+
import "../Control/ListView"
14+
import "../"
15+
import "../ThumbnailImageView"
16+
17+
/**
18+
* @brief 分类详情视图
19+
* 展示指定分类下的所有图片,参考自定义相册的实现方式
20+
*/
21+
BaseView {
22+
id: classificationDetailView
23+
24+
property string classificationName: "" // 分类名称
25+
property string className: "" // 分类的类别名
26+
property int totalImages: 0 // 总图片数
27+
property int filterType: 0 // 筛选类型,默认所有
28+
property string numLabelText: "" // 总数标签显示内容
29+
property string selectedText: getSelectedText(selectedPaths)
30+
property alias selectedPaths: theView.selectedUrls
31+
32+
/**
33+
* @brief 设置分类数据
34+
*/
35+
function setClassificationData(name, className) {
36+
classificationName = name || ""
37+
classificationDetailView.className = className || ""
38+
}
39+
40+
/**
41+
* @brief 刷新分类详情视图内容
42+
*/
43+
function flushClassificationDetailView() {
44+
// 如果分类名为空,不执行数据加载
45+
if (!classificationDetailView.className || classificationDetailView.className === "") {
46+
return
47+
}
48+
49+
// 触发数据加载
50+
dataModel.className = className
51+
theView.proxyModel.refresh(filterType)
52+
GStatus.selectedPaths = theView.selectedUrls
53+
getNumLabelText()
54+
}
55+
56+
/**
57+
* @brief 清理视图状态数据
58+
*/
59+
function clearViewState() {
60+
// 清空数据属性
61+
classificationName = ""
62+
className = ""
63+
totalImages = 0
64+
numLabelText = ""
65+
66+
// 清空选中状态
67+
if (theView && theView.selectedUrls) {
68+
theView.selectedUrls = []
69+
}
70+
71+
// 清空状态栏显示
72+
GStatus.statusBarNumText = ""
73+
}
74+
75+
/**
76+
* @brief 刷新总数标签
77+
*/
78+
function getNumLabelText() {
79+
var photoCount = dataModel.rowCount()
80+
81+
if(photoCount === 0) {
82+
numLabelText = ""
83+
} else if(photoCount === 1) {
84+
numLabelText = qsTr("1 photo")
85+
} else {
86+
numLabelText = qsTr("%1 photos").arg(photoCount)
87+
}
88+
89+
if (show) {
90+
GStatus.statusBarNumText = numLabelText
91+
}
92+
}
93+
94+
/**
95+
* @brief 刷新选中项目标签内容
96+
*/
97+
function getSelectedText(paths) {
98+
if (!show)
99+
return "";
100+
101+
var selectedNumText = GStatus.getSelectedNumText(paths, numLabelText)
102+
if (show)
103+
GStatus.statusBarNumText = selectedNumText
104+
return selectedNumText
105+
}
106+
107+
// 筛选类型改变处理事件
108+
onFilterTypeChanged: {
109+
if (show)
110+
flushClassificationDetailView()
111+
}
112+
113+
onShowChanged: {
114+
if (show && classificationDetailView.className !== "") {
115+
flushClassificationDetailView()
116+
showAnimation.start()
117+
} else if (!show) {
118+
// 视图隐藏时清理状态数据,避免下次显示时出现异常
119+
clearViewState()
120+
}
121+
}
122+
123+
// 分类详情标题栏区域
124+
Item {
125+
id: classificationDetailTitleRect
126+
width: parent.width - GStatus.verticalScrollBarWidth
127+
height: GStatus.thumbnailViewTitleHieght - 10
128+
visible: classificationDetailView.className !== ""
129+
130+
// 返回按钮
131+
IconButton {
132+
id: backButton
133+
anchors {
134+
left: parent.left
135+
leftMargin: 20
136+
top: parent.top
137+
topMargin: 12
138+
}
139+
width: 30
140+
height: 30
141+
icon.name: "go-previous"
142+
icon.width: 16
143+
icon.height: 16
144+
145+
onClicked: {
146+
// 返回分类视图
147+
GStatus.currentViewType = Album.Types.ViewClassification
148+
}
149+
}
150+
151+
// 分类名称标签
152+
Label {
153+
id: classificationLabel
154+
anchors {
155+
top: parent.top
156+
topMargin: 12
157+
left: backButton.right
158+
leftMargin: 8
159+
}
160+
height: 30
161+
font: DTK.fontManager.t3
162+
text: qsTr(classificationName)
163+
}
164+
165+
Label {
166+
id: classificationNumLabel
167+
anchors {
168+
top: classificationLabel.bottom
169+
topMargin: 10
170+
left: backButton.right
171+
leftMargin: 8
172+
}
173+
font: DTK.fontManager.t6
174+
text: numLabelText !== "" ? numLabelText : qsTr("0 item")
175+
}
176+
177+
MouseArea {
178+
anchors.fill: parent
179+
onPressed: (mouse)=> {
180+
theView.selectAll(false)
181+
mouse.accepted = false
182+
}
183+
}
184+
}
185+
186+
// 缩略图列表控件
187+
ThumbnailListViewAlbum {
188+
id: theView
189+
anchors {
190+
top: classificationDetailTitleRect.bottom
191+
topMargin: 10
192+
}
193+
width: parent.width
194+
height: parent.height - classificationDetailTitleRect.height - m_topMargin
195+
thumnailListType: Album.Types.ThumbnailNormal
196+
proxyModel.sourceModel: Album.ImageDataModel {
197+
id: dataModel;
198+
modelType: Album.Types.ClassificationDetail
199+
}
200+
201+
visible: numLabelText !== "" && classificationDetailView.className !== ""
202+
property int m_topMargin: 10
203+
}
204+
205+
// 筛选无内容时,显示无结果
206+
Label {
207+
anchors {
208+
top: classificationDetailTitleRect.bottom
209+
left: parent.left
210+
bottom: theView.bottom
211+
right: parent.right
212+
centerIn: parent
213+
}
214+
visible: numLabelText === "" && filterType > 0
215+
font: DTK.fontManager.t4
216+
color: Qt.rgba(85/255, 85/255, 85/255, 0.4)
217+
text: qsTr("No results")
218+
}
219+
220+
// 无内容时显示
221+
Label {
222+
anchors {
223+
top: classificationDetailTitleRect.bottom
224+
left: parent.left
225+
bottom: theView.bottom
226+
right: parent.right
227+
centerIn: parent
228+
}
229+
visible: numLabelText === "" && filterType === 0
230+
font: DTK.fontManager.t4
231+
color: Qt.rgba(85/255, 85/255, 85/255, 0.4)
232+
text: qsTr("No photos in this classification")
233+
}
234+
235+
NumberAnimation {
236+
id: showAnimation
237+
target: theView
238+
property: "anchors.topMargin"
239+
from: 10 + theView.height
240+
to: 10
241+
duration: GStatus.sidebarAnimationEnabled ? GStatus.animationDuration : 0
242+
easing.type: Easing.OutExpo
243+
}
244+
}

0 commit comments

Comments
 (0)