Compare commits

...

137 Commits

Author SHA1 Message Date
zty199
e64e7fcae2 Reorganize codes
Reorganize all parts of codes;
Fix a bug that index page color is not correct when initialized.
2021-04-17 15:43:11 +08:00
zty199
6fd3c40e97 Reorganize part of codes
Reorganize spark-store.pro, widget.h and widget.cpp.
2021-04-17 04:30:28 +08:00
Jerry
c80737a458 Merge branch 'multiple' of gitee.com:deepin-community-store/spark-store into multiple 2021-04-17 03:19:44 +08:00
Jerry
ebf30e67f9 4线程到5线程 2021-04-17 03:19:38 +08:00
Jerry
90b684af87 Merge branch 'master' of gitee.com:deepin-community-store/spark-store into multiple 2021-04-17 03:18:40 +08:00
Jerry
3ff363d7b1 4线程切到5线程 2021-04-17 03:16:22 +08:00
Jerry
f7ced7739c !17 多线程下载合并
* Update README.md
* Improve Features
* 修改获取线路的域名
* 修改默认源
* 修改多线程下载域名和图片服务器
* 修正取消下载的闪退问题
* 将图片下载由 curl 转为 QtNetworkService
* 切换多域名下载,提高下载速度
* 完成并发请求下载
2021-04-17 01:35:51 +08:00
zty199
e12f617f59 Update README.md 2021-04-17 01:16:01 +08:00
zty199
d164aec86d Improve Features
Fix a bug that annotations in server list can be chosen in combobox;
Fix a bug that index page don't follow system theme;
Disable qDebug/qWarning output in Release version;
Update default server list.
2021-04-17 00:53:26 +08:00
Jerry
96cd1b9918 修改获取线路的域名 2021-04-16 19:05:30 +08:00
Jerry
393c8220f5 修改默认源 2021-04-16 18:48:01 +08:00
Jerry
01d1543cc4 修改多线程下载域名和图片服务器 2021-04-16 12:19:22 +08:00
metanoia1989
4ccc8c0dae 修正取消下载的闪退问题 2021-03-06 17:00:55 +08:00
metanoia1989
815036e28f 将图片下载由 curl 转为 QtNetworkService 2021-03-06 15:18:31 +08:00
metanoia1989
9cc68fac86 切换多域名下载,提高下载速度 2021-02-20 06:47:57 +08:00
metanoia1989
2f8c11a30b 完成并发请求下载 2021-02-16 23:00:27 +08:00
1a4b1176fb !16 Improve Features
* Improce Features
* History Backup for Version 3.0-stable
2021-01-30 21:12:20 +08:00
3101f1fe70 Restore Patches 2020-12-17 00:18:10 +08:00
58f32c119a Bump version to 3.0 2020-12-16 23:49:54 +08:00
ccdcf407cc 调整安装选项顺序
调整安装选项顺序;
更新服务器源优先级;
重新打包。
2020-12-15 00:30:26 +08:00
1a18a51d3c !15 修正搜索功能的几个BUG
Merge pull request !15 from 枯叶蚊/search
2020-12-12 14:48:02 +08:00
metanoia1989
ab88af006b 修正搜索列表应用重复点击进入失败的问题 2020-12-10 22:46:55 +08:00
metanoia1989
d02900cb10 Merge branch 'search' of gitee.com:deepin-community-store/spark-store into search
合并主分支的搜索图标
2020-12-10 21:32:15 +08:00
metanoia1989
3e473c091a 解决从详情页返回列表页的进度恢复问题 2020-12-10 21:31:53 +08:00
shenmo
84541d0c22 修改: translations/spark-store_zh_CN.ts 2020-12-10 16:41:25 +08:00
shenmo
51f84bed1b 修改: src/downloadlist.cpp
修改:     src/spark-store.pro
	删除:     trans/spark-store_en.qm
	删除:     trans/spark-store_fr.qm
	删除:     trans/spark-store_zh_CN.qm
	重命名:   trans/spark-store_en.ts -> translations/spark-store_en.ts
	重命名:   trans/spark-store_fr.ts -> translations/spark-store_fr.ts
	重命名:   trans/spark-store_zh_CN.ts -> translations/spark-store_zh_CN.ts
2020-12-10 16:40:42 +08:00
4a0acf0575 修改: assets/icons/category_active.svg
新文件:   assets/icons/category_active.svg.bak
	修改:     assets/icons/category_active_dark.svg
	新文件:   assets/icons/category_active_dark.svg.bak
2020-12-10 12:35:14 +08:00
metanoia1989
e28d1c39ac 返回列表不重新加载 2020-12-08 23:31:40 +08:00
metanoia1989
f58201a612 合并来自主干的分支 2020-12-08 22:48:27 +08:00
405d3b6986 Try to fix bugs
尝试修复多屏幕下截图全屏预览的偏移问题。
2020-12-06 12:32:09 +08:00
b619d3cc7b Fix Bugs
修复了搜索结果页存在页边距的问题;
修复了多次搜索同时触发导致的崩溃问题。
2020-12-05 18:45:47 +08:00
枯叶蚊
14e3e7f9a2 !14 星火商店搜索功能
* 更换搜索服务器域名为星火的域名
* 更新搜索服务器为线上服务器
* 完成搜索功能
* 解决搜索结果图标锯齿问题
* 更新appitem的样式
* 完成应用搜索列表的滚动问题
* 合并master分支
* 添加一些文件到忽略列表
* 更新项目结构
* 更新搜索列表UI
* 添加 QtNetworkService库
2020-12-05 16:06:52 +08:00
metanoia1989
1d0e0cc65c 更换搜索服务器域名为星火的域名 2020-12-05 13:53:13 +08:00
metanoia1989
7a5b982dea 更新搜索服务器为线上服务器 2020-12-02 22:22:59 +08:00
metanoia1989
91fcab56df 完成搜索功能 2020-12-01 22:10:52 +08:00
metanoia1989
4315f04023 解决搜索结果图标锯齿问题 2020-11-30 22:44:25 +08:00
metanoia1989
736ede0742 更新appitem的样式 2020-11-29 22:25:20 +08:00
metanoia1989
a73a4416fc 完成应用搜索列表的滚动问题 2020-11-29 20:28:59 +08:00
shenmo
62f0dd097c update README.md. 2020-11-29 12:11:42 +08:00
metanoia1989
6f3e4398df 合并master分支 2020-11-28 20:59:22 +08:00
metanoia1989
3cca0d87fe 添加一些文件到忽略列表 2020-11-21 11:18:20 +08:00
b7e038bd88 Fix Bugs
Fix a bug that dirname in ~/.local/share/spark-union/ is translated into
Chinese.
2020-11-19 14:16:16 +08:00
shenmo
4f0e00ad76 修改了一个图标的名字 2020-11-18 15:17:36 +08:00
shenmo
579008e8b2 !13 更新部分语言翻译
Merge pull request !13 from 枯叶蚊/master
2020-11-14 12:17:36 +08:00
metanoia1989
ccb405c983 更新部分语言翻译 2020-11-14 11:02:50 +08:00
metanoia1989
66ef37c1ca 更新项目结构 2020-11-13 22:57:37 +08:00
metanoia1989
8972425c7c 更新搜索列表UI 2020-11-08 22:35:31 +08:00
metanoia1989
2ae6e80785 添加 QtNetworkService库 2020-11-08 20:41:18 +08:00
f5788efb47 update README.md. 2020-10-27 17:26:56 +08:00
32bc272791 update README.md. 2020-10-27 17:26:12 +08:00
c5f04b5675 update README.md. 2020-10-27 17:24:50 +08:00
cf208d0736 Roll back to old version of DAboutDialog
Roll back to old version of DAboutDialog for compability with Ubuntu.
2020-10-27 17:15:08 +08:00
fdc7cb4cb6 修改: README.md 2020-10-27 16:55:56 +08:00
8e89ce3ae3 keep old version AboutDialog
Keep old version AboutDialog to be compatible with ubuntu.
2020-10-26 23:29:41 +08:00
ee51f59874 Bump version to 2.0.2.5
Bump version to 2.0.2.5.
2020-10-26 22:50:21 +08:00
3d09a28794 main.coo改回定制,2.0.2.5版本号,改了logo配色 2020-10-26 17:47:24 +08:00
4b05758479 main.coo改回定制,2.0.2.5版本号,改了logo配色 2020-10-26 17:47:14 +08:00
74d9d0b563 main.coo改回定制,2.0.2.5版本号,改了logo配色 2020-10-26 17:44:19 +08:00
123456
f1a4f7acb5 main.cpp改回spark定制 2020-10-26 17:41:46 +08:00
93be66e871 Improve menu list on the left
Set StyleSheet of menu list on the left to
"text-align:left;padding-left:15px;" when system locale is not "zh_CN";

Update part of names in menu list and corresponding translation.
2020-10-24 20:24:31 +08:00
7b46cca1d4 Fix Bugs
Fix a Bug in updateUI(), everytime calling this method will set fonts to
system default display font and delete custom font settings, have to set
custom fonts again here.
2020-10-23 15:15:53 +08:00
c529367998 !12 卸载安装包时,将pkg转换为小写,让dpkg能够识别
Merge pull request !12 from 枯叶蚊/master
2020-10-20 09:02:27 +08:00
metanoia1989
beae3e3efa 应用下载后面换行 2020-10-19 22:44:44 +08:00
metanoia1989
9706480931 添加.vscode和build为忽略目录 2020-10-19 22:17:22 +08:00
metanoia1989
ed64eb6f5b 卸载安装包时,将pkg-name转换为小写 2020-10-19 22:16:59 +08:00
8f6e5408ae 修改: main.cpp,使用了旧式的DAboutDialog,放弃了定制,可以在ubuntu上运行了。仍然无法打开关于 2020-10-19 15:16:54 +08:00
d5783458fc update README.md. 2020-10-19 00:14:10 +08:00
6e05066dd9 update README.md. 2020-10-19 00:11:33 +08:00
58aa5a3787 update README.md. 2020-10-19 00:10:21 +08:00
6087c8a5c8 update README.md. 2020-10-18 10:13:06 +08:00
ff758946be update README.md. 2020-10-18 10:12:30 +08:00
d1cbf95447 update README.md. 2020-10-18 10:11:18 +08:00
Jerry
187ce7e277 编译说明增加一个组件 2020-10-16 15:07:18 +08:00
d3e4f75254 !11 修复中文环境无法显示通知的问题
Merge pull request !11 from lidanger/master
2020-10-15 15:12:53 +08:00
lidanger
949eb33511 修复中文环境通知,上次少加两行初始化代码 2020-10-14 22:20:55 +08:00
lidanger
3f65002dc9 修复中文环境无法显示通知的问题 2020-10-14 20:57:32 +08:00
RigoLigoRLC
cfd4c7689a RigoLigo合并随机崩溃的修复 2020-10-09 02:08:01 +08:00
RigoLigoRLC
367c8d857c 修复 加载应用程序信息时随机崩溃
这个问题就是因为在异步操作时直接修改GUI,导致事件发生时GUI与异步加载线程不同步导致程序崩溃。将所有加载操作已移至
SpkAppInfoLoaderThread,在其需要操作GUI时阻塞该工作线程并发射信号让主线程操作GUI,以解决该问题。
2020-10-09 02:06:28 +08:00
b41f846ea4 2.0.2.3,修复depend 2020-10-07 22:08:13 +08:00
1dcd7d1c5c fr 2020-10-06 19:48:14 +08:00
01ac388f50 更改qm名字适配dtk 2020-10-06 17:25:25 +08:00
cfbbd34695 !9 add trans/lang_French.ts.
Merge pull request !9 from App4Deepin/N/A
2020-10-06 09:33:16 +08:00
App4Deepin
b7afc5ec8a add trans/lang_French.ts. 2020-10-05 23:34:27 +08:00
b911158274 修改: trans/spark-store_en.ts
修改:     trans/spark-store_zh_CN.qm,支持了贡献翻译
	修改:     trans/spark-store_zh_CN.ts,支持贡献翻译
	修改:     widget.cpp,微调原文,天加两个tips
2020-10-05 22:11:38 +08:00
67c28d0224 删除构建文件 2020-10-05 22:04:13 +08:00
1d65bfc66d 版本号不再翻译,贡献翻译按钮翻译。to-do:翻译按界面翻译 2020-10-05 19:49:37 +08:00
0e595db328 logo 2020-10-05 10:44:20 +08:00
7a16028e38 logo 2020-10-05 10:44:08 +08:00
RigoLigoRLC
1b56d3ad52 更改贡献翻译按钮提示框为DDialog,并修复遗留翻译问题 2020-10-04 00:13:06 +08:00
431e7b555f dwine5-dwine2-tags 2020-10-03 11:26:21 +08:00
RigoLigoRLC
1cbabe7cc5 详情页面添加翻译按钮 2020-10-03 01:30:05 +08:00
d027d513a3 微调界面 2020-10-02 21:33:03 +08:00
RigoLigoRLC
eff8878e8b 修复下载软件失败导致的崩溃,更改APT源更新逻辑
下载失败会导致发射的progress信号已接受数和总数均为0,导致计算进度时被零除;
APT源更新逻辑代码重构,将需要复用的值单独创建变量,重新格式化代码,以及加入了已经废弃的星火源降低优先级代码
2020-10-02 12:03:40 +08:00
f0bf0e5aec 微调翻译,增添翻译 2020-10-01 10:12:13 +08:00
RigoLigoRLC
678a4229f7 将通知移至专用函数,并更新中文翻译ts文件 2020-10-01 02:53:10 +08:00
e89c14b5ca QObject::tr---update-ubuntu-tag 2020-09-30 15:39:30 +08:00
RigoLigoRLC
d4c734627a 添加了用户端QtCreator配置缓存的忽略 2020-09-30 01:07:42 +08:00
8830b14e10 translate#4 2020-09-29 19:49:35 +08:00
8c9f5b8ba6 尝试添加tag.Trying to add tags 2020-09-29 17:44:55 +08:00
badc3aa089 translate-to-English 2020-09-29 17:18:43 +08:00
9c9317dea0 更多的翻译 2020-09-29 12:37:49 +08:00
594f71f98a 英文 2020-09-28 19:50:39 +08:00
c45bf8ffaf !8 菜单新增“软件投稿”选项,添加刷新按钮
Merge pull request !8 from RigoLigo/master
2020-09-28 11:04:35 +08:00
RigoLigoRLC
c9c0357646 新增刷新按钮(和WebEngineView的右键菜单并存) 2020-09-27 15:02:41 +08:00
RigoLigoRLC
bd8f660fe9 菜单中加入投稿按钮,拉起默认浏览器进入投稿
https://gitee.com/deepin-community-store/spark-store/issues/I1VD2Q
2020-09-27 02:06:40 +08:00
538c742303 !7 新增了下载app.json或图标失败将给用户发送提醒
Merge pull request !7 from RigoLigo/master
2020-09-26 10:36:42 +08:00
RigoLigoRLC
c23e816017 新增 下载app.json或图标失败将给用户发送提醒
这里改变了widget这个类里面loadappinfo的返回值,使处理异常返回状态可以实现
2020-09-26 01:19:57 +08:00
2be0e1b523 2.0+1发布 2020-09-23 17:32:26 +08:00
eacc56eaf3 完全改好了 2020-09-23 17:30:46 +08:00
9458bbf2c9 更新DAboutDialog 2020-09-23 16:08:44 +08:00
moshengren
e472ba76f1 !6 Update of english translations
Merge pull request !6 from alvarosamudio/master
2020-09-19 10:20:37 +08:00
alvarosamudio
74bd3ae018 update trans/lang_English.ts. 2020-09-18 23:57:40 +08:00
Jerry
ccc2bb8898 更新README 2020-09-18 15:46:43 +08:00
a59e2e0527 fanyi 2020-09-18 12:36:42 +08:00
2c205bf5db 翻译 2020-09-17 16:49:23 +08:00
efc6c50f88 微调DAboutDialogue内容 2020-09-12 13:36:37 +08:00
ce263c3cb4 更新翻译 2020-09-12 13:29:34 +08:00
78ac339629 翻译更新 2020-09-11 13:36:29 +08:00
58359aa63a !5 更新了图片服务器 线路说明转移到服务端上
Merge pull request !5 from Jerry/master
2020-09-08 17:20:58 +08:00
jerry
e839f7836f 不在客户端现实线路说明, 线路说明写到server.list中 2020-09-08 15:31:55 +08:00
jerry
b34c50d2e0 图片服务器使用jsdelivr cdn 2020-09-08 14:19:47 +08:00
jerry
4d4e3e3e3a 更新图片服务器 2020-09-05 18:20:14 +08:00
jerry
10741a1d92 更新了线路说明, 更具有普适性 2020-09-03 18:40:04 +08:00
maicss
b78ca0e103 新增 软件标签 2020-09-03 10:05:48 +08:00
maicss
3a0ed16a21 Merge branch 'master' of https://gitee.com/deepin-community-store/spark-store 2020-09-02 14:28:52 +08:00
maicss
0a3d583d76 优化UI细节 2020-09-02 14:28:09 +08:00
张天怿
f5121a0405 Delete .pro.user files
Delete useless .pro.user files.
2020-09-01 11:03:20 +08:00
Maicss
e568ddafba 更新 文档
Merge pull request !2 from Jerry/master
2020-08-29 07:42:02 +08:00
jerry
111174a46f update 2020-08-29 00:43:36 +08:00
jerry
6de25a299f update readme 2020-08-29 00:41:23 +08:00
jerry
cbd57a3e25 增加线路说明, 如何编译的说明 2020-08-29 00:27:02 +08:00
maicss
6e083f295b 新增 深度安装器安装选项 2020-08-28 15:03:10 +08:00
maicss
b82a821d01 修复UI细节问题 2020-08-28 14:45:39 +08:00
Maicss
6578af935e 优化UI 2020-08-26 19:58:42 +08:00
Maicss
fe4143a3f7 修改 web控件 2020-08-26 09:39:55 +08:00
Maicss
8952be33c4 修改 web控件 2020-08-26 09:27:34 +08:00
Maicss
4e4f55995e 修改 web控件 2020-08-26 09:26:59 +08:00
103 changed files with 8081 additions and 1992 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.pro.user*
build/
.vscode/
Lib/

BIN
Logo-Spark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
Logo-Spark.png.bak Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

368
README.md
View File

@@ -1,29 +1,38 @@
# 星火商店
# 星火应用商店
众所周知国内的Linux应用比较少wine应用难以获取优质工具分散在民间各大论坛无法形成合力难以改善生态
生态构建需要的不是某一方的单打独斗,而是人人行动起来,汇聚星火,产生燎原之势
我们创建了这个应用商店广泛收录大家需要的软件包搜集优质小工具主动适配wine应用存放到储存库供大家获取
我们支持Deepin 20 ; Ubuntu 20.04 LTS ; UOS Home 20
希望看到这里的人也可以加入我们的队伍开发或者投递应用都很欢迎共同构建Linux应用生态
### [在这里投稿](http://upload.spark-app.store)
#### 介绍
星火商店商店,由深度社区爱好者维护
web页面部分正在开发当中详情请见[web仓库](https://gitee.com/deepin-community-store/DCSAPP_WEB)
#### 说明
当前服务器线路列表(项目中包含):
```
http://cdn.jerrywang.top/
http://sucdn.jerrywang.top/
http://store.jerrywang.top/
http://dcstore.shenmo.tech/
https://d.store.deepinos.org.cn/
https://store.deepinos.org.cn/
```
#### 参数
#### 调用参数(spk规则)
参数只有一个Url该url应当遵循这种格式`spk://<任意合法字符>/web分类/包名`
例如:
[spk://abcdefg/network/firefox-zh](spk://abcdefg/network/firefox-zh)
[spk://abcdefg/games/store.spark-app.hmcl](spk://abcdefg/games/store.spark-app.hmcl)
[spk://higklmn/network/firefox-zh](spk://higklmn/network/firefox-zh)
可选的web分类
@@ -33,23 +42,346 @@ http://dcstore.shenmo.tech/
| 社交沟通 | chat |
| 音乐欣赏 | music |
| 视频播放 | video |
| 图形图像 | image_graphics |
| 图形图像 | graphics |
| 游戏娱乐 | games |
| 办公学习 | office |
| 阅读翻译 | reading |
| 编程开发 | development |
| 系统工具 | tools |
| 主题美化 | themes |
| 主题美化 | beautify |
| 其他应用 | others |
#### 目录结构
安装软件过程中产生的包,图标,截图等被储存到`/tmp/deepin-community-store/`中。
配置文件被储存到`~/.config/deepin-community-store/`中。
#### 如何编译
线路文件:新版的线路文件被放置于源服务器中,可随时刷新更新源列表
Deepin V20/UOS 系统下, 安装依赖
#### 参与贡献
```shell
sudo apt install qt5-default libdtkcore-dev libdtkwidget-dev qtwebengine5-dev libnotify-dev
```
1. Fork 本仓库
```shell
git clone https://gitee.com/deepin-community-store/spark-store.git
cd spark-store
mkdir build
cd build
qmake ..
make -j
```
./build文件下的spark-store即为可执行文件
# 星火应用商店文档
# 目录结构
几个目录结构
```
/
/icons 图标文件夹
/tags 首页图标
/tras 多语言翻译
```
主要的文件分析
```js
spark-store.pro Qt工程配置文件
ssinstall 调用包安装器的脚本
icons.qrc 图标资源文件
main.cpp 入口文件
widget.h widget.cpp widget.ui 主要窗口控件
downloadlist.h downloadlist.cpp downloadlist.ui 单个软件的下载安装展示控件
progressload.h progressload.cpp 网页加载显示 得在deepin上编译运行才能搞清楚
workerthreads.h workerthreads.cpp 应用信息加载线程
image_show.h image_show.cpp 应用页面截图预览控件
big_image.h big_image.cpp 大图查看控件
```
# 使用的开源库及第三方工具
* GDebi 一个 Ubuntu 软件中心的轻量级替代品 https://linux.cn/article-4982-1.html
* libnotify 系统通知 https://developer.gnome.org/libnotify/unstable/
# 源码分析
## 应用的组成部分
左侧应用分类菜单
主窗口的下拉菜单
应用列表页面
应用详情页面
应用首页,有几个链接跳转
商店设置页面
下载列表页面
## 应用初始化,及主控件加载
初始化 `DApplication` 进入事件循环。
设置关于我们弹窗 `DAboutDialog`
主控件 Widget 根据不同屏幕大小自适应。
首页打开webview页面如果传入了`spk://`参数,会打开应用详情页。
```cpp
// main.cpp
QString arg1=argv[1];
if(arg1.left(6)=="spk://"){
w.openUrl(QUrl(argv[1]));
}
// widget.cpp
void Widget::openUrl(QUrl u)
{
QString app=serverUrl + "store"+u.path()+"/app.json";
ui->webEngineView->setUrl(app); // 会触发 webEngineView 的
}
```
## Tags处理方式
**Tags处理方式**
```cpp
// widget.cpp
QString tags=json["Tags"].toString(); //Read the Tags
QStringList tagList=tags.split(";");
for (int i=0;i<tagList.size();i++) {
if(tagList[i]=="community")
ui->tag_community->show();//Tags icon shows like this
if(tagList[i]=="ubuntu")
ui->tag_ubuntu->show();
if(tagList[i]=="deepin")
ui->tag_deepin->show();
if(tagList[i]=="uos")
ui->tag_uos->show();
if(tagList[i]=="dtk5")
ui->tag_dtk5->show();
if(tagList[i]=="dwine2")
ui->tag_dwine2->show();
if(tagList[i]=="dwine5")
ui->tag_dwine5->show();
if(tagList[i]=="a2d")
ui->tag_a2d->show();
}
```
**Widget 初始化**
```cpp
void Widget::initConfig()
{
...
// 读取服务器URL并初始化菜单项的链接
QSettings readConfig(QDir::homePath()+"/.config/spark-store/config.ini",QSettings::IniFormat);
if(readConfig.value("server/choose").toString()!=""){
ui->comboBox_server->setCurrentText(readConfig.value("server/choose").toString());
appinfoLoadThread.setServer(serverUrl=readConfig.value("server/choose").toString());
}else {
appinfoLoadThread.setServer(serverUrl="http://sucdn.jerrywang.top/"); // 默认URL
}
configCanSave=true; // 防止触发保存配置信号
menuUrl[0]=serverUrl + "store/#/"; // 首页
// 下面是各个应用分类页面直接加载的webview的
// 每个连接对应一个左侧的菜单项,在构造函数用连接到 chooseLeftMenu 槽函数
menuUrl[1]=serverUrl + "store/#/network";
...
menuUrl[12]=serverUrl + "store/#/others";
...
ui->webfoot->hide();
//初始化首页
ui->webEngineView->setUrl(menuUrl[0]);
}
/**
* 菜单切换逻辑
*
*/
void Widget::chooseLeftMenu(int index)
{
nowMenu=index;
updateUI();
left_list[index]->setStyleSheet("color:#FFFFFF;background-color:"+main_color.name()+";border-radius:8;border:0px");
// index <=12 加载某个分类的应用列表的webviejw
// index == 13 加载下载列表页面
if(index<=12){
if(themeIsDark){
darkurl = URL
ui->webEngineView->setUrl(darkurl);
}else {
ui->webEngineView->setUrl(menuUrl[index]);
}
ui->stackedWidget->setCurrentIndex(0);
}else if (index==13) {
ui->stackedWidget->setCurrentIndex(1);
}
}
```
## 应用下载安装卸载分析
**应用详情页面加载**
```cpp
/**
* 加载单个应用的信息
*/
void Widget::on_webEngineView_urlChanged(const QUrl &arg1)
{
//分析出服务器中的分类名称
...
//如果是app.json就打开详情页
if(arg1.path().right(8)=="app.json"){
...
// 读取相应的应用信息
appinfoLoadThread.requestInterruption();
appinfoLoadThread.wait(100);
appinfoLoadThread.setUrl(arg1);
appinfoLoadThread.start();
}
}
// 设置详情页的APP信息
SpkAppInfoLoaderThread::requestSetAppInformation() -> Widget::sltAppinfoDetails()
// 设置详情页的APP图标
SpkAppInfoLoaderThread::finishedIconLoad() -> Widget::sltAppinfoIcon()
// 设置详情页的APP截图
SpkAppInfoLoaderThread::finishedScreenshotLoad() -> Widget::sltAppinfoScreenshot()
// 下载APP详情信息线程
void SpkAppInfoLoaderThread::run()
{
QProcess get_json;
get_json.start("curl -o app.json " + targetUrl.toString());
QFile app_json("app.json");
app.json
}
```
**应用下载**
Widget::on_pushButton_download_clicked() 是点击下载的安装方法。
最终使用的是 `QNetwrokAccessManager` 进行GET请求获取数据写入文件。
```cpp
void Widget::on_pushButton_download_clicked()
{
if(!isBusy){
file = new QFile(fileName);
...
nowDownload+=1;
startRequest(urList.at(nowDownload-1)); // 进行链接请求
}
}
void Widget::startRequest(QUrl url)
{
reply = manager->get(QNetworkRequest(url));
// 请求响应完成,关闭文件,清理下载队列
connect(reply,SIGNAL(finished()),this,SLOT(httpFinished()));
// 接收应用下载数据
connect(reply,SIGNAL(readyRead()),this,SLOT(httpReadyRead()));
// 更新应用下载进度
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(updateDataReadProgress(qint64,qint64)));
}
```
使用 QSettings 来读取配置,更换服务源
```cpp
void Widget::on_comboBox_server_currentIndexChanged(const QString &arg1)
{
appinfoLoadThread.setServer(arg1); // 服务器信息更新
if(configCanSave){
ui->label_setting1->show();
QSettings *setConfig=new QSettings(QDir::homePath()+"/.config/spark-store/config.ini",QSettings::IniFormat);
setConfig->setValue("server/choose",arg1);
}
}
```
使用 `QProcess` 来调用各种小文件下载、包安装卸载的命令。
**应用安装**
```cpp
void Widget::httpFinished() // 完成下载
{
...
download_list[nowDownload-1].readyInstall();
download_list[nowDownload-1].free=true;
if(nowDownload<allDownload){ // 如果有排队则下载下一个
...
}
}
void downloadlist::readyInstall()
{
...
ui->pushButton_install->setEnabled(true);
ui->pushButton_install->show();
ui->pushButton_2->hide();
Widget::sendNotification(tr("Finished downloading %1, awaiting to install").arg(ui->label->text()), 5000,
"/tmp/spark-store/icon_"+QString::number(num).toUtf8()+".png");
}
void downloadlist::on_pushButton_install_clicked()
{
//弹出菜单
menu_install->exec(cursor().pos());
}
downloadlist menu_install
downloadlist::install()
gdebi, dpkg, deepin-deb-installer
void downloadlist::install(int t)
{
QtConcurrent::run([=](){
QProcess installer;
installer.start("pkexec gdebi -n /tmp/spark-store/"+ui->label_filename->text().toUtf8());
installer.start("pkexec ssinstall /tmp/spark-store/"+ui->label_filename->text().toUtf8());
installer.start("deepin-deb-installer /tmp/spark-store/"+ui->label_filename->text().toUtf8());
});
}
```
**应用卸载**
```cpp
void Widget::on_pushButton_uninstall_clicked()
{
QtConcurrent::run([=](){
uninstall.start("pkexec apt purge -y "+pkgName);
});
}
```
**仓库源更新**
```cpp
// 更新源列表
void Widget::on_pushButton_updateServer_clicked()
{
QtConcurrent::run([=](){
...
QFile::remove(QDir::homePath().toUtf8()+"/.config/spark-store/server.list");
system("curl -o "+QDir::homePath().toUtf8()+"/.config/spark-store/server.list http://dcstore.shenmo.tech/store/server.list");
server.open(QDir::homePath().toUtf8()+"/.config/spark-store/server.list",std::ios::in);
...
while (getline(server,lineTmp)) {
ui->comboBox_server->addItem(QString::fromStdString(lineTmp));
}
});
}
// 更新星火商店apt源
void Widget::on_pushButton_updateApt_clicked()
{
QtConcurrent::run([=](){
comboBox_server /tmp/spark-store/sparkstore.list
bash脚本 sparkstore.list /etc/apt/sources.list.d/
使QProcess pkexec update.sh
}):
}
```
## 发送系统通知
```cpp
#include <libnotify/notify.h>
static NotifyNotification *_notify = nullptr; // 初始化
notify_init(tr("Spark\\ Store").toLocal8Bit()); // 构造函数初始化
notify_uninit(); // 析构函数调用
void Widget::sendNotification(const QString &message, const int msTimeout, const QString &icon)
{
if(_notify == nullptr)
{
_notify = notify_notification_new(tr("Spark\\ Store").toLocal8Bit(), message.toLocal8Bit(), icon.toLocal8Bit());
notify_notification_set_timeout(_notify, msTimeout);
}
else
notify_notification_update(_notify, tr("Spark\\ Store").toLocal8Bit(), message.toLocal8Bit(), icon.toLocal8Bit());
notify_notification_show(_notify, nullptr);
}
```

Binary file not shown.

View File

@@ -30,5 +30,40 @@
<file>icons/homepage_dark.svg</file>
<file>icons/category_active_dark.svg</file>
<file>icons/category_active.svg</file>
<file>icons/refresh-page-dark.svg</file>
<file>icons/refresh-page.svg</file>
</qresource>
<qresource prefix="/">
<file>../Logo-Spark.png</file>
<file>../src/big_image.cpp</file>
<file>../src/big_image.h</file>
<file>../src/downloadlist.cpp</file>
<file>../src/downloadlist.h</file>
<file>../src/image_show.cpp</file>
<file>../src/image_show.h</file>
<file>../src/main.cpp</file>
<file>../src/progressload.cpp</file>
<file>../src/progressload.h</file>
<file>../src/widget.cpp</file>
<file>../src/widget.h</file>
<file>tags/a2d.png</file>
<file>tags/community.svg</file>
<file>tags/deepin.svg</file>
<file>tags/logo_icon.svg</file>
<file>tags/uos-authorize.svg</file>
<file>tags/a2d-small.png</file>
<file>tags/community-small.png</file>
<file>tags/deepin-small.png</file>
<file>tags/dtk-small.png</file>
<file>tags/uos-small.png</file>
<file>tags/community.png</file>
<file>tags/ubuntu-small.png</file>
<file>tags/ubuntu.png</file>
<file>tags/dwine5-small.png</file>
<file>tags/dwine5.svg</file>
<file>tags/dwine2-small.png</file>
</qresource>
<qresource prefix="/fonts">
<file>fonts/华康少女字体.ttf</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#536076" d="M10.138807,3.52000025 L10.138,12.5230002 L14.6066017,8.05523757 L15.3137085,8.76234435 L9.65685425,14.4191986 L4,8.76234435 L4.70710678,8.05523757 L9.138,12.4870002 L9.138807,3.52000025 L10.138807,3.52000025 Z" transform="rotate(90 9.657 11.518)"/>
</svg>

After

Width:  |  Height:  |  Size: 366 B

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#536076" d="M10.138807,3.52000025 L10.138,12.5230002 L14.6066017,8.05523757 L15.3137085,8.76234435 L9.65685425,14.4191986 L4,8.76234435 L4.70710678,8.05523757 L9.138,12.4870002 L9.138807,3.52000025 L10.138807,3.52000025 Z" transform="rotate(90 9.657 11.518)"/>
</svg>

After

Width:  |  Height:  |  Size: 366 B

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 523 B

After

Width:  |  Height:  |  Size: 523 B

View File

Before

Width:  |  Height:  |  Size: 553 B

After

Width:  |  Height:  |  Size: 553 B

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 865 B

After

Width:  |  Height:  |  Size: 865 B

View File

Before

Width:  |  Height:  |  Size: 895 B

After

Width:  |  Height:  |  Size: 895 B

View File

Before

Width:  |  Height:  |  Size: 929 B

After

Width:  |  Height:  |  Size: 929 B

View File

Before

Width:  |  Height:  |  Size: 959 B

After

Width:  |  Height:  |  Size: 959 B

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 810 B

View File

Before

Width:  |  Height:  |  Size: 828 B

After

Width:  |  Height:  |  Size: 828 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
height="16"
width="16"
sodipodi:docname="refresh-page-dark.svg"
xml:space="preserve"
viewBox="0 0 16 16"
y="0px"
x="0px"
id="Layer_1"
version="1.1"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
inkscape:current-layer="Layer_1"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:cy="8.8351119"
inkscape:cx="12.452215"
inkscape:zoom="34.711876"
showgrid="true"
id="namedview5"
inkscape:window-height="958"
inkscape:window-width="1920"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff"><inkscape:grid
id="grid834"
type="xygrid" /></sodipodi:namedview>
<g
style="fill:#ffffff;fill-opacity:0.60000002"
transform="scale(0.03125,0.0312082)"
id="XMLID_2_">
<path
style="fill:#ffffff;fill-opacity:0.60000002"
d="M 436.6,75.4 C 390.1,28.9 326.7,0 256,0 114.5,0 0,114.5 0,256 0,397.5 114.5,512 256,512 375.2,512 474.8,430.1 503.6,320.2 h -67 C 410.5,394.7 339.8,447.7 256,447.7 149.9,447.7 64.2,362.1 64.2,255.9 64.2,149.7 149.9,64.2 256,64.2 c 53.1,0 100.5,22.3 135,56.8 L 287.7,224.3 H 512 V 0 Z"
id="XMLID_4_" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
height="16"
width="16"
sodipodi:docname="refresh-page.svg"
xml:space="preserve"
viewBox="0 0 16 16"
y="0px"
x="0px"
id="Layer_1"
version="1.1"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
inkscape:current-layer="Layer_1"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:cy="8.8351119"
inkscape:cx="12.452215"
inkscape:zoom="34.711876"
showgrid="true"
id="namedview5"
inkscape:window-height="958"
inkscape:window-width="1920"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff"><inkscape:grid
id="grid834"
type="xygrid" /></sodipodi:namedview>
<g
transform="scale(0.03125,0.0312082)"
id="XMLID_2_">
<path
d="M 436.6,75.4 C 390.1,28.9 326.7,0 256,0 114.5,0 0,114.5 0,256 0,397.5 114.5,512 256,512 375.2,512 474.8,430.1 503.6,320.2 h -67 C 410.5,394.7 339.8,447.7 256,447.7 149.9,447.7 64.2,362.1 64.2,255.9 64.2,149.7 149.9,64.2 256,64.2 c 53.1,0 100.5,22.3 135,56.8 L 287.7,224.3 H 512 V 0 Z"
id="XMLID_4_" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 494 B

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
assets/tags/a2d-small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets/tags/a2d.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/tags/community.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

240
assets/tags/community.svg Normal file
View File

@@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="deepin-community-store.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 200 200"
height="200mm"
width="200mm">
<defs
id="defs2">
<linearGradient
id="linearGradient1200"
inkscape:collect="always">
<stop
id="stop1196"
offset="0"
style="stop-color:#000000;stop-opacity:0.1299435" />
<stop
id="stop1198"
offset="1"
style="stop-color:#dadada;stop-opacity:0.81960785" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient1138">
<stop
style="stop-color:#99e7ea;stop-opacity:1"
offset="0"
id="stop1134" />
<stop
style="stop-color:#007ffc;stop-opacity:1"
offset="1"
id="stop1136" />
</linearGradient>
<linearGradient
id="linearGradient1128"
inkscape:collect="always">
<stop
id="stop1124"
offset="0"
style="stop-color:#99e7ea;stop-opacity:1" />
<stop
id="stop1126"
offset="1"
style="stop-color:#007ffc;stop-opacity:1" />
</linearGradient>
<inkscape:path-effect
only_selected="false"
apply_with_weight="true"
apply_no_weight="true"
helper_size="0"
steps="2"
weight="33.333333"
is_visible="true"
id="path-effect960"
effect="bspline" />
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.98112945,0,3.7714702)"
r="90.135414"
fy="199.86011"
fx="100.35268"
cy="199.86011"
cx="100.35268"
id="radialGradient1130"
xlink:href="#linearGradient1128"
inkscape:collect="always" />
<radialGradient
r="90.135414"
fy="199.86011"
fx="100.35268"
cy="199.86011"
cx="100.35268"
gradientTransform="matrix(1,0,0,0.98112945,0,3.7714702)"
gradientUnits="userSpaceOnUse"
id="radialGradient1132"
xlink:href="#linearGradient1138"
inkscape:collect="always" />
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.98112945,0,3.7714702)"
r="90.135414"
fy="199.86011"
fx="100.35268"
cy="199.86011"
cx="100.35268"
id="radialGradient1202"
xlink:href="#linearGradient1200"
inkscape:collect="always" />
<filter
id="filter1448"
inkscape:label="Drop Shadow"
style="color-interpolation-filters:sRGB;">
<feFlood
id="feFlood1438"
result="flood"
flood-color="rgb(145,145,145)"
flood-opacity="0.372549" />
<feComposite
id="feComposite1440"
result="composite1"
operator="in"
in2="SourceGraphic"
in="flood" />
<feGaussianBlur
id="feGaussianBlur1442"
result="blur"
stdDeviation="5.2918"
in="composite1" />
<feOffset
id="feOffset1444"
result="offset"
dy="0"
dx="0" />
<feComposite
id="feComposite1446"
result="composite2"
operator="over"
in2="offset"
in="SourceGraphic" />
</filter>
<filter
id="filter2201"
inkscape:label="Drop Shadow"
style="color-interpolation-filters:sRGB;">
<feFlood
id="feFlood2191"
result="flood"
flood-color="rgb(145,145,145)"
flood-opacity="0.372549" />
<feComposite
id="feComposite2193"
result="composite1"
operator="in"
in2="SourceGraphic"
in="flood" />
<feGaussianBlur
id="feGaussianBlur2195"
result="blur"
stdDeviation="3.76995"
in="composite1" />
<feOffset
id="feOffset2197"
result="offset"
dy="0"
dx="0" />
<feComposite
id="feComposite2199"
result="composite2"
operator="over"
in2="offset"
in="SourceGraphic" />
</filter>
</defs>
<sodipodi:namedview
inkscape:document-rotation="0"
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="-8"
inkscape:window-height="705"
inkscape:window-width="1366"
showgrid="false"
inkscape:current-layer="layer2"
inkscape:document-units="mm"
inkscape:cy="507.94585"
inkscape:cx="120.33119"
inkscape:zoom="0.35"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-97)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1" />
<g
inkscape:label="Layer 2"
id="layer2"
inkscape:groupmode="layer">
<path
inkscape:connector-curvature="0"
id="path1002"
d="m 91.937746,32.561862 c 0,0 11.145084,-0.157476 22.599704,6.928753 11.45465,7.086223 18.11074,13.857508 20.27783,18.266716 2.16708,4.409213 9.28755,12.125333 9.13277,26.297793 -0.15488,14.17244 -3.40543,23.620766 -8.97797,31.494326 -5.57254,7.87359 -15.63405,13.85751 -27.70786,14.17247 -12.073834,0.31494 -19.813464,-7.55863 -22.444934,-11.96786 -2.63147,-4.4092 -3.71502,-13.85751 -0.46438,-20.628766 3.25065,-6.77132 8.3588,-6.77132 10.06152,-6.14141 1.70272,0.6299 2.16709,1.5747 3.09585,3.30689 0.92875,1.73219 1.54793,1.41726 2.941094,0.47243 1.39311,-0.94484 1.85748,-2.04715 1.54792,-3.46439 -0.30958,-1.41726 -2.167114,-3.14944 -3.715054,-3.62186 -1.54791,-0.47241 -3.71502,-1.88966 -6.81087,-1.41721 -3.09585,0.47238 -9.13276,2.04711 -14.24091,8.97585 -5.10815,6.928746 -4.48899,17.636836 -1.39313,22.675926 3.09585,5.0391 8.97796,14.48739 21.98054,17.79431 13.002594,3.30693 34.054354,-6.45633 41.329594,-21.57361 7.27524,-15.117296 7.43004,-22.203516 7.12048,-34.643766 C 145.96036,67.048164 134.97008,48.466503 122.74148,41.69522 110.51288,34.923934 101.38012,30.987141 91.937746,32.561862 Z"
style="fill:#ffc344;fill-opacity:1;stroke:none;stroke-width:0.309114px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1012"
d="m 98.407556,95.469094 c 0,0 -3.06474,-2.8951 -5.80111,2.22698 -2.73637,5.122046 -4.04983,9.687366 -1.31346,15.254856 2.73637,5.56745 8.42801,8.90792 17.184454,8.79659 8.75636,-0.11148 15.21419,-2.22699 19.0451,-8.5739 3.83094,-6.34692 6.7862,-11.02357 7.11457,-13.361906 0.32839,-2.33835 2.07965,0.334036 1.53239,2.338336 -0.54728,2.00426 -0.43783,5.9015 1.09454,5.12204 1.53237,-0.77942 2.73637,-2.00427 2.95526,-1.44753 0.21893,0.55673 -1.75127,4.00859 -2.68162,5.73449 -0.93039,1.7259 -3.83094,5.90153 -5.5275,7.51607 -1.69652,1.61458 -4.04981,3.89724 -6.84092,5.56747 -2.79109,1.67024 -7.11454,3.61887 -9.52255,4.06427 -2.40801,0.44539 -7.22403,1.39184 -9.96042,1.2805 -2.73635,-0.11137 -6.238924,-0.61243 -10.124564,-2.56104 -3.88565,-1.94859 -8.09966,-4.95505 -9.74148,-7.96148 -1.64182,-3.00643 -4.15928,-5.73449 -3.61201,-13.30623 0.54728,-7.571746 3.01001,-11.468986 4.87074,-13.250556 1.86073,-1.7816 4.5971,-2.56105 6.40311,-2.17131 1.806,0.38971 2.95528,1.44755 3.3931,2.22697 0.43781,0.77944 1.14928,2.22699 1.53237,2.50538 z"
style="fill:#f06767;fill-opacity:1;stroke:none;stroke-width:0.309114px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1014"
d="m 126.2091,135.55481 c 0,0 13.13457,-7.6831 19.26404,-19.82016 6.12948,-12.13707 8.7564,-19.931516 7.99021,-31.845916 -0.76619,-11.91436 -2.62692,-6.01286 -0.54728,-11.13493 2.07964,-5.12208 -1.31345,-15.25486 0.10943,-15.922952 1.42292,-0.668097 4.48765,4.453972 5.3633,8.573902 0.87562,4.11992 4.70654,16.81372 1.64181,32.9594 -3.06472,16.145636 -10.28872,22.603906 -15.10475,26.946546 -4.81603,4.34261 -15.7615,11.91437 -17.40332,11.80302 -1.64183,-0.11148 -1.86073,-0.11148 -1.31348,-1.55891 z"
style="fill:#3f62eb;fill-opacity:1;stroke:none;stroke-width:0.309114px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1016"
d="m 102.56686,112.17145 c 0,0 7.00512,1.5589 12.36838,-1.78156 5.3633,-3.3405 9.5226,-8.79661 10.17931,-10.912246 0.65672,-2.11564 0.76619,1.892936 1.53236,-0.11125 0.76618,-2.00432 3.83093,-6.01288 2.29855,-15.36624 -1.53237,-9.35333 -1.64182,-9.79875 -4.48762,-14.80947 -2.84586,-5.01072 -9.63207,-11.914367 -13.79134,-14.141356 -4.15927,-2.226986 -9.96037,-5.567464 -17.950604,-5.344766 -7.9902,0.222699 -5.36328,-0.334048 -13.02512,1.002146 -7.66184,1.336187 -14.55749,5.010717 -16.85604,7.015006 -2.29855,2.004286 -8.42802,7.46041 -10.61712,10.46684 -2.1891,3.00643 -8.20911,9.79874 -11.3833,19.26344 -3.17419,9.46469 -4.48765,10.46683 -4.70656,16.702396 -0.21891,6.23557 0.54727,7.90578 -0.76618,8.46256 -1.31346,0.55673 -1.09455,-4.67668 -0.9851,-8.35123 0.10946,-3.6745 0.10946,-13.250556 4.92547,-23.940086 4.81601,-10.68954 13.35349,-19.59749 18.1695,-22.826621 4.81601,-3.229128 10.39821,-7.571752 19.04514,-9.687392 8.64693,-2.115635 17.29386,-1.781586 22.65716,-0.779443 5.363304,1.002145 13.681874,4.453974 18.169504,7.905802 4.48766,3.45183 9.41314,7.683104 13.13459,14.586764 3.72146,6.90365 4.70654,15.70025 4.59709,19.37477 -0.10943,3.67455 -1.97019,12.137106 -4.81601,16.257016 -2.84583,4.11993 -6.67674,9.0193 -10.61711,10.80089 -3.94039,1.78157 -8.75642,2.33833 -12.69675,0.55674 -3.94039,-1.78161 -3.72147,-2.44969 -4.3782,-4.34263 z"
style="fill:#fce102;fill-opacity:1;stroke:none;stroke-width:0.309114px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1018"
d="m 102.56686,112.17145 c 0,0 5.54674,1.56707 10.22918,-0.55878 4.68248,-2.12587 6.4239,-4.17301 7.54615,-5.3934 1.12223,-1.22042 3.25064,-3.50376 3.83112,-4.56669 0.58046,-1.06292 1.19962,-1.692816 1.19962,-2.086486 0,-0.39369 -1.08353,-1.33853 -0.0386,-3.62186 1.04482,-2.28333 1.97361,-7.51928 1.50922,-9.88134 -0.46438,-2.36207 -1.54792,-15.47161 -10.83547,-24.2113 -9.28753,-8.739679 -18.497714,-9.763243 -18.497714,-9.763243 0,0 14.705314,4.645415 21.593574,18.739133 6.88828,14.09374 4.64376,23.14835 0.23218,28.65986 -4.41158,5.511506 -10.68068,6.141406 -12.38342,4.724146 -1.70268,-1.41725 -5.2113,6.47164 -4.38574,7.95996 z"
style="fill:#5ed938;fill-opacity:1;stroke:none;stroke-width:0.309114px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1020"
d="m 84.662496,61.615394 c 0,0 -9.90672,-0.0787 -17.41416,4.88161 -7.50743,4.96037 -11.14506,8.9759 -13.23476,12.12533 -2.08969,3.14945 -4.48898,7.47991 -4.17939,7.40118 0.30958,-0.0788 9.05536,-12.2828 15.86623,-15.43223 6.81087,-3.14944 13.62174,-7.55865 22.98669,-4.33049 9.36495,3.22819 13.776544,7.71612 15.788844,6.61383 2.0123,-1.1023 2.3993,-2.44082 1.23835,-3.9368 -1.16094,-1.49598 -9.364964,-8.81842 -21.051804,-7.32243 z"
style="fill:#8fdbe9;fill-opacity:1;stroke:none;stroke-width:0.309114px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1022"
d="m 69.839846,124.53124 c 0.65673,0.33404 5.58219,8.90795 9.52257,10.35548 3.94037,1.44755 6.56728,1.55888 7.0051,2.227 0.43782,0.66809 0.32837,4.00858 3.83092,5.45614 3.50256,1.44753 19.920794,6.01284 24.627334,6.45824 4.70658,0.4454 0.76621,3.22911 3.61202,4.23125 2.84584,1.00217 14.99532,1.5589 17.95061,0.33405 2.95527,-1.22483 2.95527,1.1135 -2.1891,3.1178 -5.14437,2.00427 -18.71677,6.79231 -27.58261,5.67882 -8.865864,-1.1135 -19.373534,-3.00644 -25.284094,-7.23772 -5.91056,-4.23126 -8.42802,-6.1242 -6.23892,-6.45824 2.18909,-0.33407 7.9902,5.9015 10.50766,6.56959 2.51746,0.66809 4.5971,0.66809 3.06473,-0.8908 -1.53236,-1.55888 -11.49275,-8.46253 -12.47784,-8.1285 -0.9851,0.33405 -3.06474,0.55674 -4.81602,-1.78159 -1.75127,-2.33832 -10.94548,-15.25485 -10.39821,-22.1585 0.54728,-6.90365 2.29856,-5.12208 4.3782,-2.56103 2.07964,2.56103 4.48765,4.78801 4.48765,4.78801 z"
style="fill:#fd7aff;fill-opacity:1;stroke:none;stroke-width:0.309114px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

35
assets/tags/deepin.svg Normal file
View File

@@ -0,0 +1,35 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path d="m17,129c0,-61.30371 49.69629,-111 111,-111c61.30371,0 111,49.69629 111,111c0,61.30371 -49.69629,111 -111,111c-61.30371,0 -111,-49.69629 -111,-111z" id="deepin-launcher-b"/>
<filter filterUnits="objectBoundingBox" y="-6.3%" x="-8.1%" height="116.2%" width="116.2%" id="deepin-launcher-a">
<feGaussianBlur stdDeviation="5"/>
</filter>
<path d="m172.29023,47.20405c-13.1683,-7.14564 -28.25541,-11.20405 -44.29023,-11.20405c-3.85759,0 -7.66034,0.23489 -11.39471,0.69114c-13.8375,19.06416 -21.01047,50.16415 -20.43985,69.1948c0,0 -0.90954,28.19993 18.37748,38.20231c0,0 17.33291,7.61701 29.14502,-17.01295c0,0 8.11834,-13.89983 4.81381,-45.31395c0,0 3.16155,0.55793 3.51901,1.05927c0.35746,0.49728 6.98241,24.18523 2.32747,44.13339c0,0 -4.28556,23.44132 -25.35592,28.80234c-21.06242,5.36102 -37.47784,-7.60893 -48.62269,-19.82688c-8.29983,-9.09853 -20.62176,-33.53254 -26.69028,-62.84231c-11.72709,15.56323 -18.67934,34.92741 -18.67934,55.91495c0,14.49236 3.31502,28.21045 9.22816,40.43737c20.53097,6.06549 72.70224,17.88328 105.89216,-6.44123c21.96964,-16.10443 19.13893,-41.89451 19.13893,-41.89451c-0.36372,-19.61754 -12.30727,-39.10374 -12.30727,-39.10374c46.44186,42.01354 18.99265,88.68931 18.99265,88.68931c-12.77926,26.10154 -39.04908,42.79526 -62.02961,50.25104c4.59324,0.69782 9.29685,1.05965 14.08498,1.05965c51.36266,0 93,-41.63639 93,-92.99789c0,-21.72407 -7.44877,-41.70884 -19.93152,-57.53933c-4.54487,0.31322 -9.825,0.49664 -15.30337,0.31684c0,0 -24.70889,1.15387 -38.56418,8.0646c0,0 -10.33212,4.60716 -20.65999,12.41858c0,0 -2.19192,1.09575 -2.08593,5.12183c0.09327,4.01362 -2.10288,14.71385 -9.42058,20.98539c0,0 -5.22329,2.68544 -2.61165,6.52889c0,0 3.52742,5.12183 8.49633,-0.25734c0,0 12.81233,-13.18228 13.59668,-34.42501c0,0 4.05314,-3.4616 10.45506,-4.48264c0,0 1.17863,31.86824 -14.63539,47.86878c0,0 -15.04241,16.38239 -19.87565,-7.93593c0,0 -5.88469,-23.16861 24.45027,-50.68289c0,0 20.448,-20.42091 32.46751,-29.98388c1.66104,0.69233 3.29908,1.42749 4.91265,2.20406l-0.00001,-0.00001zm-44.29023,183.79595c-56.33322,0 -102,-45.66583 -102,-101.99789c0,-56.33281 45.66752,-102.00211 102,-102.00211c56.33248,0 102,45.6693 102,102.00211c0,56.33206 -45.66678,101.99789 -102,101.99789z" id="deepin-launcher-f"/>
<filter filterUnits="objectBoundingBox" y="-.5%" x="-.5%" height="102%" width="101%" id="deepin-launcher-e"/>
<filter filterUnits="objectBoundingBox" y="-.5%" x="-.5%" height="102%" width="101%" id="deepin-launcher-g"/>
<linearGradient y2="97.178%" y1="4.64%" x2="50%" x1="50%" id="deepin-launcher-c">
<stop stop-color="#00ABFF" offset="0%"/>
<stop stop-color="#0163FA" offset="100%"/>
</linearGradient>
<linearGradient y2="100%" y1="0%" x2="50%" x1="50%" id="deepin-launcher-d">
<stop stop-color="#32BBFF" offset="0%"/>
<stop stop-color="#004FC5" offset="100%"/>
</linearGradient>
</defs>
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="514" width="514" y="-1" x="-1"/>
</g>
<g>
<title>Layer 1</title>
<g stroke="null" id="svg_1" fill-rule="evenodd" fill="none">
<use x="0" y="0" transform="matrix(2.15315318107605,0,0,2.15315318107605,-19.603604078292847,-20.756757259368896) " stroke="null" id="svg_2" xlink:href="#deepin-launcher-b" filter="url(#deepin-launcher-a)" fill="#000"/>
<use x="0" y="0" transform="matrix(2.15315318107605,0,0,2.15315318107605,-19.603604078292847,-20.756757259368896) " stroke="url(#deepin-launcher-d)" id="svg_3" xlink:href="#deepin-launcher-b" stroke-width="2" fill="url(#deepin-launcher-c)"/>
<g stroke="null" id="svg_4" fill-rule="nonzero">
<use x="0" y="0" transform="matrix(2.15315318107605,0,0,2.15315318107605,-19.603604078292847,-20.756757259368896) " stroke="null" id="svg_5" xlink:href="#deepin-launcher-f" filter="url(#deepin-launcher-e)" fill="#000"/>
<use x="0" y="0" transform="matrix(2.15315318107605,0,0,2.15315318107605,-19.603604078292847,-20.756757259368896) " stroke="null" id="svg_6" xlink:href="#deepin-launcher-f" fill="#F0F9FF"/>
<use x="0" y="0" transform="matrix(2.15315318107605,0,0,2.15315318107605,-19.603604078292847,-20.756757259368896) " stroke="null" id="svg_7" xlink:href="#deepin-launcher-f" filter="url(#deepin-launcher-g)" fill="#000"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
assets/tags/dtk-small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

1
assets/tags/dwine5.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="305" height="304" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" overflow="hidden"><defs><clipPath id="clip0"><path d="M688 378 993 378 993 682 688 682Z" fill-rule="evenodd" clip-rule="evenodd"/></clipPath><clipPath id="clip1"><path d="M751.382 385.273 984.326 440.078 929.522 673.022 696.578 618.218Z" fill-rule="evenodd" clip-rule="evenodd"/></clipPath><clipPath id="clip2"><path d="M751.382 385.273 984.326 440.078 929.522 673.022 696.578 618.218Z" fill-rule="evenodd" clip-rule="evenodd"/></clipPath><clipPath id="clip3"><path d="M751.382 385.273 984.326 440.078 929.522 673.022 696.578 618.218Z" fill-rule="evenodd" clip-rule="evenodd"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-688 -378)"><path d="M689 529.5C689 445.829 756.829 378 840.5 378 924.171 378 992 445.829 992 529.5 992 613.171 924.171 681 840.5 681 756.829 681 689 613.171 689 529.5Z" fill="#A20000" fill-rule="evenodd"/><g clip-path="url(#clip1)"><g clip-path="url(#clip2)"><g clip-path="url(#clip3)"><path d="M812.478 604.512 826.339 545.597C790.456 531.483 772.809 490.952 786.924 455.069 792.525 440.83 802.648 428.825 815.737 420.901L910.837 443.275C931.459 475.856 921.763 518.985 889.182 539.607 876.253 547.79 860.994 551.499 845.751 550.164L831.89 609.079 851.302 613.646C856.662 614.908 859.986 620.276 858.724 625.636 857.463 630.996 852.095 634.32 846.735 633.058L788.499 619.357C783.138 618.096 779.815 612.728 781.076 607.368 782.338 602.007 787.705 598.684 793.066 599.945L812.478 604.512ZM803.34 469.2 900.4 492.035C901.355 477.612 900.407 467.146 897.544 460.634L819.896 442.366C814.431 446.918 808.915 455.864 803.34 469.2Z" fill="#FFFFFF"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

23
assets/tags/logo_icon.svg Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>logo icon</title>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#00F1FF" offset="0%"></stop>
<stop stop-color="#008AFF" offset="100%"></stop>
</linearGradient>
</defs>
<g id="light" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="1" transform="translate(-460.000000, -159.000000)">
<g id="tittlebar" transform="translate(450.000000, 150.000000)">
<g id="logo-icon" transform="translate(10.000000, 9.000000)">
<g>
<rect id="矩形" fill="url(#linearGradient-1)" x="0" y="0" width="32" height="32" rx="8"></rect>
<path d="M23,25 L23,24.0002278 C21.6666667,24.0002278 21,24.0002278 21,24.0002278 L21,20 L22,20 L22,19 L21,19 L21,17 L20,17.5 L20,19 L19,19 L19,20 L20,20 L20,24.0002278 C20,24.0002278 20,24.0002278 20,24.0002278 C20,24.5523868 20.4476132,25 20.9997722,25 C20.9998481,25 20.9999241,25 21,25 L23,25 Z M24.0210414,23.9789586 L24.0210414,22.2215556 L24.0210414,22.2215556 L24.6565969,21.6226667 L27.2207674,25 L29.0052118,25 L25.6099302,20.7304444 L28.4678057,18.0074755 L26.6711391,18.0074755 L24.0210414,20.6204444 L24,15.5 L23,15 L23,25 C23.5639056,25 24.0210414,24.5428642 24.0210414,23.9789586 Z" id="tk" fill="#FFFFFF" fill-rule="nonzero"></path>
<path d="M10.275,24 C13.175,24 15.35,23.2 16.85,21.6 C18.275,20.075 19,17.825 19,15 C19,12.15 18.275,10.05 16.85,8.55 C15.35,6.95 13.175,6 10.275,6 L4,6 L4,24 L10.275,24 Z M9.725,21 L7,21 L7,9 L9.725,9 C11.95,9 13.575,9.15 14.6,10.175 C15.6,11.175 16,12.75 16,15 C16,17.2 15.6,18.9 14.6,19.95 C13.575,20.975 11.95,21 9.725,21 Z" id="D" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/tags/ubuntu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<defs>
<linearGradient id="uos-托盘1-a" x1="15.968%" x2="100%" y1="14.224%" y2="60.554%">
<stop offset="0%" stop-color="#0071FF"/>
<stop offset="48.72%" stop-color="#00E8FC"/>
<stop offset="100%" stop-color="#00A2FF"/>
</linearGradient>
</defs>
<g fill="none" transform="translate(1 1)">
<circle cx="9" cy="9" r="9" fill="url(#uos-托盘1-a)"/>
<path fill="#FFF" d="M13,4.5 C13.5128358,4.5 13.9355072,4.88604019 13.9932723,5.38337887 L14,5.5 L14,9.5 C14,12.5 12,14.5 9,14.5 C6,14.5 4,12.5 4.0043492,9.70016408 L4,9.5 L4,5.5 C4,4.94771525 4.44771525,4.5 5,4.5 C5.51283584,4.5 5.93550716,4.88604019 5.99327227,5.38337887 L6,5.5 L6,9.5 C6,11.5 7.5,12.5 9,12.5 C10.5,12.5 12,11.5 12,9.5 L12,5.5 C12,4.94771525 12.4477153,4.5 13,4.5 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 873 B

BIN
assets/tags/uos-small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

13
assets/tags/uos.svg Normal file
View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<defs>
<linearGradient id="uos-托盘1-a" x1="15.968%" x2="100%" y1="14.224%" y2="60.554%">
<stop offset="0%" stop-color="#0071FF"/>
<stop offset="48.72%" stop-color="#00E8FC"/>
<stop offset="100%" stop-color="#00A2FF"/>
</linearGradient>
</defs>
<g fill="none" transform="translate(1 1)">
<circle cx="9" cy="9" r="9" fill="url(#uos-托盘1-a)"/>
<path fill="#FFF" d="M13,4.5 C13.5128358,4.5 13.9355072,4.88604019 13.9932723,5.38337887 L14,5.5 L14,9.5 C14,12.5 12,14.5 9,14.5 C6,14.5 4,12.5 4.0043492,9.70016408 L4,9.5 L4,5.5 C4,4.94771525 4.44771525,4.5 5,4.5 C5.51283584,4.5 5.93550716,4.88604019 5.99327227,5.38337887 L6,5.5 L6,9.5 C6,11.5 7.5,12.5 9,12.5 C10.5,12.5 12,11.5 12,9.5 L12,5.5 C12,4.94771525 12.4477153,4.5 13,4.5 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 873 B

View File

@@ -1,200 +0,0 @@
#include "downloadlist.h"
#include "ui_downloadlist.h"
#include <QDebug>
#include <QIcon>
#include <QPixmap>
#include <QtConcurrent>
#include <QProcess>
#include <QTextBrowser>
bool downloadlist::isInstall=false;
downloadlist::downloadlist(QWidget *parent) :
QWidget(parent),
ui(new Ui::downloadlist)
{
ui->setupUi(this);
ui->pushButton_install->setEnabled(false);
ui->progressBar->setValue(0);
ui->label_filename->hide();
ui->pushButton_install->hide();
ui->pushButton_3->hide();
ui->widget_spinner->start();
ui->widget_spinner->hide();
action_dpkg->setText("dpkg");
action_gdebi->setText("gdebi");
connect(action_dpkg,&QAction::triggered,[=](){downloadlist::install(1);});
connect(action_gdebi,&QAction::triggered,[=](){downloadlist::install(0);});
menu_install->addAction(action_gdebi);
//ssinstall命令存在时再加入该选项
QFile ssinstall("/bin/ssinstall");
ssinstall.open(QIODevice::ReadOnly);
if(ssinstall.isOpen()){
menu_install->addAction(action_dpkg);
}
}
downloadlist::~downloadlist()
{
delete ui;
}
void downloadlist::setValue(long long value)
{
ui->progressBar->setValue(int(value));
ui->label_2->setText(QString::number(double(value)/100)+"% ("+speed+")");
if(ui->label_2->text().left(4)=="100%"){
ui->label_2->setText("已完成,等待安装");
}
}
void downloadlist::setMax(long long max)
{
ui->progressBar->setMaximum(int(max));
}
void downloadlist::setName(QString name)
{
ui->label->setText(name);
}
QString downloadlist::getName()
{
return ui->label_filename->text();
}
void downloadlist::readyInstall()
{
if(ui->progressBar->value()!= ui->progressBar->maximum() && !close){
ui->progressBar->hide();
ui->pushButton_install->show();
ui->pushButton_2->hide();
system("notify-send \""+ui->label->text().toUtf8()+"下载失败\"" +" --icon=/tmp/spark-store/icon_"+QString::number(num).toUtf8()+".png");
ui->label_2->setText("下载失败,可能是网络错误");
ui->pushButton_install->setEnabled(false);
return;
}
if(!close){
ui->progressBar->hide();
ui->pushButton_install->setEnabled(true);
ui->pushButton_install->show();
ui->pushButton_2->hide();
system("notify-send \""+ui->label->text().toUtf8()+"下载完成,等待安装\"" +" --icon=/tmp/spark-store/icon_"+QString::number(num).toUtf8()+".png");
}
}
void downloadlist::setFileName(QString fileName)
{
ui->label_filename->setText(fileName);
}
void downloadlist::seticon(const QPixmap icon)
{
ui->label_3->setPixmap(icon);
}
void downloadlist::closeDownload()
{
on_pushButton_2_clicked();
}
void downloadlist::setSpeed(QString s)
{
speed=s;
}
void downloadlist::install(int t)
{
if(!isInstall){
isInstall=true;
ui->pushButton_install->hide();
ui->widget_spinner->show();
qDebug()<<"/tmp/spark-store/"+ui->label_filename->text().toUtf8();
ui->label_2->setText("正在安装,请稍候");
QtConcurrent::run([=](){
QProcess installer;
if(reinstall){
if(t==0){
installer.start("pkexec gdebi -n /tmp/spark-store/"+ui->label_filename->text().toUtf8());
}else {
installer.start("pkexec ssinstall /tmp/spark-store/"+ui->label_filename->text().toUtf8());
}
}else {
if(t==0){
installer.start("pkexec gdebi -n /tmp/spark-store/"+ui->label_filename->text().toUtf8());
}else {
installer.start("pkexec ssinstall /tmp/spark-store/"+ui->label_filename->text().toUtf8());
}
}
installer.waitForFinished();
out=installer.readAllStandardOutput();
QStringList everyOut=out.split("\n");
bool haveError=false;
bool notRoot=false;
for (int i=0;i<everyOut.size();i++) {
qDebug()<<everyOut[i].left(2);
if(everyOut[i].left(2)=="E:"){
haveError=true;
}
if(everyOut[i].right(14)=="Not authorized"){
notRoot=true;
}
}
QProcess isInstall;
isInstall.start("dpkg -s "+pkgName);
isInstall.waitForFinished();
int error=QString::fromStdString(isInstall.readAllStandardError().toStdString()).length();
if(error==0){
ui->pushButton_install->hide();
ui->label_2->setText("安装完成");
ui->pushButton_3->show();
}else {
ui->pushButton_install->show();
ui->pushButton_install->setText("重装");
ui->label_2->setText("安装出现错误,可重新安装");
ui->pushButton_3->show();
}
if(notRoot){
ui->label_2->setText("安装被终止,可重新安装");
ui->pushButton_install->show();
ui->pushButton_3->hide();
}
ui->widget_spinner->hide();
downloadlist::isInstall=false;
});
qDebug()<<ui->label_filename->text().toUtf8();
}
}
void downloadlist::on_pushButton_install_clicked()
{
//弹出菜单
menu_install->exec(cursor().pos());
}
void downloadlist::on_pushButton_2_clicked()
{
ui->label_2->setText("已取消下载");
ui->pushButton_2->setEnabled(false);
ui->progressBar->hide();
close=true;
}
void downloadlist::on_pushButton_3_clicked()
{
output_w.layout()->addWidget(textbrowser);
textbrowser->setLineWidth(0);
textbrowser->setText(out);
output_w.layout()->setMargin(20);
output_w.setTitle(ui->label->text());
output_w.setMinimumHeight(600);
output_w.setAttribute(Qt::WA_TranslucentBackground);
output_w.show();
}

View File

@@ -1,47 +0,0 @@
#include "image_show.h"
#include <QHBoxLayout>
#include <QDebug>
#include <QPainter>
#include <DDialog>
#include <DBlurEffectWidget>
#include <DWidgetUtil>
#include <DApplication>
#include <QDesktopWidget>
DWIDGET_USE_NAMESPACE
image_show::image_show(QWidget *parent) : QWidget(parent)
{
QHBoxLayout *layout=new QHBoxLayout;
layout->addWidget(m_label);
setLayout(layout);
m_label->setText("layout");
}
void image_show::setImage(QPixmap image)
{
QImage screen0;
screen0=image.toImage();
// QPainter painter(&screen0);
QImage re_screen1;
QImage re_screen0=screen0.scaled(QSize(400,300),Qt::KeepAspectRatio,Qt::SmoothTransformation);
desktop_w=DApplication::desktop()->width();
desktop_h=DApplication::desktop()->height();
if(screen0.width()>(desktop_w-20) || screen0.height()>(desktop_h-20)){
re_screen1=screen0.scaled(QSize(desktop_w-20,desktop_h-20),Qt::KeepAspectRatio,Qt::SmoothTransformation);
m_image=QPixmap::fromImage(re_screen1);
}else {
m_image=image;
}
m_label->setPixmap(QPixmap::fromImage(re_screen0));
}
void image_show::mousePressEvent(QMouseEvent *)
{
m_dialog->setimage(m_image);
m_dialog->showFullScreen();
m_dialog->setFixedSize(desktop_w,desktop_h);
m_dialog->move(0,0);/*
moveToCenter(m_dialog);*/
}

View File

@@ -1,56 +0,0 @@
#include <DApplication>
#include <DWidgetUtil> //Dtk::Widget::moveToCenter(&w); 要调用它就得引用DWidgetUtil
#include <QDesktopWidget>
#include <widget.h>
DWIDGET_USE_NAMESPACE
int main(int argc, char *argv[])
{
DApplication::loadDXcbPlugin(); //让bar处在标题栏中
DApplication a(argc, argv);
a.setAttribute(Qt::AA_UseHighDpiPixmaps);
a.loadTranslator();
a.setOrganizationName("spark-union");
a.setApplicationVersion(DApplication::buildVersion("1.0-beta7"));
a.setApplicationAcknowledgementPage("https://gitee.com/deepin-community-store/spark-store");
a.setProductIcon(QIcon::fromTheme("spark-store")); //设置Logo
a.setProductName("Spark应用商店");
a.setApplicationDescription("社区驱动的一款为第三方应用商店,为改变而生");
a.setApplicationName("Spark应用商店"); //只有在这儿修改窗口标题才有效
Widget w;
QDesktopWidget *s=DApplication::desktop();
int d_w=s->width();
int d_h=s->height();
if(d_w<=1366){
w.setMinimumWidth(925);
w.resize(925,650);
}else if(d_w<=1920){
w.setMinimumWidth(1180);
w.resize(1180,760);
}else {
w.setMinimumWidth(1180);
w.resize(1180,760);
}
if(d_h<=768){
w.setMinimumHeight(650);
w.resize(925,650);
}else if(d_h<=1080){
w.setMinimumHeight(760);
w.resize(1180,760);
}else {
w.setMinimumHeight(760);
w.resize(1180,760);
}
// w.resize(925,650);
w.show();
QString arg1=argv[1];
if(arg1.left(6)=="spk://"){
w.openUrl(QUrl(argv[1]));
}
//让打开时界面显示在正中
Dtk::Widget::moveToCenter(&w);
return a.exec();
}

9
spark-store-project.pro Normal file
View File

@@ -0,0 +1,9 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += \
third-party/QtNetworkService \
src/spark-store.pro
spark-store.depends = third-party/QtNetworkService

View File

@@ -1,48 +0,0 @@
#-------------------------------------------------
#
# Project created by QtCreator 2019-06-30T12:53:03
#
#-------------------------------------------------
QT += core gui webkitwidgets network concurrent
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = spark-store
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += main.cpp\
widget.cpp \
downloadlist.cpp \
image_show.cpp \
big_image.cpp
HEADERS += \
widget.h \
downloadlist.h \
image_show.h \
big_image.h
CONFIG += link_pkgconfig
PKGCONFIG += dtkwidget
CONFIG += c++11
FORMS += \
widget.ui \
downloadlist.ui
RESOURCES += \
icons.qrc

102
src/appitem.cpp Normal file
View File

@@ -0,0 +1,102 @@
#include "appitem.h"
#include "ui_appitem.h"
#include <QtConcurrent>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QPainter>
#include <QGraphicsDropShadowEffect>
AppItem::AppItem(QWidget *parent) :
QWidget(parent),
ui(new Ui::AppItem)
{
ui->setupUi(this);
// auto shadow = new QGraphicsDropShadowEffect();
// shadow->setXOffset(0);
// shadow->setYOffset(1);
// shadow->setBlurRadius(2);
// shadow->setColor(QColor::fromRgba(qRgba(0, 0, 0, 180)));
// ui->container->setGraphicsEffect(shadow);
}
AppItem::~AppItem()
{
delete ui;
}
void AppItem::setTitle(QString title)
{
m_title = title;
ui->lbl_title->setText(title);
}
void AppItem::setDescription(QString description)
{
m_description = description;
QString elidedText = ui->lbl_desc->fontMetrics().elidedText(
description, Qt::ElideRight,
ui->lbl_desc->width(), Qt::TextShowMnemonic);
ui->lbl_desc->setText(elidedText);
ui->lbl_desc->setAlignment(Qt::AlignTop);
}
void AppItem::setIcon(QString icon)
{
m_icon = icon;
if(!icon.isEmpty())
{
downloadIcon(icon);
}
}
void AppItem::setUrl(QString url)
{
m_url = url;
}
void AppItem::mousePressEvent(QMouseEvent *event)
{
Q_UNUSED(event)
emit clicked(QUrl(m_url));
}
/**
* @brief 下载图标
* @param icon
*/
void AppItem::downloadIcon(QString icon)
{
QtConcurrent::run([=]()
{
auto reqManager = new QNetworkAccessManager();
QUrl url(icon);
QNetworkReply *reply = reqManager->get(QNetworkRequest(url));
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(reply, &QNetworkReply::finished, this, [=] () { emit finished(); });
loop.exec();
reqManager->deleteLater();
QPixmap pixmap;
pixmap.loadFromData(reply->readAll());
pixmap = pixmap.scaled(78, 78, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
if (reply->error() == QNetworkReply::NoError)
{
QMetaObject::invokeMethod(this, "loadIcon", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap));
}
else
{
qDebug() << reply->errorString();
}
});
}
void AppItem::loadIcon(QPixmap pic)
{
ui->lbl_icon->setPixmap(pic);
}

45
src/appitem.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef APPITEM_H
#define APPITEM_H
#include <QWidget>
#include <QUrl>
namespace Ui {
class AppItem;
}
class AppItem : public QWidget
{
Q_OBJECT
public:
explicit AppItem(QWidget *parent = nullptr);
~AppItem() override;
void setTitle(QString title);
void setDescription(QString description);
void setIcon(QString icon);
void setUrl(QString url);
protected:
void mousePressEvent(QMouseEvent *event) override;
private:
Ui::AppItem *ui;
QString m_title;
QString m_description;
QString m_icon;
QString m_url;
public slots:
void downloadIcon(QString icon);
void loadIcon(QPixmap pic);
signals:
void clicked(QUrl url);
void finished();
};
#endif // APPITEM_H

160
src/appitem.ui Normal file
View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AppItem</class>
<widget class="QWidget" name="AppItem">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>333</width>
<height>133</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">QWidget#AppItem {
width: 300px;
height: 100px;
margin: 15px;
color: #6d6d6d;
border-radius: 18px;
background-color: width: 300px;
height: 100px;
margin: 15px;
color: #6d6d6d;
border-radius: 18px;
background-color: #F4F4F6;
}
QWidget#container {
background-color: #F4F4F6;
}
QLabel#lbl_icon {
background: transparent;
border-radius: 10px;
}
QLabel#lbl_title {
text-align: left;
white-space: nowrap;
padding-right: 10px;
font-size: 19px;
}
QLabel#lbl_desc {
text-align: left;
font-weight: lighter;
white-space: nowrap;
font-size: 12px;
color: grey;
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="container" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lbl_icon">
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">width: 78px;
height: 70px;
padding: 10px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lbl_title">
<property name="minimumSize">
<size>
<width>200</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>50</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbl_desc">
<property name="minimumSize">
<size>
<width>200</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>50</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,22 +1,30 @@
#include "big_image.h"
#include <QHBoxLayout>
#include <QtConcurrent>
big_image::big_image(DBlurEffectWidget *parent) : DBlurEffectWidget(parent)
big_image::big_image(DBlurEffectWidget *parent) :
DBlurEffectWidget(parent),
m_image(new QLabel)
{
// m_image->setParent(this);
QHBoxLayout *layout=new QHBoxLayout;
setLayout(layout);
layout->addWidget(m_image);
layout->setMargin(0);
m_image->setAlignment(Qt::AlignCenter);
// m_image->setMaximumSize(1360,768);
setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint);//设置图片对话框总在最前
setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint); // 设置图片对话框总在最前
setRadius(0);
setMaskAlpha(60);
setMaskColor(QColor("#000000"));
QHBoxLayout *layout = new QHBoxLayout;
setLayout(layout);
layout->addWidget(m_image);
layout->setMargin(0);
// m_image->setParent(this);
// m_image->setMaximumSize(1360,768);
m_image->setAlignment(Qt::AlignCenter);
}
void big_image::setimage(QPixmap image)
{
m_image->setPixmap(image);
}
void big_image::mousePressEvent(QMouseEvent *)
@@ -25,11 +33,6 @@ void big_image::mousePressEvent(QMouseEvent *)
m_image->clear();
}
void big_image::setimage(QPixmap image)
{
m_image->setPixmap(image);
}
void big_image::focusOutEvent(QFocusEvent *)
{
hide();

View File

@@ -1,25 +1,27 @@
#ifndef BIG_IMAGE_H
#define BIG_IMAGE_H
#include <QWidget>
#include <DBlurEffectWidget>
#include <QMouseEvent>
#include <QLabel>
#include <DBlurEffectWidget>
DWIDGET_USE_NAMESPACE
class big_image : public DBlurEffectWidget
{
Q_OBJECT
public:
explicit big_image(DBlurEffectWidget *parent = nullptr);
void mousePressEvent(QMouseEvent *event);
QLabel *m_image=new QLabel;
QLabel *m_image;
void setimage(QPixmap);
void focusOutEvent(QFocusEvent *event);
signals:
public slots:
protected:
void mousePressEvent(QMouseEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
};
#endif // BIG_IMAGE_H

255
src/downloadlist.cpp Normal file
View File

@@ -0,0 +1,255 @@
#include "downloadlist.h"
#include "ui_downloadlist.h"
#include <QtConcurrent>
#include <QProcess>
#include "widget.h"
bool downloadlist::isInstall = false;
downloadlist::downloadlist(QWidget *parent) :
QWidget(parent),
reinstall(false),
close(false),
ui(new Ui::downloadlist),
menu_install(new QMenu),
action_dpkg(new QAction),
action_deepin(new QAction),
action_gdebi(new QAction),
output_w(new DDialog),
textbrowser(new QTextBrowser)
{
ui->setupUi(this);
ui->pushButton_install->setEnabled(false);
ui->progressBar->setValue(0);
ui->label_filename->hide();
ui->pushButton_install->hide();
ui->pushButton_3->hide();
ui->widget_spinner->start();
ui->widget_spinner->hide();
action_dpkg->setText(tr("Spark Store App Installer"));
action_deepin->setText(tr("deepin deb installer"));
action_gdebi->setText(tr("gdebi"));
connect(action_dpkg,&QAction::triggered,[=](){downloadlist::install(0);});
connect(action_deepin,&QAction::triggered,[=](){downloadlist::install(1);});
connect(action_gdebi,&QAction::triggered,[=](){downloadlist::install(2);});
// ssinstall 命令存在时再加入该选项
QFile ssinstall("/usr/local/bin/ssinstall");
ssinstall.open(QIODevice::ReadOnly);
if(ssinstall.isOpen())
{
menu_install->addAction(action_dpkg);
}
QFile deepin("/usr/bin/deepin-deb-installer");
deepin.open(QIODevice::ReadOnly);
if(deepin.isOpen())
{
menu_install->addAction(action_deepin);
}
menu_install->addAction(action_gdebi);
}
downloadlist::~downloadlist()
{
delete ui;
}
void downloadlist::setValue(qint64 value)
{
ui->progressBar->setValue(qint32(value));
ui->label_2->setText(QString::number(double(value) / 100) + "% (" + speed + ")");
if(ui->label_2->text().left(4) == "100%")
{
ui->label_2->setText(tr("Downloaded, waiting to install"));
}
}
void downloadlist::setMax(qint64 max)
{
ui->progressBar->setMaximum(qint32(max));
}
void downloadlist::setName(QString name)
{
ui->label->setText(name);
}
QString downloadlist::getName()
{
return ui->label_filename->text();
}
void downloadlist::readyInstall()
{
if(ui->progressBar->value() != ui->progressBar->maximum() && !close)
{
ui->progressBar->hide();
ui->pushButton_install->show();
ui->pushButton_2->hide();
Widget::sendNotification(tr("Failed to download %1").arg(ui->label->text()), 5000,
"/tmp/spark-store/icon_" + QString::number(num).toUtf8() + ".png");
ui->label_2->setText(tr("Download FailedCheck Your Connection"));
ui->pushButton_install->setEnabled(false);
return;
}
if(!close)
{
ui->progressBar->hide();
ui->pushButton_install->setEnabled(true);
ui->pushButton_install->show();
ui->pushButton_2->hide();
Widget::sendNotification(tr("Finished downloading %1, awaiting to install").arg(ui->label->text()), 5000,
"/tmp/spark-store/icon_" + QString::number(num).toUtf8() + ".png");
}
}
void downloadlist::setFileName(QString fileName)
{
ui->label_filename->setText(fileName);
}
void downloadlist::seticon(const QPixmap icon)
{
ui->label_3->setPixmap(icon);
}
void downloadlist::closeDownload()
{
on_pushButton_2_clicked();
}
void downloadlist::setSpeed(QString s)
{
speed = s;
}
void downloadlist::install(int t)
{
if(!isInstall)
{
isInstall = true;
ui->pushButton_install->hide();
ui->widget_spinner->show();
qDebug() << "/tmp/spark-store/" + ui->label_filename->text().toUtf8();
ui->label_2->setText(tr("Installing"));
QtConcurrent::run([=]()
{
QProcess installer;
if(!reinstall)
{
switch(t)
{
case 0:
installer.start("pkexec ssinstall /tmp/spark-store/" + ui->label_filename->text().toUtf8());
break;
case 1:
installer.start("deepin-deb-installer /tmp/spark-store/" + ui->label_filename->text().toUtf8());
break;
case 2:
installer.start("pkexec gdebi -n /tmp/spark-store/" + ui->label_filename->text().toUtf8());
break;
}
}
else
{
switch(t)
{
case 0:
installer.start("pkexec ssinstall /tmp/spark-store/" + ui->label_filename->text().toUtf8());
break;
case 1:
installer.start("deepin-deb-installer /tmp/spark-store/" + ui->label_filename->text().toUtf8());
break;
case 2:
installer.start("pkexec gdebi -n /tmp/spark-store/" + ui->label_filename->text().toUtf8());
break;
}
}
bool haveError = false;
bool notRoot = false;
installer.waitForFinished();
out = installer.readAllStandardOutput();
QStringList everyOut = out.split("\n");
for(int i=0;i<everyOut.size();i++)
{
if(everyOut[i].left(2) == "E:")
{
haveError = true;
}
if(everyOut[i].right(14) == "Not authorized")
{
notRoot = true;
}
}
QProcess isInstall;
isInstall.start("dpkg -s " + pkgName);
isInstall.waitForFinished();
int error = QString::fromStdString(isInstall.readAllStandardError().toStdString()).length();
if(error == 0)
{
ui->pushButton_install->hide();
ui->label_2->setText(tr("Finish"));
ui->pushButton_3->show();
}
else
{
ui->pushButton_install->show();
ui->pushButton_install->setText(tr("Retry"));
ui->label_2->setText(tr("Error happened in dpkg progress , you can try it again"));
ui->pushButton_3->show();
}
if(notRoot)
{
ui->label_2->setText(tr("dpkg progress had been abortedyou can retry installation"));
ui->pushButton_install->show();
ui->pushButton_3->hide();
}
ui->widget_spinner->hide();
downloadlist::isInstall = false;
});
qDebug()<<ui->label_filename->text().toUtf8();
}
}
void downloadlist::on_pushButton_install_clicked()
{
// 弹出菜单
menu_install->exec(cursor().pos());
}
void downloadlist::on_pushButton_2_clicked()
{
ui->label_2->setText(tr("Download canceled"));
ui->pushButton_2->setEnabled(false);
ui->progressBar->hide();
close = true;
}
void downloadlist::on_pushButton_3_clicked()
{
textbrowser->setLineWidth(0);
textbrowser->setText(out);
output_w->setMinimumHeight(600);
output_w->setAttribute(Qt::WA_TranslucentBackground);
output_w->setTitle(ui->label->text());
output_w->layout()->setMargin(20);
output_w->layout()->addWidget(textbrowser);
output_w->show();
}

View File

@@ -2,11 +2,14 @@
#define DOWNLOADLIST_H
#include <QWidget>
#include <DDialog>
#include <QTextBrowser>
#include <QMenu>
#include <QAction>
#include <DDialog>
DWIDGET_USE_NAMESPACE
namespace Ui {
class downloadlist;
}
@@ -18,39 +21,45 @@ class downloadlist : public QWidget
public:
explicit downloadlist(QWidget *parent = nullptr);
~downloadlist();
void setValue(long long);
void setMax(long long);
int num;
bool free;
static bool isInstall;
bool reinstall;
QString speed;
QString out;
QString pkgName;
bool close;
void setValue(qint64);
void setMax(qint64);
void setName(QString);
QString getName();
void readyInstall();
bool free;
void setFileName(QString);
void seticon(const QPixmap);
void closeDownload();
void setSpeed(QString);
int num;
bool close=false;
QString out;
DDialog output_w;
QTextBrowser *textbrowser=new QTextBrowser;
bool reinstall=false;
QString pkgName;
QMenu *menu_install=new QMenu;
QAction *action_gdebi=new QAction;
QAction *action_dpkg=new QAction;
void install(int);
private slots:
void on_pushButton_install_clicked();
// void on_pushButton_maninst_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
private:
Ui::downloadlist *ui;
static bool isInstall;
QString speed;
QMenu *menu_install;
QAction *action_dpkg;
QAction *action_deepin;
QAction *action_gdebi;
DDialog *output_w;
QTextBrowser *textbrowser;
private slots:
void on_pushButton_install_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
};
//bool downloadlist::isInstall=false;
#endif // DOWNLOADLIST_H

View File

@@ -103,7 +103,7 @@
</font>
</property>
<property name="text">
<string>名称</string>
<string>Name</string>
</property>
</widget>
</item>
@@ -186,7 +186,7 @@
</font>
</property>
<property name="text">
<string>等待开始下载</string>
<string>Waiting to download</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -237,7 +237,7 @@
</size>
</property>
<property name="text">
<string>安装</string>
<string>Install</string>
</property>
</widget>
</item>
@@ -256,7 +256,7 @@
</size>
</property>
<property name="text">
<string>取消</string>
<string>Cancel</string>
</property>
</widget>
</item>
@@ -275,7 +275,7 @@
</size>
</property>
<property name="text">
<string>详情</string>
<string>Info</string>
</property>
</widget>
</item>

277
src/downloadworker.cpp Normal file
View File

@@ -0,0 +1,277 @@
#include "downloadworker.h"
#include <QEventLoop>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QThread>
#include <QRegularExpression>
#include <QFileInfo>
#include <QDir>
DownloadWorker::DownloadWorker(QObject *parent)
{
Q_UNUSED(parent)
}
void DownloadWorker::setIdentifier(int identifier)
{
this->identifier = identifier;
}
void DownloadWorker::setParamter(const QString &url, QPair<qint64, qint64> range, QFile *file)
{
this->url = url;
this->startPos = range.first;
this->endPos = range.second;
this->file = file;
}
qint64 DownloadWorker::getReceivedPos()
{
return receivedPos;
}
void DownloadWorker::doWork()
{
mgr = new QNetworkAccessManager(this);
QNetworkRequest request;
request.setUrl(url);
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setRawHeader("Range", QString("bytes=%1-%2").arg(startPos).arg(endPos).toLocal8Bit());
reply = mgr->get(request);
qDebug() << "开始下载数据:" << QString(" %1~%2 -> writePos Start %3").arg(startPos).arg(endPos).arg(receivedPos);
connect(reply, static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError) > (&QNetworkReply::error),
[this](QNetworkReply::NetworkError error)
{
if(error != QNetworkReply::NoError)
{
qDebug() << "出错了:" << reply->errorString();
}
});
connect(reply, &QNetworkReply::finished, mgr, &QNetworkAccessManager::deleteLater);
connect(reply, &QNetworkReply::readyRead, this, &DownloadWorker::dataReady);
connect(reply, &QNetworkReply::finished, this, &DownloadWorker::slotFinish);
connect(reply, &QNetworkReply::downloadProgress, this, &DownloadWorker::handleProcess);
// connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, &DownloadWorker::doStop);
}
void DownloadWorker::doStop()
{
if (reply) {
reply->disconnect();
reply->aboutToClose();
reply->deleteLater();
reply = nullptr;
}
}
void DownloadWorker::dataReady()
{
QByteArray data = reply->readAll();
file->seek(startPos + receivedPos);
file->write(data);
receivedPos += data.size();
}
void DownloadWorker::slotFinish()
{
file->flush();
qDebug() << "数据块下载完毕:" << QString(" %1~%2 -> writePos Start %3").arg(startPos).arg(endPos).arg(receivedPos);
emit workFinished();
}
void DownloadWorker::handleProcess(qint64, qint64)
{
emit this->downloadProcess();
}
DownloadController::DownloadController(QObject *parent)
{
Q_UNUSED(parent)
domains = {
"d1.store.deepinos.org.cn",
"d2.store.deepinos.org.cn",
"d3.store.deepinos.org.cn",
"d4.store.deepinos.org.cn",
"d5.store.deepinos.org.cn"
};
this->threadNum = domains.size() > 5 ? 5 : domains.size();
}
DownloadController::~DownloadController()
{
if(workers.size() > 0)
{
for(int i = 0; i < workers.size(); i++)
{
workers.at(i)->doStop();
workers.at(i)->disconnect();
workers.at(i)->deleteLater();
}
workers.clear();
}
}
void DownloadController::setFilename(QString filename)
{
this->filename = filename;
}
void DownloadController::setThreadNum(int threadNum)
{
this->threadNum = threadNum;
}
/**
* @brief 开始下载
*/
void DownloadController::startDownload(const QString &url)
{
finish = 0;
// 下载任务等分,计算每个线程的下载数据
fileSize = getFileSize(url);
if(fileSize == 0)
{
emit errorOccur("文件大小获取失败");
return;
}
qint64 segmentSize = fileSize / threadNum;
ranges.resize(threadNum);
QVector<qint64> receivedBytes;
receivedBytes.resize(threadNum);
for(int i = 0; i < threadNum; i++)
{
ranges[i].first = i * segmentSize;
ranges[i].second = i * segmentSize + segmentSize - 1;
receivedBytes[i] = 0;
}
ranges[threadNum - 1].second = fileSize; // 余数部分加入最后一个
// 打开文件
QDir tmpdir("/tmp/spark-store");
file = new QFile;
file->setFileName(tmpdir.absoluteFilePath(filename));
if(file->exists())
{
file->remove();
}
if(!file->open(QIODevice::WriteOnly))
{
delete file;
file = nullptr;
emit errorOccur(file->errorString());
return;
}
file->resize(fileSize);
// 创建下载线程
workers.clear();
for(int i = 0; i < ranges.size(); i++)
{
qDebug() << QString("第%1个下载请求%2-%3").arg(i).arg(ranges.at(i).first).arg(ranges.at(i).second);
auto worker = new DownloadWorker(this);
auto range = ranges.at(i);
QString chunkUrl = replaceDomain(url, domains.at(i));
worker->setIdentifier(i);
worker->setParamter(chunkUrl, range, file);
workers.append(worker);
connect(worker, &DownloadWorker::downloadProcess, this, &DownloadController::handleProcess);
connect(worker, &DownloadWorker::workFinished, this, &DownloadController::chunkDownloadFinish);
worker->doWork();
}
}
/**
* @brief 停止下载
*/
void DownloadController::stopDownload()
{
for(int i = 0; i < workers.size(); i++)
{
workers.at(i)->doStop();
workers.at(i)->disconnect();
workers.at(i)->deleteLater();
}
workers.clear();
qDebug() << "文件下载路径:" << QFileInfo(file->fileName()).absoluteFilePath();
file->flush();
file->close();
delete file;
file = nullptr;
}
void DownloadController::handleProcess()
{
qint64 bytesReceived = 0;
for(int i = 0; i < workers.size(); i++)
{
bytesReceived += workers.at(i)->getReceivedPos();
}
qDebug() << QString("下载进度 %1-%2").arg(bytesReceived).arg(fileSize);
emit downloadProcess(bytesReceived, fileSize);
}
void DownloadController::chunkDownloadFinish()
{
finish++;
qDebug() << QString("已下载了%1块共%2块").arg(finish).arg(threadNum);
if(finish == threadNum)
{
stopDownload();
emit downloadFinished();
}
}
qint64 DownloadController::getFileSize(const QString& url)
{
QEventLoop event;
QNetworkAccessManager requestManager;
QNetworkRequest request;
request.setUrl(QUrl(url));
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
QNetworkReply *reply = requestManager.head(request);
connect(reply, static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError) > (&QNetworkReply::error),
[this, reply](QNetworkReply::NetworkError error)
{
if(error != QNetworkReply::NoError)
{
emit errorOccur(reply->errorString());
}
});
connect(reply, &QNetworkReply::finished, &event, &QEventLoop::quit);
event.exec();
qint64 fileSize = 0;
if(reply->rawHeader("Accept-Ranges") == QByteArrayLiteral("bytes")
&& reply->hasRawHeader(QString("Content-Length").toLocal8Bit()))
{
fileSize = reply->header(QNetworkRequest::ContentLengthHeader).toUInt();
}
qDebug() << "文件大小为:" << fileSize;
reply->deleteLater();
return fileSize;
}
QString DownloadController::replaceDomain(const QString& url, const QString domain)
{
QRegularExpression regex(R"((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9])");
if(regex.match(url).hasMatch())
{
return QString(url).replace(regex.match(url).captured(), domain);
}
return url;
}

81
src/downloadworker.h Normal file
View File

@@ -0,0 +1,81 @@
#ifndef DOWNLOADWORKER_H
#define DOWNLOADWORKER_H
#include <QObject>
#include <QList>
#include <QFile>
#include <QNetworkReply>
class DownloadWorker : public QObject
{
Q_OBJECT
public:
explicit DownloadWorker(QObject *parent = nullptr);
void setIdentifier(int identifier);
void setParamter(const QString &url, QPair<qint64, qint64> range, QFile *flle);
qint64 getReceivedPos();
public slots:
void doWork();
void doStop();
void dataReady();
void slotFinish();
void handleProcess(qint64, qint64);
private:
int identifier;
QString url;
qint64 startPos;
qint64 endPos;
qint64 receivedPos = 0;
QNetworkReply *reply;
QNetworkAccessManager *mgr;
QFile *file;
signals:
void resultReady(int identifier, QByteArray data);
void testSignals();
void workFinished();
void downloadProcess();
};
class DownloadController : public QObject
{
Q_OBJECT
public:
explicit DownloadController(QObject *parent = nullptr);
~DownloadController();
void setFilename(QString filename);
void setThreadNum(int threadNum);
void startDownload(const QString &url);
void stopDownload();
qint64 getFileSize(const QString& url);
QString replaceDomain(const QString& url, const QString domain);
private:
int threadNum;
QString filename;
qint64 fileSize;
QVector<QPair<qint64, qint64>> ranges;
QFile *file;
QList<DownloadWorker*> workers;
int finish = 0;
QVector<QString> domains;
public slots:
void handleProcess();
void chunkDownloadFinish();
signals:
void errorOccur(const QString& msg);
void downloadProcess(qint64, qint64);
void downloadFinished();
};
#endif // FILEDOWNLOADWORKER_H

223
src/flowlayout.cpp Normal file
View File

@@ -0,0 +1,223 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtWidgets>
#include "flowlayout.h"
//! [1]
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
: m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
//! [1]
//! [2]
FlowLayout::~FlowLayout()
{
QLayoutItem *item;
while ((item = takeAt(0)))
delete item;
}
//! [2]
//! [3]
void FlowLayout::addItem(QLayoutItem *item)
{
itemList.append(item);
}
//! [3]
//! [4]
int FlowLayout::horizontalSpacing() const
{
if (m_hSpace >= 0) {
return m_hSpace;
} else {
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
}
}
int FlowLayout::verticalSpacing() const
{
if (m_vSpace >= 0) {
return m_vSpace;
} else {
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}
}
//! [4]
//! [5]
int FlowLayout::count() const
{
return itemList.size();
}
QLayoutItem *FlowLayout::itemAt(int index) const
{
return itemList.value(index);
}
QLayoutItem *FlowLayout::takeAt(int index)
{
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
else
return nullptr;
}
//! [5]
//! [6]
Qt::Orientations FlowLayout::expandingDirections() const
{
return nullptr;
}
//! [6]
//! [7]
bool FlowLayout::hasHeightForWidth() const
{
return true;
}
int FlowLayout::heightForWidth(int width) const
{
int height = doLayout(QRect(0, 0, width, 0), true);
return height;
}
//! [7]
//! [8]
void FlowLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
doLayout(rect, false);
}
QSize FlowLayout::sizeHint() const
{
return minimumSize();
}
QSize FlowLayout::minimumSize() const
{
QSize size;
QLayoutItem *item;
foreach (item, itemList)
size = size.expandedTo(item->minimumSize());
size += QSize(2*margin(), 2*margin());
return size;
}
//! [8]
//! [9]
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
{
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
//! [9]
//! [10]
QLayoutItem *item;
foreach (item, itemList) {
QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
//! [10]
//! [11]
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
return y + lineHeight - rect.y() + bottom;
}
//! [11]
//! [12]
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
{
QObject *parent = this->parent();
if (!parent) {
return -1;
} else if (parent->isWidgetType()) {
QWidget *pw = static_cast<QWidget *>(parent);
return pw->style()->pixelMetric(pm, nullptr, pw);
} else {
return static_cast<QLayout *>(parent)->spacing();
}
}
//! [12]

88
src/flowlayout.h Normal file
View File

@@ -0,0 +1,88 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H
#include <QLayout>
#include <QRect>
#include <QStyle>
//! [0]
class FlowLayout : public QLayout
{
public:
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
~FlowLayout() override;
void addItem(QLayoutItem *item) override;
int horizontalSpacing() const;
int verticalSpacing() const;
Qt::Orientations expandingDirections() const override;
bool hasHeightForWidth() const override;
int heightForWidth(int) const override;
int count() const override;
QLayoutItem *itemAt(int index) const override;
QSize minimumSize() const override;
void setGeometry(const QRect &rect) override;
QSize sizeHint() const override;
QLayoutItem *takeAt(int index) override;
private:
int doLayout(const QRect &rect, bool testOnly) const;
int smartSpacing(QStyle::PixelMetric pm) const;
QList<QLayoutItem *> itemList;
int m_hSpace;
int m_vSpace;
};
//! [0]
#endif // FLOWLAYOUT_H

51
src/image_show.cpp Normal file
View File

@@ -0,0 +1,51 @@
#include "image_show.h"
#include <QHBoxLayout>
#include <QScreen> // Qt5 不再建议使用 QDesktopWidget
#include <QGuiApplication>
image_show::image_show(QWidget *parent) :
QWidget(parent),
m_dialog(new big_image),
m_label(new QLabel)
{
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(m_label);
setLayout(layout);
m_label->setText("layout");
}
void image_show::setImage(QPixmap image)
{
QImage screen0;
screen0 = image.toImage();
QImage re_screen1;
QImage re_screen0 = screen0.scaled(QSize(400, 300), Qt::KeepAspectRatio, Qt::SmoothTransformation);
// 获取主屏幕尺寸
desktop_w = QGuiApplication::primaryScreen()->geometry().width();
desktop_h = QGuiApplication::primaryScreen()->geometry().height();
if(screen0.width() > (desktop_w - 20) || screen0.height() > (desktop_h - 20))
{
re_screen1 = screen0.scaled(QSize(desktop_w - 20, desktop_h - 20), Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_image = QPixmap::fromImage(re_screen1);
}
else
{
m_image = image;
}
m_label->setPixmap(QPixmap::fromImage(re_screen0));
}
void image_show::mousePressEvent(QMouseEvent *)
{
m_dialog->setimage(m_image);
m_dialog->showFullScreen();
// 识别主屏幕尺寸并设置 widget 大小
m_dialog->setFixedSize(desktop_w, desktop_h);
m_dialog->move(0,0);
}

View File

@@ -3,30 +3,30 @@
#include <QWidget>
#include <QMouseEvent>
#include <QLabel>
#include <QPixmap>
#include <DDialog>
#include <DBlurEffectWidget>
#include <big_image.h>
DWIDGET_USE_NAMESPACE
#include "big_image.h"
class image_show : public QWidget
{
Q_OBJECT
public:
explicit image_show(QWidget *parent = nullptr);
void setImage(QPixmap);
int desktop_w;
int desktop_h;
private:
QLabel *m_label=new QLabel;
QPixmap m_image;
QLabel image;
big_image *m_dialog=new big_image;
void mousePressEvent(QMouseEvent *event);
signals:
public slots:
void setImage(QPixmap);
protected:
void mousePressEvent(QMouseEvent *event) override;
private:
big_image *m_dialog;
QLabel *m_label;
QLabel image;
QPixmap m_image;
};
#endif // IMAGE_SHOW_H

90
src/main.cpp Normal file
View File

@@ -0,0 +1,90 @@
#include <DApplication>
#include <DWidgetUtil> //Dtk::Widget::moveToCenter(&w); 要调用它就得引用DWidgetUtil
#include <QDesktopWidget>
#include <widget.h>
#include <QTranslator>
#include <DAboutDialog>
#include "appitem.h"
DWIDGET_USE_NAMESPACE
int main(int argc, char *argv[])
{
DApplication::loadDXcbPlugin(); //让bar处在标题栏中
DApplication a(argc, argv);
a.setAttribute(Qt::AA_UseHighDpiPixmaps);
a.loadTranslator();//载入翻译
/* Customized DAboutDialog (Can't work on other distro like Ubuntu...)
*
* DAboutDialog dialog;
* a.setAboutDialog(&dialog);
* dialog.setLicense(QObject::tr("We publish this program under GPL V3"));
* dialog.setVersion(DApplication::buildVersion("Version 2.0.2.5"));
* dialog.setProductIcon(QIcon::fromTheme("spark-store")); //设置Logo
* dialog.setProductName(QLabel::tr("Spark Store"));
* dialog.setDescription(
* QObject::tr(
* "<span style=' font-size:10pt;font-weight:60;'>An appstore powered by deepin community</span><br/>"
* "<a href='https://www.spark-app.store/'>https://www.spark-app.store</a><br/>"
* "<span style=' font-size:12pt;'>Spark developers</span>"
* )
* );
* dialog.setProductName(QLabel::tr("Spark Store"));
* dialog.setCompanyLogo(QPixmap(":/Logo-Spark.png"));
* dialog.setWebsiteName(QObject::tr("The Spark Project"));
* dialog.setWebsiteLink("https://gitee.com/deepin-community-store");
*/
a.setProductName(QLabel::tr("Spark Store"));
a.setProductIcon(QIcon::fromTheme("spark-store")); //设置Logo
a.setOrganizationName("spark-union");
a.setOrganizationDomain("https://www.deepinos.org/");
a.setApplicationName("Spark Store"); //不需要翻译,否则 ~/.local/share/ 下文件夹名称也被翻译为中文
a.setApplicationVersion(DApplication::buildVersion("3.0"));
a.setApplicationAcknowledgementPage("https://gitee.com/deepin-community-store/spark-store");
a.setApplicationDescription(
QObject::tr(
"<span style='font-size:10pt;font-weight:60;'>An appstore powered by deepin community</span><br/>"
"<a href='https://www.spark-app.store/'>https://www.spark-app.store</a><br/>"
"<span style='font-size:12pt;'>Spark developers</span><br/><br/>"
"Published under GPL V3"
)
);
Widget w;
QDesktopWidget *s=DApplication::desktop();
int d_w=s->width();
int d_h=s->height();
if(d_w<=1366){
w.setMinimumWidth(925);
w.resize(925,650);
}else if(d_w<=1920){
w.setMinimumWidth(1180);
w.resize(1180,760);
}else {
w.setMinimumWidth(1180);
w.resize(1180,760);
}
if(d_h<=768){
w.setMinimumHeight(650);
w.resize(925,650);
}else if(d_h<=1080){
w.setMinimumHeight(760);
w.resize(1180,760);
}else {
w.setMinimumHeight(760);
w.resize(1180,760);
}
QString arg1=argv[1];
if(arg1.left(6)=="spk://"){
w.openUrl(QUrl(argv[1]));
}
//让打开时界面显示在正中
Dtk::Widget::moveToCenter(&w);
w.show();
return a.exec();
}

42
src/progressload.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include "progressload.h"
ProgressLoad::ProgressLoad(QWidget *parent) :
QWidget(parent),
m_progess(new QWidget(this)),
timer(new QTimer),
value(0)
{
m_progess->move(0,0);
m_progess->show();
timer->setInterval(10);
timer->start();
connect(timer, &QTimer::timeout, [=]()
{
m_progess->setFixedWidth(width() / 100 * value);
m_progess->setFixedHeight(height());
});
}
void ProgressLoad::setValue(int v)
{
value = v;
m_progess->setFixedWidth(width() / 100 * value);
}
void ProgressLoad::setTheme(bool dark, QColor color)
{
if(dark)
{
plt.setColor(QPalette::Background, QColor(28,28,28));
setAutoFillBackground(true);
setPalette(plt);
}
else
{
plt.setColor(QPalette::Background, QColor(255,255,255));
setAutoFillBackground(true);
setPalette(plt);
}
m_progess->setStyleSheet("background-color: " + color.name() + ";");
}

26
src/progressload.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef PROGRESSLOAD_H
#define PROGRESSLOAD_H
#include <QWidget>
#include <QTimer>
#include <QPalette>
class ProgressLoad : public QWidget
{
Q_OBJECT
public:
explicit ProgressLoad(QWidget *parent = nullptr);
void setValue(int v);
void setTheme(bool dark,QColor color);
private:
QWidget *m_progess;
QTimer *timer;
int value;
QPalette plt;
};
#endif // PROGRESSLOAD_H

83
src/spark-store.pro Normal file
View File

@@ -0,0 +1,83 @@
#-------------------------------------------------
#
# Project created by QtCreator 2019-06-30T12:53:03
#
#-------------------------------------------------
QT += core gui network concurrent webenginewidgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = spark-store
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
DEFINES += QT_APP_DEBUG
include(../third-party/QtNetworkService/QtNetworkService.pri)
CONFIG += c++11 link_pkgconfig
PKGCONFIG += dtkwidget glib-2.0 gdk-pixbuf-2.0 libnotify
# 禁止输出 qWarning / qDebug 信息
CONFIG(release, debug|release): DEFINES += QT_NO_WARNING_OUTPUT QT_NO_DEBUG_OUTPUT
SOURCES += \
appitem.cpp \
big_image.cpp \
downloadlist.cpp \
downloadworker.cpp \
flowlayout.cpp \
image_show.cpp \
main.cpp \
progressload.cpp \
widget.cpp \
workerthreads.cpp
HEADERS += \
appitem.h \
big_image.h \
downloadlist.h \
downloadworker.h \
flowlayout.h \
image_show.h \
progressload.h \
widget.h \
workerthreads.h
FORMS += \
appitem.ui \
downloadlist.ui \
widget.ui
RESOURCES += \
../assets/icons.qrc
DISTFILES += \
../assets/tags/a2d-small.png \
../assets/tags/a2d.png \
../assets/tags/community-small.png \
../assets/tags/community.png \
../assets/tags/deepin-small.png \
../assets/tags/dtk-small.png \
../assets/tags/ubuntu-small.png \
../assets/tags/ubuntu.png \
../assets/tags/uos-small.png \
../assets/tags/community.svg \
../assets/tags/deepin.svg \
../assets/tags/logo_icon.svg \
../assets/tags/uos.svg
TRANSLATIONS += \
../translations/spark-store_en.ts \
../translations/spark-store_fr.ts \
../translations/spark-store_zh_CN.ts

1352
src/widget.cpp Normal file

File diff suppressed because it is too large Load Diff

168
src/widget.h Normal file
View File

@@ -0,0 +1,168 @@
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFile>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QJsonObject>
#include <QProcess>
#include <QFuture>
#include <QFutureWatcher>
#include <QToolButton>
#include <QTimer>
#include <QJsonArray>
#include <QFontDatabase>
#include <QMutex>
#include <DBlurEffectWidget>
#include <DTitlebar>
#include <DSearchEdit>
#include <DSettings>
#include <DSpinner>
#include <DWaterProgress>
#include "image_show.h"
#include "downloadlist.h"
#include "progressload.h"
#include "workerthreads.h"
#define LIST_MAX 99 // 一次最多下载数量
#define TMP_PATH "/tmp/spark-store"
DWIDGET_USE_NAMESPACE
namespace Ui {
class Widget;
}
class FlowLayout;
class DownloadController;
namespace AeaQt {
class HttpClient;
}
class Widget : public DBlurEffectWidget
{
Q_OBJECT
public:
explicit Widget(DBlurEffectWidget *parent = nullptr);
~Widget();
void startRequest(QUrl url, QString fileName);
void searchApp(QString);
int nowDownload = 0;
int allDownload = 0;
int isdownload = false;
void opensetting(); //打开设置页面
void openUrl(QUrl);
void setTheme(bool,QColor);
DTitlebar* getTitlebar();
static void sendNotification(const QString &message, const int msTimeout = 5000, const QString &icon = "spark-store");
static void sendNotification(const char *message, const int msTimeout = 5000, const QString &icon = "spark-store");
private slots:
void httpFinished();
void httpReadyRead();
void updateDataReadProgress(qint64,qint64);
// SpkAppInfoLoaderThread的槽函数
void sltAppinfoResetUi();
void sltAppinfoTags(QStringList *tagList);
void sltAppinfoDetails(QString *name, QString *details, QString *info,
QString *website, QString *packageName,
QUrl *fileUrl, bool isInstalled);
void sltAppinfoIcon(QPixmap *icon);
void sltAppinfoScreenshot(QPixmap *picture, int index);
void sltAppinfoFinish();
void displaySearchApp(QJsonArray array); // 展示搜索的APP信息
void downloadIconsFinished(int arraysize); // 当前搜索列表图标是否下载完成
void on_pushButton_download_clicked();
void on_pushButton_return_clicked();
void on_comboBox_server_currentIndexChanged(const QString &arg1);
void on_pushButton_updateServer_clicked();
void on_pushButton_updateApt_clicked();
void on_pushButton_uninstall_clicked();
void on_pushButton_clear_clicked();
void on_pushButton_website_clicked();
void on_pushButton_clicked();
void on_btn_openDir_clicked();
void on_stackedWidget_currentChanged(int arg1);
void on_webEngineView_urlChanged(const QUrl &arg1);
void on_webEngineView_loadStarted();
void on_webEngineView_loadProgress(int progress);
void on_webEngineView_loadFinished(bool arg1);
void on_pushButton_refresh_clicked();
void on_pushButton_translate_clicked();
public:
QUrl url;
downloadlist download_list[LIST_MAX];
Ui::Widget *ui;
QNetworkAccessManager *manager;
QNetworkReply *reply;
QList<QUrl> urList;
QFile *file;
QString appName;
QString urladdress;
QString pkgName;
QString appweb;
bool themeIsDark;
QFont font;
private:
void initUI();
void initConfig();
void chooseLeftMenu(int index);
void setfoot(int);
void updatefoot();
void updateUI();
quint64 dirFileSize(const QString &path);
private:
QPushButton * left_list[15];
QUrl menuUrl[13];
ProgressLoad *m_loadweb;
QLabel *m_loaderror = new QLabel;
QString serverUrl;
bool configCanSave = false;
bool isBusy = false;
int nowMenu = 0; // 定位当前菜单
int prePage = 0; // 定位前一个页面
long download_size = 0;
long size1 = 0;
long size2 = 0;
QPixmap screen[5];
QFuture<void> load;
QFutureWatcher<void> watchScreenshotLoad;
QTimer download_speed;
QString type_name;
QColor main_color;
int foot;
DSearchEdit *searchEdit = new DSearchEdit;
DTitlebar *titlebar;
DSpinner *spinner = new DSpinner;
int count = 0; // 记录当前搜索列表下载图标完成的个数
QMutex mutex; // 禁止多次搜索事件同时发生
QList<image_show*> label_screen;
SpkAppInfoLoaderThread appinfoLoadThread;
AeaQt::HttpClient *httpClient;
FlowLayout *applist_grid;
QHBoxLayout *main;
DownloadController *downloadController;
};
#endif // WIDGET_H

File diff suppressed because it is too large Load Diff

188
src/workerthreads.cpp Normal file
View File

@@ -0,0 +1,188 @@
#include "workerthreads.h"
#include <QProcess>
#include <QDir>
#include <QFile>
#include <QJsonDocument>
#include "widget.h"
#include "HttpClient.h"
void SpkAppInfoLoaderThread::run()
{
emit requestResetUi();
httpClient = new AeaQt::HttpClient;
httpClient->get(targetUrl.toString())
.header("content-type", "application/json")
.onResponse([this](QByteArray json_array)
{
qDebug() << "请求应用信息 " << json_array;
QString urladdress, deatils, more, packagename, appweb;
bool isInstalled;
// 将路径转化为相应源的下载路径
urladdress = targetUrl.toString().left(targetUrl.toString().length() - 8);
QStringList downloadurl = urladdress.split("/");
QString deburl = serverUrl;
deburl = deburl.left(urladdress.length() - 1);
urladdress = "https://img.jerrywang.top/"; // 使用图片专用服务器请保留这行,删除后将使用源服务器
urladdress = urladdress.left(urladdress.length() - 1);
for(int i = 3; i < downloadurl.size(); i++)
{
urladdress += "/" + downloadurl[i];
deburl += "/" + downloadurl[i];
}
// 路径转化完成
QJsonObject json = QJsonDocument::fromJson(json_array).object();
QString appName = json["Name"].toString();
QUrl fileUrl = deburl + json["Filename"].toString();
// 软件信息加载
QString details;
details = tr("PkgName: ") + json["Pkgname"].toString() + "\n";
details += tr("Version: ") + json["Version"].toString() + "\n";
if(!json["Author"].toString().trimmed().isEmpty())
{
details += tr("Author: ") + json["Author"].toString() + "\n";
}
if(!json["Website"].toString().trimmed().isEmpty())
{
details += tr("Official Site: ") + json["Website"].toString() + "\n";
// ui->pushButton_website->show(); // move to setinfo slot
appweb = json["Website"].toString();
}
details += tr("Contributor: ") + json["Contributor"].toString() + "\n";
details += tr("Update Time: ") + json["Update"].toString() + "\n";
details += tr("Installed Size: ") + json["Size"].toString() + "\n";
more = json["More"].toString();
QProcess isInstall;
packagename = json["Pkgname"].toString();
isInstall.start("dpkg -s " + json["Pkgname"].toString());
isInstall.waitForFinished();
int error = QString::fromStdString(isInstall.readAllStandardError().toStdString()).length();
if(error == 0)
{
isInstalled = true;
}
else
{
isInstalled = false;
}
emit requestSetAppInformation(&appName, &details, &more, &appweb, &packagename, &fileUrl, isInstalled);
// tag 加载
QString tags = json["Tags"].toString();
QStringList tagList = tags.split(";");
emit requestSetTags(&tagList);
// 图标加载
httpClient->get(urladdress+"icon.png")
.onResponse([this](QByteArray imgData)
{
QPixmap appicon;
appicon.loadFromData(imgData);
emit finishedIconLoad(&appicon);
})
.onError([this](QString errorStr)
{
Q_UNUSED(this)
Q_UNUSED(errorStr)
Widget::sendNotification(tr("Failed to load application icon."));
})
.block()
.timeout(5 * 100)
.exec();
// 截图展示加载
QPixmap screenshotCache[5];
for(int i = 0; i < 5; i++)
{
httpClient->get(urladdress + "screen_" + QString::number(i + 1) + ".png")
.onResponse([this, i, &screenshotCache](QByteArray imgData)
{
bool s = screenshotCache[i].loadFromData(imgData);
if(s)
{
emit finishedScreenshotLoad(&screenshotCache[i], i);
}
else
{
emit finishedScreenshotLoad(nullptr, i);
}
})
.onError([this](QString errorStr)
{
Q_UNUSED(this)
Q_UNUSED(errorStr)
qDebug() << "截图下载失败";
// Widget::sendNotification(tr("Failed to load application screenshot."));
})
.block()
.timeout(4 * 100)
.exec();
}
emit finishAllLoading();
httpClient->deleteLater();
})
.onError([](QString errorStr)
{
Q_UNUSED(errorStr)
Widget::sendNotification(tr("Failed to download app info. Please check internet connection."));
})
.timeout(5 * 100)
.block()
.exec();
}
void SpkAppInfoLoaderThread::setUrl(const QUrl &url)
{
targetUrl = url;
}
void SpkAppInfoLoaderThread::setServer(const QString &server)
{
serverUrl = server;
}
void SpkAppInfoLoaderThread::downloadFinished(int exitcode, QProcess::ExitStatus status)
{
Q_UNUSED(exitcode)
Q_UNUSED(status)
qDebug() << "Finish one download";
finishedDownload = true;
}
int SpkAppInfoLoaderThread::waitDownload(QProcess& downloader)
{
while(!downloader.waitForFinished(100))
{
if(downloader.state() == QProcess::NotRunning)
{
return -1;
}
if(this->isInterruptionRequested())
{
downloader.terminate();
downloader.waitForFinished(-1);
qDebug() << "Appinfo loader thread was forcefully terminated";
return -1;
}
}
return 0;
}

50
src/workerthreads.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef WORKERTHREADS_H
#define WORKERTHREADS_H
#include <QThread>
#include <QPixmap>
#include <QUrl>
#include <QProcess>
namespace AeaQt {
class HttpClient;
}
class SpkAppInfoLoaderThread Q_DECL_FINAL : public QThread
{
Q_OBJECT
public:
// explicit SpkAppInfoLoaderThread() = default;
protected:
void run() Q_DECL_OVERRIDE;
private:
QUrl targetUrl;
QString serverUrl;
bool finishedDownload = false;
int downloaderRetval = 0;
AeaQt::HttpClient *httpClient;
int waitDownload(QProcess& downloader);
public slots:
void setUrl(const QUrl &url);
void setServer(const QString &server);
void downloadFinished(int exitcode, QProcess::ExitStatus status);
signals:
void requestResetUi();
void requestSetTags(QStringList *tagList);
void requestSetAppInformation(QString *name, QString *details, QString *info,
QString *website, QString *packageName,
QUrl *fileUrl, bool isInstalled);
void finishedIconLoad(QPixmap *icon);
void finishedScreenshotLoad(QPixmap *icon, int index); // 该信号必须以 BlockingQueued 方式连接
void finishAllLoading(); // 该信号必须以 BlockingQueued 方式连接
};
#endif // WORKERTHREADS_H

View File

@@ -1,2 +1,3 @@
#!/bin/sh
dpkg -i $1 || apt install -yf || dpkg -P $1

View File

@@ -0,0 +1,42 @@
/**********************************************************
Author: Qt君
微信公众号: Qt君(文章首发)
Website: qtbig.com(后续更新)
Email: 2088201923@qq.com
QQ交流群: 732271126
LISCENSE: MIT
**********************************************************/
#include "HttpClient.h"
#include <QJsonObject>
#include <QJsonDocument>
#include <QBuffer>
using namespace AeaQt;
HttpClient::HttpClient()
{
}
HttpClient::~HttpClient()
{
}
HttpRequest HttpClient::get(const QString &url)
{
return HttpRequest(QNetworkAccessManager::GetOperation, this).url(url);
}
HttpRequest HttpClient::post(const QString &url)
{
return HttpRequest(QNetworkAccessManager::PostOperation, this).url(url);
}
HttpRequest HttpClient::put(const QString &url)
{
return HttpRequest(QNetworkAccessManager::PutOperation, this).url(url);
}
HttpRequest HttpClient::send(const QString &url, QNetworkAccessManager::Operation op)
{
return HttpRequest(op, this).url(url);
}

View File

@@ -0,0 +1,36 @@
/**********************************************************
Author: Qt君
微信公众号: Qt君(文章首发)
Website: qtbig.com(后续更新)
Email: 2088201923@qq.com
QQ交流群: 732271126
LISCENSE: MIT
**********************************************************/
#ifndef HTTP_CLIENT_H
#define HTTP_CLIENT_H
#include "HttpRequest.h"
#include "HttpResponse.h"
#include <QNetworkRequest>
#include <QNetworkReply>
namespace AeaQt {
class HttpClient : public QNetworkAccessManager
{
Q_OBJECT
public:
friend class HttpRequest;
HttpClient();
~HttpClient();
HttpRequest get(const QString &url);
HttpRequest post(const QString &url);
HttpRequest put(const QString &url);
HttpRequest send(const QString &url, Operation op = GetOperation);
};
}
#endif

View File

@@ -0,0 +1,268 @@
/**********************************************************
Author: Qt君
微信公众号: Qt君(文章首发)
Website: qtbig.com(后续更新)
Email: 2088201923@qq.com
QQ交流群: 732271126
LISCENSE: MIT
**********************************************************/
#include "HttpRequest.h"
#include "HttpClient.h"
#include <QJsonDocument>
#include <QUrlQuery>
#include <QBuffer>
#include <QMetaEnum>
using namespace AeaQt;
static const char *s_httpOperation[] = {
"UnknownOperation",
"HeadOperation",
"GetOperation",
"PutOperation",
"PostOperation",
"DeleteOperation",
"CustomOperation"
};
HttpRequest::HttpRequest()
{
}
HttpRequest::~HttpRequest()
{
}
HttpRequest::HttpRequest(QNetworkAccessManager::Operation op, HttpClient *jsonHttpClient) :
m_body(QByteArray()),
m_op(op),
m_httpService(jsonHttpClient),
m_timeout(-1)
{
}
HttpRequest &HttpRequest::url(const QString &url)
{
m_networkRequest.setUrl(QUrl(url));
return *this;
}
HttpRequest &HttpRequest::header(const QString &key, const QVariant &value)
{
m_networkRequest.setRawHeader(QByteArray(key.toStdString().data()), QByteArray(value.toString().toStdString().data()));
return *this;
}
HttpRequest &HttpRequest::headers(const QMap<QString, QVariant> &headers)
{
QMapIterator<QString, QVariant> iter(headers);
while (iter.hasNext()) {
iter.next();
header(iter.key(), iter.value());
}
return *this;
}
HttpRequest &HttpRequest::body(const QVariantMap &content)
{
m_body = QJsonDocument(QJsonObject::fromVariantMap(content)).toJson();
return *this;
}
HttpRequest &HttpRequest::body(const QJsonObject &content)
{
m_body = QJsonDocument(QJsonObject::fromVariantMap(content.toVariantMap())).toJson();
return *this;
}
HttpRequest &HttpRequest::body(const QByteArray &content)
{
m_body = content;
return *this;
}
#if 0
HttpRequest &HttpRequest::body(const QVariant &body)
{
/// clear m_jsonBody
m_jsonBody = QJsonObject();
if (type == X_Www_Form_Urlencoded) {
QUrl url;
QUrlQuery urlQuery(url);
if (body.type() == QVariant::Map
|| body.typeName() == QMetaType::typeName(QMetaType::QJsonObject)) {
QMapIterator<QString, QVariant> i(body.toMap());
while (i.hasNext()) {
i.next();
urlQuery.addQueryItem(i.key(), i.value().toString());
}
url.setQuery(urlQuery);
m_body = url.toString(QUrl::FullyEncoded).toUtf8().remove(0, 1);
}
else {
m_body = body.toByteArray();
}
}
else if (type == Raw_Text_Json) {
if (body.type() == QVariant::Map
|| body.typeName() == QMetaType::typeName(QMetaType::QJsonObject)) {
m_body = QJsonDocument(QJsonObject::fromVariantMap(body.toMap())).toJson();
}
else {
log_warning << "This is not data in JSON format(QVariantMap or QJsonObject).";
m_body = QByteArray();
// warning output
}
}
else {
m_body = QByteArray();
log_warning << "Disable body.";
}
log_debugger << "Body Content:" << m_body;
return *this;
}
#endif
HttpRequest &HttpRequest::onResponse(const QObject *receiver, const char *slot, HttpResponse::SupportMethod type)
{
m_slotsMap.insert(type, {slot, QVariant::fromValue((QObject *)receiver)});
return *this;
}
HttpRequest &HttpRequest::onResponse(std::function<void (QNetworkReply *)> lambda)
{
return onResponse(QVariant::fromValue(lambda));
}
HttpRequest &HttpRequest::onResponse(std::function<void (QVariantMap)> lambda)
{
return onResponse(QVariant::fromValue(lambda));
}
HttpRequest &HttpRequest::onResponse(std::function<void (QByteArray)> lambda)
{
return onResponse(QVariant::fromValue(lambda));
}
HttpRequest &HttpRequest::onResponse(std::function<void (qint64, qint64)> lambda)
{
return onResponse(QVariant::fromValue(lambda));
}
HttpRequest &HttpRequest::onError(const QObject *receiver, const char *slot)
{
return onResponse(receiver, slot, HttpResponse::AutoInfer);
}
HttpRequest &HttpRequest::onError(std::function<void (QNetworkReply::NetworkError)> lambda)
{
return onResponse(QVariant::fromValue(lambda));
}
HttpRequest &HttpRequest::onError(std::function<void (QString)> lambda)
{
return onResponse(QVariant::fromValue(lambda));
}
HttpRequest &HttpRequest::onError(std::function<void (QNetworkReply::NetworkError, QNetworkReply *)> lambda)
{
return onResponse(QVariant::fromValue(lambda));
}
HttpRequest &HttpRequest::onError(std::function<void (QString, QNetworkReply *)> lambda)
{
return onResponse(QVariant::fromValue(lambda));
}
HttpRequest &HttpRequest::timeout(const int &msec)
{
m_timeout = msec;
return *this;
}
HttpRequest &HttpRequest::block()
{
m_isBlock = true;
return *this;
}
HttpRequest &HttpRequest::onResponse(QVariant lambda)
{
m_slotsMap.insert(HttpResponse::AutoInfer, {lambda.typeName(), lambda});
return *this;
}
HttpResponse *HttpRequest::exec()
{
QNetworkReply* reply = NULL;
QBuffer* sendBuffer = new QBuffer();
if (! m_body.isEmpty()) {
sendBuffer->setData(m_body);
}
log_debugger << "Http Client info: ";
log_debugger << "Type: " << s_httpOperation[m_op];
log_debugger << "Url: " << m_networkRequest.url().toString();
QString headers;
for (int i = 0; i < m_networkRequest.rawHeaderList().count(); i++) {
QString each = m_networkRequest.rawHeaderList().at(i);
QString header = m_networkRequest.rawHeader(each.toUtf8());
headers += QString("%1: %2;").arg(each)
.arg(header);
}
log_debugger << "Header: " << headers;
log_debugger << "Send buffer(Body):\r\n" << m_body;
reply = m_httpService->createRequest(m_op, m_networkRequest, sendBuffer);
if (reply == NULL) {
sendBuffer->deleteLater();
return NULL;
}
else {
sendBuffer->setParent(reply);
}
return new HttpResponse(reply, m_slotsMap, m_timeout, m_isBlock);
}
HttpRequest &HttpRequest::queryParam(const QString &key, const QVariant &value)
{
QUrl url(m_networkRequest.url());
QUrlQuery urlQuery(url);
urlQuery.addQueryItem(key, value.toString());
url.setQuery(urlQuery);
m_networkRequest.setUrl(url);
return *this;
}
HttpRequest &HttpRequest::queryParams(const QMap<QString, QVariant> &params)
{
QMapIterator<QString, QVariant> iter(params);
while (iter.hasNext()) {
iter.next();
queryParam(iter.key(), iter.value());
}
return *this;
}
HttpRequest &HttpRequest::userAttribute(const QVariant &value)
{
m_networkRequest.setAttribute(QNetworkRequest::User, value);
return *this;
}

View File

@@ -0,0 +1,115 @@
/**********************************************************
Author: Qt君
微信公众号: Qt君(文章首发)
Website: qtbig.com(后续更新)
Email: 2088201923@qq.com
QQ交流群: 732271126
LISCENSE: MIT
**********************************************************/
#ifndef HTTP_REQUEST_H
#define HTTP_REQUEST_H
#include "HttpResponse.h"
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QJsonObject>
#include <QDebug>
namespace AeaQt {
#ifdef QT_APP_DEBUG
#define log_debugger qDebug().noquote().nospace() \
<< "[AeaQt::Network] Debug: -> " \
<< "function: " << __func__ << "; " \
<< "line: " << __LINE__ << "; "
#else
#define log_debugger QString()
#endif
#define log_warning qWarning().noquote().nospace() \
<< "[AeaQt::Network] Warning: -> " \
<< "function: " << __func__ << "; " \
<< "line: " << __LINE__ << "; "
class HttpClient;
class HttpRequest
{
public:
enum BodyType {
None = 0, // This request does not have a body.
X_Www_Form_Urlencoded, // x-www-form-urlencoded
Raw_Text_Json, // application/json
};
explicit HttpRequest(QNetworkAccessManager::Operation op, HttpClient *jsonHttpClient);
virtual ~HttpRequest();
HttpRequest &url(const QString &url);
HttpRequest &header(const QString &key, const QVariant &value);
HttpRequest &headers(const QMap<QString, QVariant> &headers);
HttpRequest &queryParam(const QString &key, const QVariant &value);
HttpRequest &queryParams(const QMap<QString, QVariant> &params);
/* Mainly used for identification */
HttpRequest &userAttribute(const QVariant &value);
HttpRequest &body(const QVariantMap &content);
HttpRequest &body(const QJsonObject &content);
HttpRequest &body(const QByteArray &content);
/*
* @onRespone slot support type: void function(QVariantMap resultMap) OR
* void function(QByteArray resultData) OR
* void function(QNetworkReply* reply)
* note: The same type is only triggered once
*/
HttpRequest &onResponse(const QObject *receiver, const char *slot, HttpResponse::SupportMethod type = HttpResponse::AutoInfer);
HttpRequest &onResponse(std::function<void (QNetworkReply*)> lambda);
HttpRequest &onResponse(std::function<void (QVariantMap)> lambda);
HttpRequest &onResponse(std::function<void (QByteArray)> lambda);
HttpRequest &onResponse(std::function<void (qint64, qint64)> lambda);
/*
* @onError slot support type: void function(QNetworkReply::NetworkError error)
* void function(QString errorString);
* void function(QNetworkReply::NetworkError error, QNetworkReply* reply);
* void function(QString errorString, QNetworkReply* reply);
* note: The same type is only triggered once
*/
HttpRequest &onError(const QObject *receiver, const char *slot);
HttpRequest &onError(std::function<void (QNetworkReply::NetworkError)> lambda);
HttpRequest &onError(std::function<void (QString)> lambda);
HttpRequest &onError(std::function<void (QNetworkReply::NetworkError, QNetworkReply*)> lambda);
HttpRequest &onError(std::function<void (QString, QNetworkReply*)> lambda);
/**
* @brief msec <= 0, disable timeout
* msec > 0, enable timeout
*/
HttpRequest &timeout(const int &msec = -1);
/**
* @brief Block current thread, entering an event loop.
*/
HttpRequest &block();
HttpResponse *exec();
private:
HttpRequest();
HttpRequest &onResponse(QVariant lambda);
private:
QNetworkRequest m_networkRequest;
QByteArray m_body;
QNetworkAccessManager::Operation m_op;
HttpClient *m_httpService;
int m_timeout;
bool m_isBlock;
QMultiMap<HttpResponse::SupportMethod, QPair<QString, QVariant>> m_slotsMap;
};
}
#endif // HTTP_REQUEST_H

View File

@@ -0,0 +1,331 @@
/**********************************************************
Author: Qt君
微信公众号: Qt君(文章首发)
Website: qtbig.com(后续更新)
Email: 2088201923@qq.com
QQ交流群: 732271126
LISCENSE: MIT
**********************************************************/
#include "HttpResponse.h"
#include <QRegExp>
#include <QStringList>
#include <QByteArray>
#include <QNetworkConfigurationManager>
#include <QMetaEnum>
#include <QEventLoop>
#include <QJsonDocument>
#include <QJsonObject>
#define T2S(t) (QString(#t).remove(QRegExp("\\s"))) //type to string
#define _exec(target, type, arg) \
if (target.canConvert<std::function<void (type)> >()) { \
std::function<void (type)> func = target.value<std::function<void (type)> >(); func(arg); \
} \
else
#define _exec2(target, type1, type2, arg1, arg2) \
if (target.canConvert<std::function<void (type1, type2)> >()) { \
std::function<void (type1, type2)> func = target.value<std::function<void (type1, type2)> >(); func(arg1, arg2); \
} else
using namespace AeaQt;
static const QMap<HttpResponse::SupportMethod, QMap<QString, QVariant>> methodParams =
{
{
HttpResponse::onResponse_QNetworkReply_A_Pointer,
{
{"types", QStringList({T2S(QNetworkReply*)})},
{"lambda", T2S(std::function<void (QNetworkReply*)>)},
{"signal", SIGNAL(finished(QNetworkReply*))},
{"isAutoInfer", true}
}
},
{
HttpResponse::onResponse_QByteArray,
{
{"types", QStringList({T2S(QByteArray)})},
{"lambda", T2S(std::function<void (QByteArray)>)},
{"signal", SIGNAL(finished(QByteArray))},
{"isAutoInfer", true}
}
},
{
HttpResponse::onResponse_QVariantMap,
{
{"types", QStringList({T2S(QVariantMap)})},
{"lambda", T2S(std::function<void (QVariantMap)>)},
{"signal", SIGNAL(finished(QVariantMap))},
{"isAutoInfer", true}
}
},
{
HttpResponse::onDownloadProgress_qint64_qint64,
{
{"types", QStringList({T2S(qint64), T2S(qint64)})},
{"lambda", T2S(std::function<void (qint64, qint64)>)},
{"signal", SIGNAL(downloadProgress(qint64, qint64))},
{"isAutoInfer", true}
}
},
{
HttpResponse::onError_QNetworkReply_To_NetworkError,
{
{"types", QStringList({T2S(QNetworkReply::NetworkError)})},
{"lambda", T2S(std::function<void (QNetworkReply::NetworkError)>)},
{"signal", SIGNAL(error(QNetworkReply::NetworkError))},
{"isAutoInfer", true}
}
},
{
HttpResponse::onError_QString,
{
{"types", QStringList({T2S(QString)})},
{"lambda", T2S(std::function<void (QString)>)},
{"signal", SIGNAL(error(QString))},
{"isAutoInfer", true}
}
},
{
HttpResponse::onError_QNetworkReply_To_NetworkError_QNetworkReply_A_Pointer,
{
{"types", QStringList({T2S(QNetworkReply::NetworkError), T2S(QNetworkReply*)})},
{"lambda", T2S(std::function<void (QNetworkReply::NetworkError, QNetworkReply*)>)},
{"signal", SIGNAL(error(QNetworkReply::NetworkError, QNetworkReply*))},
{"isAutoInfer", true}
}
},
{
HttpResponse::onError_QString_QNetworkReply_A_Poniter,
{
{"types", QStringList({T2S(QString), T2S(QNetworkReply*)})},
{"lambda", T2S(std::function<void (QString, QNetworkReply*)>)},
{"signal", SIGNAL(error(QString, QNetworkReply*))},
{"isAutoInfer", true}
}
},
};
static int extractCode(const char *member)
{
/* extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE */
return (((int)(*member) - '0') & 0x3);
}
HttpResponse::HttpResponse(QNetworkReply *networkReply,
const QMultiMap<SupportMethod, QPair<QString, QVariant> > &slotsMap,
const int &timeout,
bool isBlock)
: m_networkReply(networkReply),
m_slotsMap(slotsMap),
QObject(networkReply)
{
slotsMapOperation(m_slotsMap);
new HttpResponseTimeout(networkReply, timeout);
connect(m_networkReply, SIGNAL(finished()), this, SLOT(onFinished()));
connect(m_networkReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
connect(m_networkReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
if (isBlock) {
QEventLoop loop;
QObject::connect(m_networkReply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
}
}
HttpResponse::~HttpResponse()
{
}
QNetworkReply *HttpResponse::networkReply()
{
return m_networkReply;
}
void HttpResponse::onFinished()
{
QNetworkReply *reply = m_networkReply;
if (reply->error() != QNetworkReply::NoError)
return;
if (m_slotsMap.contains(onResponse_QNetworkReply_A_Pointer)) {
_exec(m_slotsMap.value(onResponse_QNetworkReply_A_Pointer).second, QNetworkReply*, reply) {
emit finished(reply);
}
}
else if (m_slotsMap.contains((onResponse_QByteArray))) {
QByteArray result = reply->readAll();
_exec(m_slotsMap.value((onResponse_QByteArray)).second, QByteArray, result) {
emit finished(result);
}
reply->deleteLater();
}
else if (m_slotsMap.contains((onResponse_QVariantMap))) {
QByteArray result = reply->readAll();
QVariantMap resultMap = QJsonDocument::fromJson(result).object().toVariantMap();
_exec(m_slotsMap.value((onResponse_QVariantMap)).second, QVariantMap, resultMap){
emit finished(resultMap);
}
reply->deleteLater();
}
}
void HttpResponse::onError(QNetworkReply::NetworkError error)
{
QNetworkReply *reply = m_networkReply;
const QMetaObject & metaObject = QNetworkReply::staticMetaObject;
QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("NetworkError"));
QString errorString = reply->errorString().isEmpty() ? metaEnum.valueToKey(error) : reply->errorString();
if (m_slotsMap.contains((onError_QString_QNetworkReply_A_Poniter))) {
_exec2(m_slotsMap.value((onError_QString_QNetworkReply_A_Poniter)).second, QString, QNetworkReply*, errorString, reply) {
emit this->error(errorString, reply);
}
}
else if (m_slotsMap.contains((onError_QNetworkReply_To_NetworkError_QNetworkReply_A_Pointer))) {
_exec2(m_slotsMap.value((onError_QNetworkReply_To_NetworkError_QNetworkReply_A_Pointer)).second,
QNetworkReply::NetworkError, QNetworkReply*,
error, reply) {
emit this->error(error, reply);
}
}
else if (m_slotsMap.contains((onError_QString))) {
_exec(m_slotsMap.value((onError_QString)).second, QString, errorString) {
emit this->error(errorString);
}
reply->deleteLater();
}
else if (m_slotsMap.contains((onError_QNetworkReply_To_NetworkError))) {
_exec(m_slotsMap.value((onError_QNetworkReply_To_NetworkError)).second, QNetworkReply::NetworkError, error) {
emit this->error(error);
}
reply->deleteLater();
}
}
void HttpResponse::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
if (m_slotsMap.contains((onDownloadProgress_qint64_qint64))) {
_exec2(m_slotsMap.value((onDownloadProgress_qint64_qint64)).second, qint64, qint64, bytesReceived, bytesTotal) {
emit downloadProgress(bytesReceived, bytesTotal);
}
}
}
static void extractSlot(const QString &respReceiverSlot, QString &extractSlot, QStringList &extractSlotTypes)
{
QString slot(respReceiverSlot);
if (extractCode(respReceiverSlot.toStdString().data()) == QSLOT_CODE && !slot.isEmpty()) {
slot.remove(0, 1);
QString unconvertedSlotType = slot;
int startIndex = slot.indexOf('(');
int endIndex = slot.indexOf(')');
Q_ASSERT(startIndex != -1 && endIndex != -1);
extractSlot = slot.remove(startIndex, endIndex-startIndex+1);
extractSlotTypes = unconvertedSlotType.mid(startIndex+1, endIndex-startIndex-1)
.remove(QRegExp("\\s"))
.split(',');
}
}
/* from slotMap get [SupportMethod] */
static HttpResponse::SupportMethod getSupportMethod(const QPair<QString, QVariant> &slotMap) {
QMapIterator<HttpResponse::SupportMethod, QMap<QString, QVariant>> iter(methodParams);
QString receiverSlot = slotMap.first;
QString slot;
QStringList slotTypes;
extractSlot(receiverSlot, slot, slotTypes);
while (iter.hasNext()) {
iter.next();
HttpResponse::SupportMethod supportMethod = iter.key();
QMap<QString, QVariant> value = iter.value();
if (slotTypes == value.value("types").toStringList()) {
return supportMethod;
}
else if (receiverSlot == value.value("lambda").toString()) {
return supportMethod;
}
}
return HttpResponse::Invalid;
}
static void autoInfterConvertedSupportMethod(QMultiMap<HttpResponse::SupportMethod, QPair<QString, QVariant> > &unconvertedSlotsMap)
{
QMultiMap<HttpResponse::SupportMethod, QPair<QString, QVariant> > convertedSlotsMap;
QMapIterator<HttpResponse::SupportMethod, QPair<QString, QVariant> > iter(unconvertedSlotsMap);
while (iter.hasNext()) {
iter.next();
const HttpResponse::SupportMethod supportMethod = iter.key();
const QPair<QString, QVariant> slotMap = iter.value();
if (supportMethod == HttpResponse::AutoInfer) {
HttpResponse::SupportMethod supportMethod = getSupportMethod(slotMap);
if (supportMethod == HttpResponse::Invalid) {
qDebug()<<"Not find support Method!"<<slotMap.first;
}
else {
if (methodParams[supportMethod].value("isAutoInfer").toBool())
convertedSlotsMap.insert(supportMethod, slotMap);
else
qDebug()<<"This type["<<methodParams[supportMethod].value("types").toString()<<"] does not support automatic derivation";
}
}
else {
if (methodParams[supportMethod].value("isAutoInfer").toBool())
convertedSlotsMap.insert(supportMethod, slotMap);
else
qDebug()<<"This type["<<methodParams[supportMethod].value("types").toString()<<"] does not support automatic derivation";
}
}
unconvertedSlotsMap = convertedSlotsMap;
}
void HttpResponse::slotsMapOperation(QMultiMap<SupportMethod, QPair<QString, QVariant> > &slotsMap)
{
autoInfterConvertedSupportMethod(slotsMap);
QMapIterator<SupportMethod, QPair<QString, QVariant> > iter(slotsMap);
while (iter.hasNext()) {
iter.next();
SupportMethod supportMethod = iter.key();
const QPair<QString, QVariant> &slotMap = iter.value();
const QString &receiverSlot = slotMap.first;
QVariant target = slotMap.second;
const QObject *receiver = target.value<QObject*>();
if (receiver) {
if (methodParams.contains(supportMethod)) {
connect(this,
methodParams[supportMethod].value("signal").toString().toStdString().data(),
receiver,
receiverSlot.toStdString().data(),
Qt::QueuedConnection);
}
}
}
}
HttpResponse::HttpResponse()
{
}

View File

@@ -0,0 +1,107 @@
/**********************************************************
Author: Qt君
微信公众号: Qt君(文章首发)
Website: qtbig.com(后续更新)
Email: 2088201923@qq.com
QQ交流群: 732271126
LISCENSE: MIT
**********************************************************/
#ifndef HTTP_RESPONSE_H
#define HTTP_RESPONSE_H
#include <QNetworkReply>
#include <QMultiMap>
#include <functional>
#include <QTimer>
namespace AeaQt {
class HttpResponseTimeout : public QObject {
Q_OBJECT
public:
HttpResponseTimeout(QNetworkReply *parent = NULL, const int timeout = -1) : QObject(parent) {
if (timeout > 0)
QTimer::singleShot(timeout, this, SLOT(onTimeout()));
}
private slots:
void onTimeout() {
QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
if (reply->isRunning()) {
reply->abort();
reply->deleteLater();
}
}
};
class HttpResponse : public QObject
{
Q_OBJECT
public:
/*
* Support Reflex Method
* default: AutoInfer
* AutoInfer: Automatic derivation based on type
*/
enum SupportMethod {
Invalid = 0,
AutoInfer,
onResponse_QNetworkReply_A_Pointer, /* method: void function(QNetworkReply* reply); Is_AutoInfer: true */
onResponse_QByteArray, /* method: void function(QByteArray data); Is_AutoInfer: true */
onResponse_QVariantMap, /* method: void function(QVariantMap map); Is_AutoInfer: true */
onDownloadProgress_qint64_qint64, /* method: void function(qint64 bytesReceived, qint64 bytesTotal); Is_AutoInfer: true */
onError_QNetworkReply_To_NetworkError, /* method: void function(QNetworkReply::NetworkError error); Is_AutoInfer: true */
onError_QString, /* method: void function(QString errorString); Is_AutoInfer: true */
onError_QNetworkReply_To_NetworkError_QNetworkReply_A_Pointer, /* method: void function(QNetworkReply::NetworkError error, QNetworkReply* reply); Is_AutoInfer: true */
onError_QString_QNetworkReply_A_Poniter/* method: void function(QString errorString, QNetworkReply* reply); Is_AutoInfer: true */
};
explicit HttpResponse(QNetworkReply *networkReply,
const QMultiMap<SupportMethod, QPair<QString, QVariant> > &slotsMap,
const int &timeout,
bool isBlock);
virtual ~HttpResponse();
QNetworkReply *networkReply();
protected:
void slotsMapOperation(QMultiMap<SupportMethod, QPair<QString, QVariant> > &slotsMap);
signals:
void finished(QNetworkReply *reply);
void finished(QByteArray data);
void finished(QVariantMap map);
void error(QString errorString);
void error(QNetworkReply::NetworkError error);
void error(QString errorString, QNetworkReply *reply);
void error(QNetworkReply::NetworkError error, QNetworkReply *reply);
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private slots:
void onFinished();
void onError(QNetworkReply::NetworkError error);
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private:
HttpResponse();
private:
QMultiMap<SupportMethod, QPair<QString, QVariant> > m_slotsMap;
QNetworkReply *m_networkReply;
};
}
Q_DECLARE_METATYPE(std::function<void (QNetworkReply*)>)
Q_DECLARE_METATYPE(std::function<void (QByteArray)>)
Q_DECLARE_METATYPE(std::function<void (QVariantMap)>)
Q_DECLARE_METATYPE(std::function<void (QString)>)
Q_DECLARE_METATYPE(std::function<void (QNetworkReply::NetworkError)>)
Q_DECLARE_METATYPE(std::function<void (QNetworkReply::NetworkError, QNetworkReply *)>)
Q_DECLARE_METATYPE(std::function<void (QString, QNetworkReply *)>)
Q_DECLARE_METATYPE(std::function<void (qint64, qint64)>)
#endif // HTTP_RESPONSE_H

21
third-party/QtNetworkService/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Qt君
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,15 @@
#**********************************************************
#Author: Qt君
#微信公众号: Qt君(文章首发)
#Website: qtbig.com(后续更新)
#Email: 2088201923@qq.com
#QQ交流群: 732271126
#LISCENSE: MIT
#**********************************************************
INCLUDEPATH += $$PWD/
QT += network
!CONFIG(QT_APP_MODE) {
LIBS += -L$$OUT_PWD/../third-party/QtNetworkService/Lib -lQtNetworkService
}

View File

@@ -0,0 +1,37 @@
#**********************************************************
#Author: Qt君
#微信公众号: Qt君(文章首发)
#Website: qtbig.com(后续更新)
#Email: 2088201923@qq.com
#QQ交流群: 732271126
#LISCENSE: MIT
#**********************************************************
CONFIG += c++11
#CONFIG += QT_APP_MODE
DEFINES += QT_APP_DEBUG
QT += network
TEMPLATE = lib
CONFIG += staticlib
unix:TARGET = $$OUT_PWD/Lib/QtNetworkService
win32: {
DESTDIR = $$OUT_PWD/Lib/
TARGET = QtNetworkService
}
message(" ================ QtNetworkService Library ================ ")
SOURCES += \
$$PWD/HttpResponse.cpp \
$$PWD/HttpRequest.cpp \
$$PWD/HttpClient.cpp
HEADERS += \
$$PWD/HttpResponse.h \
$$PWD/HttpRequest.h \
$$PWD/HttpClient.h
include(QtNetworkService.pri)

83
third-party/QtNetworkService/README.md vendored Normal file
View File

@@ -0,0 +1,83 @@
# 示例
(1) 简单示例
* 使用lambda特性
```cpp
static HttpClient http;
http.post("https://example.com")
.header("content-type", "application/json")
.queryParam("key", "Hello world!")
.body(R"({"user": "test"})")
.onResponse([](QByteArray result) { /* 接收数据 */
qDebug() << "Result: " << result;
})
.onResponse([](qint64 recv, qint64 total) { /* 接收进度 */
qDebug() << "Total: " << total << "; Received: " << recv;
})
.onError([](QString errorStr) { /* 错误处理 */
qDebug()<<"Error: "<<errorStr;
})
.timeout(30 * 1000) /* 超时操作(30s) */
.block() /* 阻塞操作 */
.exec();
```
* 使用Qt信号与槽特性
```cpp
http.post("https://example.com")
.header("content-type", "application/json")
.queryParam("key", "Hello world!")
.body(R"({"user": "test"})")
.onResponse(this, SLOT(finish(QByteArray)))
.onResponse(this, SLOT(downloadProgress(qint64, qint64)))
.onError(this, SLOT(error(QString)))
.timeout(30 * 1000) /* 超时操作(30s) */
.block() /* 阻塞操作 */
.exec();
```
(2) 复杂示例
```cpp
/* 获取音乐url功能请求嵌套请求 */
static HttpService http;
http.get("http://mobilecdn.kugou.com/api/v3/search/song")
.queryParam("format", "json")
.queryParam("keyword", "稻香")
.queryParam("page", 1)
.queryParam("pagesize", 3)
.queryParam("showtype", 1)
.onResopnse([](QVariantMap result){
QVariantMap data;
QList<QVariant> infos;
if (!result.isEmpty())
data = result.value("data").toMap();
if (!data.isEmpty())
infos = data.value("info").toList();
static HttpService http;
foreach (QVariant each, infos) {
http.get("http://m.kugou.com/app/i/getSongInfo.php")
.queryParam("cmd", "playInfo")
.queryParam("hash", each.toMap()["hash"])
.onResopnse([](QVariantMap result){
qDebug()<<"mp3: "<<result["url"].toString();
})
.onError([](QString errorStr){ qDebug()<<"Error: "<<errorStr; })
.exec();
}
})
.onError([](QString errorStr){ qDebug()<<"Error: "<<errorStr; })
.exec();
```
## 1.如何使用?
* 以Qt子工程使用再在其他子工程包含pri文件即可使用;
* 通过引用库的方式使用.
## 2.如何启用demo测试
* 在QtNetworkService.pro文件中将"#CONFIG += QT_APP_MODE"这一行的#去除即可转为可执行文件形式在Demo目录的main.cpp为主执行文件如需要测试接口编辑便可。
## 3.扫码关注,第一时间获取推送
<p align="center">
<img src="http://www.qtbig.com/about/index/my_qrcode.jpg" alt="微信公众号:Qt君">
<p align="center"><em>Qt君</em></p>
</p>

Binary file not shown.

View File

@@ -0,0 +1,632 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en">
<context>
<name>AppItem</name>
<message>
<location filename="../src/appitem.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QLabel</name>
<message>
<location filename="../src/main.cpp" line="39"/>
<source>Spark Store</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../src/main.cpp" line="47"/>
<source>&lt;span style=&apos;font-size:10pt;font-weight:60;&apos;&gt;An appstore powered by deepin community&lt;/span&gt;&lt;br/&gt;&lt;a href=&apos;https://www.spark-app.store/&apos;&gt;https://www.spark-app.store&lt;/a&gt;&lt;br/&gt;&lt;span style=&apos;font-size:12pt;&apos;&gt;Spark developers&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;Published under GPL V3</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SpkAppInfoLoaderThread</name>
<message>
<location filename="../src/workerthreads.cpp" line="117"/>
<source>Failed to download app info. Please check internet connection.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/workerthreads.cpp" line="44"/>
<source>PkgName: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/workerthreads.cpp" line="45"/>
<source>Version: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/workerthreads.cpp" line="47"/>
<source>Author: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/workerthreads.cpp" line="51"/>
<source>Official Site: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/workerthreads.cpp" line="55"/>
<source>Contributor: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/workerthreads.cpp" line="56"/>
<source>Update Time: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/workerthreads.cpp" line="57"/>
<source>Installed Size: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/workerthreads.cpp" line="85"/>
<source>Failed to load application icon.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Widget</name>
<message>
<location filename="../src/widget.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="84"/>
<source>background-color:#FFFFFF</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="184"/>
<source>Tools</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="265"/>
<source>Beautify</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="115"/>
<source>Music</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="198"/>
<source>Office</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="286"/>
<source>Others</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="279"/>
<source>Games</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="560"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="600"/>
<source>ICON</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="547"/>
<location filename="../src/widget.cpp" line="772"/>
<source>Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="655"/>
<source>Uninstall</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="671"/>
<source>Site</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="744"/>
<location filename="../src/widget.ui" line="747"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This app is developed by community user,we give this tag to honor those who contribute to the Linux Ecology&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="750"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/community-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="763"/>
<location filename="../src/widget.ui" line="766"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Capable to Ubuntu 20.04&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="769"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/ubuntu-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="807"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/uos-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="782"/>
<location filename="../src/widget.ui" line="785"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Capable to deepin 20&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="313"/>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="306"/>
<source>Download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="238"/>
<source>Back to category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="191"/>
<source>Reading</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="225"/>
<source>Chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="211"/>
<source>Reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="415"/>
<source>The list is currently empty. Go and download some softwares!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="447"/>
<source>Open download directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="788"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/deepin-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="801"/>
<location filename="../src/widget.ui" line="804"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Capable to UOS home 20&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="820"/>
<location filename="../src/widget.ui" line="823"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This is a DTK5 app,which means it would have better effect on Deepin Desktop Environment&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="826"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/dtk-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="858"/>
<location filename="../src/widget.ui" line="861"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A deepin-wine5 app.If you are using ubuntu or other non-deepin distro,you should deploy deepin-wine5 by your self.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="877"/>
<location filename="../src/widget.ui" line="880"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;An Appimage to deb app.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="883"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/a2d-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="710"/>
<source>Share</source>
<translation type="unfinished">Spk share link</translation>
</message>
<message>
<location filename="../src/widget.ui" line="129"/>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="296"/>
<source>Home</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="122"/>
<source>Graphics</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="272"/>
<source>Network</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="839"/>
<location filename="../src/widget.ui" line="842"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A deepin-wine2 app.If you are using ubuntu or other non-deepin distro,you should deploy deepin-wine2 by your self.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="845"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/dwine2-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="864"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/dwine5-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="906"/>
<source>Contribute translation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="924"/>
<source>Info</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="931"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;An app store developed by community enthusiasts&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="970"/>
<source>Screenshots</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1133"/>
<source>Line Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1149"/>
<source>Choose Line:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1172"/>
<source>Refresh</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1214"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The role of the source server is to ensure that the software is updated, and supports the use of the apt tool to get the software. We usually prefer that you use the first line as the update source, which is generally the most stable. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1224"/>
<source>Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1231"/>
<source>Source Server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1251"/>
<source>Server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1272"/>
<source>Temp</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1295"/>
<source>Clean</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1305"/>
<source>Since the dictionary is at /tmp,It would be cleaned automatically when system reboot.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1318"/>
<source>Size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1332"/>
<source>Location/tmp/spark-store</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1353"/>
<source>About us</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1360"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;We are &lt;span style=&quot; font-weight:600;&quot;&gt;NOT&lt;/span&gt; the official team, just like you, we are just one of the many Linux/deepin system enthusiasts and users, we develop and run the &amp;quot;Spark Store&amp;quot;! &amp;quot;, is to bring the community together to share useful software, or to participate in development together, so that we all use the latest and greatest software. &lt;/p&gt;&lt;p&gt;We don&apos;t make a profit from this, all developers and maintainers don&apos;t get paid, and we rely on the community&apos;s donations to us for most of our expenses, which we are grateful for and which allow us not to spend too much energy worrying about funding. &lt;/p&gt;&lt;p&gt;Our service and software are free for everyone to use, communicate, and learn, but you must comply with local laws and regulations in the process of your use, otherwise any problems have nothing to do with us. &lt;/p&gt;&lt;p&gt;If any part of the store infringes your rights, please tell us &amp;lt;jifengshenmo@outlook.com&amp;gt; we will remove the infringing content as soon as possible. &lt;/p&gt;&lt;p&gt;If you&apos;d like to get involved with us too, whether you&apos;re involved in development, design, pitching or submitting work, we welcome you to join us. &lt;/p&gt;&lt;p&gt;QQ group:872690351&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="148"/>
<source>icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="374"/>
<source>about:blank</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="572"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1185"/>
<source>Take effect when restart</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.ui" line="1325"/>
<source>0B</source>
<translation type="unfinished">0B</translation>
</message>
<message>
<location filename="../src/widget.cpp" line="172"/>
<source>Spark Store</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="173"/>
<source>Search or enter spk://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="179"/>
<source>Submit App</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="180"/>
<source>Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="295"/>
<source>Not Exist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="127"/>
<location filename="../src/widget.cpp" line="349"/>
<location filename="../src/widget.cpp" line="353"/>
<location filename="../src/widget.cpp" line="362"/>
<location filename="../src/widget.cpp" line="366"/>
<source>Spark\ Store</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="563"/>
<location filename="../src/widget.cpp" line="769"/>
<source>Reinstall</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="536"/>
<source>Failed to get the name to the file to be downloaded.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="919"/>
<source>Updating, please wait...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="972"/>
<source>Apt has reported an error. Please use apt update in terminal to locate the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="978"/>
<source>Unknown error!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="1164"/>
<source>Yes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="1164"/>
<source>No</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="1165"/>
<source>Information for Contributors</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="1166"/>
<source>Currently the translation contribution is limited to English,
and you will be redirected to our Gitee repository at which you are
supposed to be creating pull requests to contribute app info
translations.
Tips:You can simply click the file to edit to create a flat pull request without create a full pull request
Tips:You can use github account to login to Gitee
Click yes to continue.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="998"/>
<source>Uninstall succeeded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="607"/>
<source>Relative apps Not Found!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="615"/>
<source>Request Error: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="1012"/>
<source>Temporary cache was cleaned</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/widget.cpp" line="1081"/>
<source>The URL has been copied to the clipboard</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>downloadlist</name>
<message>
<location filename="../src/downloadlist.ui" line="20"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.ui" line="50"/>
<source>icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.ui" line="82"/>
<source>TextLabel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.ui" line="106"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.ui" line="189"/>
<source>Waiting to download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.ui" line="240"/>
<source>Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.ui" line="259"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.ui" line="278"/>
<source>Info</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="24"/>
<source>Spark Store App Installer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="25"/>
<source>gdebi</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="26"/>
<source>deepin deb installer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="57"/>
<source>Downloaded, waiting to install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="82"/>
<source>Failed to download %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="84"/>
<source>Download FailedCheck Your Connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="94"/>
<source>Finished downloading %1, awaiting to install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="127"/>
<source>Installing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="174"/>
<source>Finish</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="178"/>
<source>Retry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="179"/>
<source>Error happened in dpkg progress , you can try it again</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="183"/>
<source>dpkg progress had been abortedyou can retry installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/downloadlist.cpp" line="205"/>
<source>Download canceled</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@@ -0,0 +1,860 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr">
<context>
<name>QLabel</name>
<message>
<source>Spark应用商店</source>
<translation type="vanished">Spark Store</translation>
</message>
<message>
<source>
GPL第三版开源</source>
<translation type="vanished">Un app store tier alimenté par la communauté Deepin
Spark Store est publié sous licence GPL V3
Nous sommes nés pour le changement.</translation>
</message>
<message>
<location filename="../main.cpp" line="37"/>
<source>Spark Store</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../main.cpp" line="45"/>
<source>&lt;span style=&apos;font-size:10pt;font-weight:60;&apos;&gt;An appstore powered by deepin community&lt;/span&gt;&lt;br/&gt;&lt;a href=&apos;https://www.spark-app.store/&apos;&gt;https://www.spark-app.store&lt;/a&gt;&lt;br/&gt;&lt;span style=&apos;font-size:12pt;&apos;&gt;Spark developers&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;Published under GPL V3</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SpkAppInfoLoaderThread</name>
<message>
<location filename="../workerthreads.cpp" line="25"/>
<source>Failed to download app info. Please check internet connection.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../workerthreads.cpp" line="53"/>
<source>PkgName: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../workerthreads.cpp" line="54"/>
<source>Version: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../workerthreads.cpp" line="56"/>
<source>Author: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../workerthreads.cpp" line="60"/>
<source>Official Site: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../workerthreads.cpp" line="64"/>
<source>Contributor: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../workerthreads.cpp" line="65"/>
<source>Update Time: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../workerthreads.cpp" line="66"/>
<source>Installed Size: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../workerthreads.cpp" line="95"/>
<source>Failed to load application icon.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Widget</name>
<message>
<location filename="../widget.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="52"/>
<source>background-color:#FFFFFF</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="67"/>
<source>Video</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="74"/>
<source>Download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="81"/>
<source>Tools</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="88"/>
<source>Beautify</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="95"/>
<source>Music</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="153"/>
<source>Development</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="160"/>
<source>Office</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="173"/>
<source>Back to category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="200"/>
<source>Reading</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="207"/>
<source>Others</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="217"/>
<source>Home</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="227"/>
<source>Graphics</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="234"/>
<source>Games</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="241"/>
<source>Chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="248"/>
<source>Network</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="293"/>
<source>Reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="383"/>
<source>The list is currently empty. Go and download some softwares!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="415"/>
<source>Open download directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="515"/>
<location filename="../widget.cpp" line="586"/>
<location filename="../widget.cpp" line="808"/>
<source>Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="528"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="568"/>
<source>ICON</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="623"/>
<source>Uninstall</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="639"/>
<source>Site</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="678"/>
<source>Share</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="712"/>
<location filename="../widget.ui" line="715"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This app is developed by community user,we give this tag to honor those who contribute to the Linux Ecology&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="718"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/community-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="731"/>
<location filename="../widget.ui" line="734"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Capable to Ubuntu 20.04&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="737"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/ubuntu-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="750"/>
<location filename="../widget.ui" line="753"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Capable to deepin 20&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="756"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/deepin-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="769"/>
<location filename="../widget.ui" line="772"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Capable to UOS home 20&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="775"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/uos-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="788"/>
<location filename="../widget.ui" line="791"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This is a DTK5 app,which means it would have better effect on Deepin Desktop Environment&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="794"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/dtk-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="807"/>
<location filename="../widget.ui" line="810"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A deepin-wine2 app.If you are using ubuntu or other non-deepin distro,you should deploy deepin-wine2 by your self.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="813"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/dwine2-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="826"/>
<location filename="../widget.ui" line="829"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A deepin-wine5 app.If you are using ubuntu or other non-deepin distro,you should deploy deepin-wine5 by your self.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="832"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/dwine5-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="845"/>
<location filename="../widget.ui" line="848"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;An Appimage to deb app.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="851"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/tags/a2d-small.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="874"/>
<source>Contribute translation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="892"/>
<source>Info</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="899"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;An app store developed by community enthusiasts&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="938"/>
<source>Screenshots</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1101"/>
<source>Line Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1117"/>
<source>Choose Line:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1140"/>
<source>Refresh</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1153"/>
<source>Take effect when restart</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1182"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The role of the source server is to ensure that the software is updated, and supports the use of the apt tool to get the software. We usually prefer that you use the first line as the update source, which is generally the most stable. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1192"/>
<source>Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1199"/>
<source>Source Server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1219"/>
<source>Server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1240"/>
<source>Temp</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1263"/>
<source>Clean</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1273"/>
<source>Since the dictionary is at /tmp,It would be cleaned automatically when system reboot.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1286"/>
<source>Size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1300"/>
<source>Location/tmp/spark-store</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1321"/>
<source>About us</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.ui" line="1328"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;We are &lt;span style=&quot; font-weight:600;&quot;&gt;NOT&lt;/span&gt; the official team, just like you, we are just one of the many Linux/deepin system enthusiasts and users, we develop and run the &amp;quot;Spark Store&amp;quot;! &amp;quot;, is to bring the community together to share useful software, or to participate in development together, so that we all use the latest and greatest software. &lt;/p&gt;&lt;p&gt;We don&apos;t make a profit from this, all developers and maintainers don&apos;t get paid, and we rely on the community&apos;s donations to us for most of our expenses, which we are grateful for and which allow us not to spend too much energy worrying about funding. &lt;/p&gt;&lt;p&gt;Our service and software are free for everyone to use, communicate, and learn, but you must comply with local laws and regulations in the process of your use, otherwise any problems have nothing to do with us. &lt;/p&gt;&lt;p&gt;If any part of the store infringes your rights, please tell us &amp;lt;jifengshenmo@outlook.com&amp;gt; we will remove the infringing content as soon as possible. &lt;/p&gt;&lt;p&gt;If you&apos;d like to get involved with us too, whether you&apos;re involved in development, design, pitching or submitting work, we welcome you to join us. &lt;/p&gt;&lt;p&gt;QQ group:872690351&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source></source>
<translatorcomment>Lecteurs vidéo et créateurs vidéo</translatorcomment>
<translation type="vanished">Vidéo</translation>
</message>
<message>
<source></source>
<translation type="vanished">Liste de téléchargement</translation>
</message>
<message>
<source></source>
<translation type="vanished">Outils</translation>
</message>
<message>
<source></source>
<translatorcomment>Thème des icônes et autres </translatorcomment>
<translation type="vanished">Thèmes</translation>
</message>
<message>
<source></source>
<translation type="vanished">Musique</translation>
</message>
<message>
<location filename="../widget.ui" line="114"/>
<source>icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<source></source>
<translation type="vanished">Développement</translation>
</message>
<message>
<source></source>
<translation type="vanished">Bureau</translation>
</message>
<message>
<source></source>
<translation type="vanished">Traduire</translation>
</message>
<message>
<source></source>
<translation type="vanished">Autres</translation>
</message>
<message>
<source></source>
<translation type="vanished">Accueil</translation>
</message>
<message>
<source></source>
<translation type="vanished">Images</translation>
</message>
<message>
<source></source>
<translation type="vanished">Jeux</translation>
</message>
<message>
<source></source>
<translation type="vanished">Chat</translation>
</message>
<message>
<source></source>
<translation type="vanished">Réseau</translation>
</message>
<message>
<location filename="../widget.ui" line="342"/>
<source>about:blank</source>
<translation type="unfinished"></translation>
</message>
<message>
<source></source>
<translation type="vanished">La liste de téléchargement est vide</translation>
</message>
<message>
<source></source>
<translation type="vanished">Ouvrir dans le gestionnaire de fichiers</translation>
</message>
<message>
<source></source>
<translation type="vanished">Installer</translation>
</message>
<message>
<source></source>
<translation type="vanished">Supprimer</translation>
</message>
<message>
<source></source>
<translation type="vanished">Site</translation>
</message>
<message>
<source></source>
<translation type="vanished">Nom</translation>
</message>
<message>
<location filename="../widget.ui" line="540"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source></source>
<translation type="vanished">Icon</translation>
</message>
<message>
<source></source>
<translation type="vanished">Partager</translation>
</message>
<message>
<source></source>
<translation type="vanished">Détails</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;magasin de logiciels développé par des passionnés de la communauté&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source></source>
<translation type="vanished">Captures d&apos;écran</translation>
</message>
<message>
<source>线</source>
<translation type="vanished">Line Settings</translation>
</message>
<message>
<source>线</source>
<translation type="vanished">Choisissez: </translation>
</message>
<message>
<source></source>
<translation type="vanished">Mise à jour</translation>
</message>
<message>
<source> </source>
<translation type="vanished">Redémarrez pour prendre effet</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;使apt工具获取软件使线&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Le rôle du serveur source est de s&apos;assurer que le logiciel est mis à jour et prend en charge l&apos;utilisation de l&apos;outil apt pour obtenir le logiciel. Nous préférons généralement que vous utilisiez la première ligne comme source de mise à jour, qui est généralement la plus stable. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source></source>
<translation type="vanished">Mettre à jour la source APT</translation>
</message>
<message>
<source></source>
<translation type="vanished">Source APT: </translation>
</message>
<message>
<source></source>
<translation type="vanished">serveur</translation>
</message>
<message>
<source></source>
<translation type="vanished">Temp</translation>
</message>
<message>
<source></source>
<translation type="vanished">Nettoyer</translation>
</message>
<message>
<source>/tmp下使</source>
<translation type="vanished">Étant donné que ce répertoire se trouve sous /tmp, même si vous ne l&apos;effacez pas manuellement, il sera effacé automatiquement au redémarrage du système.</translation>
</message>
<message>
<source></source>
<translation type="vanished">Taille: </translation>
</message>
<message>
<location filename="../widget.ui" line="1293"/>
<source>0B</source>
<translation type="unfinished">0B</translation>
</message>
<message>
<source>/tmp/spark-store</source>
<translation type="vanished">Emplacement:/tmp/spark-store</translation>
</message>
<message>
<source></source>
<translation type="vanished">À propos de nous</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Linux/deepin系统爱好者和用户之中的一员Spark应用商店&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;使使&lt;/p&gt;&lt;p&gt;&amp;lt;jifengshenmo@outlook.com&amp;gt;&lt;/p&gt;&lt;p&gt;稿&lt;/p&gt;&lt;p&gt;QQ872690351&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="vanished">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Nous ne sommes pas l&apos;équipe officielle, tout comme vous, nous ne sommes qu&apos;un des nombreux passionnés et utilisateurs de systèmes Linux / deepin, nous développons et gérons le &quot;Spark Store&quot;! &quot;, est de rassembler la communauté pour partager des logiciels utiles, ou pour participer au développement ensemble, afin que nous utilisions tous les derniers et meilleurs logiciels. &lt;/p&gt;&lt;p&gt; Nous n&apos;en tirons aucun profit, tous les développeurs et mainteneurs ne sont pas payés, et nous comptons sur les dons de la communauté pour la plupart de nos dépenses, dont nous sommes reconnaissants et qui nous permettent de ne pas dépenser trop d&apos;énergie se soucier du financement. &lt;/p&gt;&lt;p&gt;Notre service et nos logiciels sont gratuits pour tout le monde à utiliser, à communiquer et à apprendre, mais vous devez vous conformer aux lois et réglementations locales dans le processus de votre utilisation, sinon tout problème n&apos;a rien à voir avec nous. &lt;/p&gt;&lt;p&gt;Si une partie du magasin enfreint vos droits, veuillez nous en informer &amp;lt;jifengshenmo@outlook.com&amp;gt; nous retirerons le contenu en infraction dès que possible. &lt;/p&gt;&lt;p&gt;Si vous souhaitez également vous impliquer avec nous, que vous soyez impliqué dans le développement, la conception, le pitching ou la soumission de travaux, nous vous invitons à nous rejoindre. &lt;/p&gt;&lt;p&gt;QQ group:872690351&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../widget.cpp" line="113"/>
<location filename="../widget.cpp" line="319"/>
<location filename="../widget.cpp" line="323"/>
<location filename="../widget.cpp" line="332"/>
<location filename="../widget.cpp" line="336"/>
<source>Spark\ Store</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="157"/>
<source>Spark Store</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="158"/>
<source>Search or enter spk://</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="164"/>
<source>Submit App</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="165"/>
<source>Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="264"/>
<source>Not Exist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="530"/>
<source>Failed to download app info. Please check internet connection.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="560"/>
<source>PkgName: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="561"/>
<source>Version: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="563"/>
<source>Author: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="567"/>
<source>Official Site: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="571"/>
<source>Contributor: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="572"/>
<source>Update Time: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="573"/>
<source>Installed Size: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="582"/>
<location filename="../widget.cpp" line="693"/>
<location filename="../widget.cpp" line="805"/>
<source>Reinstall</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="621"/>
<source>Failed to load application icon.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="668"/>
<source>Failed to get the name to the file to be downloaded.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="716"/>
<source>Spark store could only process spk:// links for now. The search feature is coming soon!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="944"/>
<source>Updating, please wait...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="997"/>
<source>Apt has reported an error. Please use apt update in terminal to locate the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="1003"/>
<source>Unknown error!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="1023"/>
<source>Uninstall succeeded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="1037"/>
<source>Temporary cache was cleaned</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="1104"/>
<source>The URL has been copied to the clipboard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="1185"/>
<source>Yes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="1185"/>
<source>No</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="1186"/>
<source>Information for Contributors</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../widget.cpp" line="1187"/>
<source>Currently the translation contribution is limited to English,
and you will be redirected to our Gitee repository at which you are
supposed to be creating pull requests to contribute app info
translations.
Tips:You can simply click the file to edit to create a flat pull request without create a full pull request
Tips:You can use github account to login to Gitee
Click yes to continue.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>downloadlist</name>
<message>
<location filename="../downloadlist.ui" line="20"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.ui" line="50"/>
<source>icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.ui" line="82"/>
<source>TextLabel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.ui" line="106"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.ui" line="189"/>
<source>Waiting to download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.ui" line="240"/>
<source>Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.ui" line="259"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.ui" line="278"/>
<source>Info</source>
<translation type="unfinished"></translation>
</message>
<message>
<source></source>
<translation type="vanished">Nom</translation>
</message>
<message>
<source></source>
<translatorcomment>Attendez pour téléchargerd</translatorcomment>
<translation type="vanished">Attendez pour télécharger</translation>
</message>
<message>
<source></source>
<translation type="vanished">Installer</translation>
</message>
<message>
<source></source>
<translation type="vanished">Annuler</translation>
</message>
<message>
<source></source>
<translation type="vanished">Détail</translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="24"/>
<source>dpkg</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="25"/>
<source>gdebi</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="26"/>
<source>deepin deb installer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="55"/>
<source>Downloaded, waiting to install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="80"/>
<source>Failed to download %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="82"/>
<source>Download FailedCheck Your Connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="92"/>
<source>Finished downloading %1, awaiting to install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="125"/>
<source>Installing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="172"/>
<source>Finish</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="176"/>
<source>Retry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="177"/>
<source>Error happened in dpkg progress , you can try it again</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="181"/>
<source>dpkg progress had been abortedyou can retry installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../downloadlist.cpp" line="203"/>
<source>Download canceled</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More