星火商店搜索功能

* 更换搜索服务器域名为星火的域名
* 更新搜索服务器为线上服务器
* 完成搜索功能
* 解决搜索结果图标锯齿问题
* 更新appitem的样式
* 完成应用搜索列表的滚动问题
* 合并master分支
* 添加一些文件到忽略列表
* 更新项目结构
* 更新搜索列表UI
* 添加 QtNetworkService库
This commit is contained in:
枯叶蚊 2020-12-05 16:06:52 +08:00 committed by zty199
parent 62f0dd097c
commit 14e3e7f9a2
84 changed files with 1973 additions and 176 deletions

5
.gitignore vendored

@ -1,3 +1,4 @@
spark-store.pro.user*
*.pro.user*
build/
.vscode/
.vscode/
Lib/

@ -34,18 +34,18 @@
<file>icons/refresh-page.svg</file>
</qresource>
<qresource prefix="/">
<file>Logo-Spark.png</file>
<file>big_image.cpp</file>
<file>big_image.h</file>
<file>downloadlist.cpp</file>
<file>downloadlist.h</file>
<file>image_show.cpp</file>
<file>image_show.h</file>
<file>main.cpp</file>
<file>progressload.cpp</file>
<file>progressload.h</file>
<file>widget.cpp</file>
<file>widget.h</file>
<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>

Before

(image error) Size: 3.3 KiB

After

(image error) Size: 3.3 KiB

Before

(image error) Size: 3.3 KiB

After

(image error) Size: 3.3 KiB

Before

(image error) Size: 1.1 KiB

After

(image error) Size: 1.1 KiB

Before

(image error) Size: 1.1 KiB

After

(image error) Size: 1.1 KiB

Before

(image error) Size: 523 B

After

(image error) Size: 523 B

Before

(image error) Size: 553 B

After

(image error) Size: 553 B

Before

(image error) Size: 3.3 KiB

After

(image error) Size: 3.3 KiB

Before

(image error) Size: 3.3 KiB

After

(image error) Size: 3.3 KiB

Before

(image error) Size: 865 B

After

(image error) Size: 865 B

Before

(image error) Size: 895 B

After

(image error) Size: 895 B

Before

(image error) Size: 929 B

After

(image error) Size: 929 B

Before

(image error) Size: 959 B

After

(image error) Size: 959 B

Before

(image error) Size: 3.4 KiB

After

(image error) Size: 3.4 KiB

Before

(image error) Size: 3.5 KiB

After

(image error) Size: 3.5 KiB

Before

(image error) Size: 1.2 KiB

After

(image error) Size: 1.2 KiB

Before

(image error) Size: 1.3 KiB

After

(image error) Size: 1.3 KiB

Before

(image error) Size: 1.3 KiB

After

(image error) Size: 1.3 KiB

Before

(image error) Size: 1.3 KiB

After

(image error) Size: 1.3 KiB

Before

(image error) Size: 810 B

After

(image error) Size: 810 B

Before

(image error) Size: 828 B

After

(image error) Size: 828 B

Before

(image error) Size: 2.0 KiB

After

(image error) Size: 2.0 KiB

Before

(image error) Size: 2.0 KiB

After

(image error) Size: 2.0 KiB

Before

(image error) Size: 1.9 KiB

After

(image error) Size: 1.9 KiB

Before

(image error) Size: 1.9 KiB

After

(image error) Size: 1.9 KiB

Before

(image error) Size: 1.2 KiB

After

(image error) Size: 1.2 KiB

Before

(image error) Size: 2.6 KiB

After

(image error) Size: 2.6 KiB

Before

(image error) Size: 2.4 KiB

After

(image error) Size: 2.4 KiB

Before

(image error) Size: 2.4 KiB

After

(image error) Size: 2.4 KiB

Before

(image error) Size: 1.9 KiB

After

(image error) Size: 1.9 KiB

Before

(image error) Size: 1.8 KiB

After

(image error) Size: 1.8 KiB

Before

(image error) Size: 494 B

After

(image error) Size: 494 B

Before

(image error) Size: 1.9 KiB

After

(image error) Size: 1.9 KiB

Before

(image error) Size: 16 KiB

After

(image error) Size: 16 KiB

Before

(image error) Size: 248 KiB

After

(image error) Size: 248 KiB

Before

(image error) Size: 1.6 KiB

After

(image error) Size: 1.6 KiB

Before

(image error) Size: 32 KiB

After

(image error) Size: 32 KiB

Before

(image error) Size: 13 KiB

After

(image error) Size: 13 KiB

Before

(image error) Size: 1.9 KiB

After

(image error) Size: 1.9 KiB

Before

(image error) Size: 4.3 KiB

After

(image error) Size: 4.3 KiB

Before

(image error) Size: 954 B

After

(image error) Size: 954 B

Before

(image error) Size: 5.7 KiB

After

(image error) Size: 5.7 KiB

Before

(image error) Size: 1.1 KiB

After

(image error) Size: 1.1 KiB

Before

(image error) Size: 1.7 KiB

After

(image error) Size: 1.7 KiB

Before

(image error) Size: 2.0 KiB

After

(image error) Size: 2.0 KiB

Before

(image error) Size: 12 KiB

After

(image error) Size: 12 KiB

Before

(image error) Size: 83 KiB

After

(image error) Size: 83 KiB

Before

(image error) Size: 873 B

After

(image error) Size: 873 B

Before

(image error) Size: 1.1 KiB

After

(image error) Size: 1.1 KiB

13
assets/tags/uos.svg Normal 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

(image error) Size: 873 B

7
spark-store-project.pro Normal file

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

96
src/appitem.cpp Normal file

@ -0,0 +1,96 @@
#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);
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);
}

43
src/appitem.h Normal file

@ -0,0 +1,43 @@
#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();
void setTitle(QString title);
void setDescription(QString description);
void setIcon(QString icon);
void setUrl(QString url);
protected:
void mousePressEvent(QMouseEvent *event) override;
signals:
void clicked(QUrl url);
public slots:
void downloadIcon(QString icon);
void loadIcon(QPixmap pic);
private:
Ui::AppItem *ui;
QString m_title;
QString m_description;
QString m_icon;
QString m_url;
};
#endif // APPITEM_H

160
src/appitem.ui Normal 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>

223
src/flowlayout.cpp Normal 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 0;
}
//! [5]
//! [6]
Qt::Orientations FlowLayout::expandingDirections() const
{
return 0;
}
//! [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, 0, pw);
} else {
return static_cast<QLayout *>(parent)->spacing();
}
}
//! [12]

88
src/flowlayout.h Normal 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();
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

@ -4,6 +4,8 @@
#include <widget.h>
#include <QTranslator>
#include <DAboutDialog>
#include "appitem.h"
DWIDGET_USE_NAMESPACE
int main(int argc, char *argv[])
{

@ -26,43 +26,52 @@ DEFINES += QT_DEPRECATED_WARNINGS
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += main.cpp\
appitem.cpp \
widget.cpp \
downloadlist.cpp \
image_show.cpp \
big_image.cpp \
progressload.cpp \
flowlayout.cpp \
workerthreads.cpp
HEADERS += \
appitem.h \
widget.h \
downloadlist.h \
image_show.h \
big_image.h \
progressload.h \
flowlayout.h \
workerthreads.h
FORMS += \
appitem.ui \
widget.ui \
downloadlist.ui
RESOURCES += \
icons.qrc
../assets/icons.qrc
DISTFILES += \
tags/a2d-small.png \
tags/a2d.png \
tags/community-small.png \
tags/community.png \
tags/deepin-small.png \
tags/dtk-small.png \
tags/ubuntu-small.png \
tags/ubuntu.png \
tags/uos-small.png \
tags/community.svg \
tags/deepin.svg \
tags/logo_icon.svg \
tags/uos.svg
../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 = ../trans/spark-store_en.ts \
../trans/spark-store_zh_CN.ts
../trans/spark-store_fr.ts\
DEFINES += QT_APP_DEBUG
include(../third-party/QtNetworkService/QtNetworkService.pri)
TRANSLATIONS = ./trans/spark-store_en.ts \
./trans/spark-store_zh_CN.ts \
./trans/spark-store_fr.ts

@ -9,8 +9,8 @@
#include <fstream>
#include <QDir>
#include <QProcess>
#include <QJsonDocument>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QByteArray>
#include <QPixmap>
@ -29,8 +29,13 @@
#include <DApplication>
#include <DGuiApplicationHelper>
#include <QPushButton>
#include "HttpClient.h"
#include "appitem.h"
#include "flowlayout.h"
DWIDGET_USE_NAMESPACE
Widget::Widget(DBlurEffectWidget *parent) :
DBlurEffectWidget(parent),
ui(new Ui::Widget)
@ -42,6 +47,8 @@ Widget::Widget(DBlurEffectWidget *parent) :
m_loadweb=ui->progressload;
m_loadweb->show();
httpClient = new AeaQt::HttpClient;
connect(ui->menu_main,&QPushButton::clicked,[=](){Widget::chooseLeftMenu(0);});
connect(ui->menu_network,&QPushButton::clicked,[=](){Widget::chooseLeftMenu(1);});
connect(ui->menu_chat,&QPushButton::clicked,[=](){Widget::chooseLeftMenu(2);});
@ -200,6 +207,9 @@ void Widget::initUI()
left_list[13]=ui->menu_download;
ui->label_show->hide();
// 搜索列表页
applist_grid = new FlowLayout;
}
void Widget::initConfig()
@ -302,7 +312,6 @@ void Widget::setTheme(bool isDark,QColor color)
if(ui->stackedWidget->currentIndex()==0){
chooseLeftMenu(nowMenu);
}
}
DTitlebar* Widget::getTitlebar()
@ -713,12 +722,69 @@ void Widget::searchApp(QString text)
if(text.left(6)=="spk://"){
openUrl(text);
}else {
sendNotification(tr("Spark store could only process spk:// links for now. The search feature is coming soon!"));
// sendNotification(tr("Spark store could only process spk:// links for now. The search feature is coming soon!"));
// ui->webView->setUrl(QUrl("http://www.baidu.com/s?wd="+text));//这东西对接百度
// ui->stackedWidget->setCurrentIndex(0);
// 关键字搜索处理
httpClient->get("http://search.deepinos.org.cn/appinfo/search")
.header("content-type", "application/json")
.queryParam("keyword", text)
.onResponse([this](QByteArray result) {
auto json = QJsonDocument::fromJson(result).array();
if (json.empty()) {
qDebug() << "搜索不到相关应用!";
sendNotification(tr("Not found relative App!"));
return;
}
displaySearchApp(json);
})
.onError([](QString errorStr) {
qDebug() << "请求出错:" << errorStr;
sendNotification(QString("请求出错:%1").arg(errorStr));
})
.timeout(10 * 1000)
.exec();
}
}
/**
* @brief APP信息
*/
void Widget::displaySearchApp(QJsonArray array)
{
ui->stackedWidget->setCurrentIndex(4);
// 清除原有的搜索结果
QLayoutItem *item;
while ((item = applist_grid->takeAt(0)) != nullptr) {
item->widget()->disconnect();
delete item->widget();
delete item;
}
item = nullptr;
for(int i = 0; i < array.size(); i++)
{
QJsonObject appInfo = array.at(i).toObject();
AppItem *appItem = new AppItem(this);
QString url = QString("spk://store/%1/%2")
.arg(appInfo["category_slug"].toString())
.arg(appInfo["pkgname"].toString());
appItem->setTitle(appInfo["name"].toString());
appItem->setDescription(appInfo["more"].toString());
appItem->setIcon(appInfo["icon"].toString());
appItem->setUrl(url);
applist_grid->addWidget(appItem);
qDebug() << "应用链接为:" << url;
connect(appItem, &AppItem::clicked, this, &Widget::openUrl);
}
ui->applist_scrollarea->widget()->setLayout(applist_grid);
qDebug() << "显示结果了吗????喵喵喵";
}
void Widget::httpReadyRead()
{
if(file)

@ -13,6 +13,7 @@
#include <QFutureWatcher>
#include <QToolButton>
#include <QTimer>
#include <QJsonArray>
#include <QFontDatabase>
@ -36,6 +37,11 @@ class Widget;
}
class FlowLayout;
namespace AeaQt {
class HttpClient;
}
class Widget : public DBlurEffectWidget
{
@ -73,6 +79,8 @@ private slots:
void sltAppinfoScreenshot(QPixmap *picture, int index);
void sltAppinfoFinish();
void displaySearchApp(QJsonArray array); // 展示搜索的APP信息
void on_pushButton_download_clicked();
void on_pushButton_return_clicked();
void on_comboBox_server_currentIndexChanged(const QString &arg1);
@ -145,6 +153,8 @@ private:
QList<image_show*> label_screen;
SpkAppInfoLoaderThread appinfoLoadThread;
AeaQt::HttpClient *httpClient;
FlowLayout *applist_grid;
};
#endif // WIDGET_H

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>1053</width>
<height>674</height>
<height>711</height>
</rect>
</property>
<property name="windowTitle">
@ -29,6 +29,38 @@
<property name="spacing">
<number>0</number>
</property>
<item row="1" column="1">
<widget class="ProgressLoad" name="progressload" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>3</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>3</height>
</size>
</property>
<property name="toolTipDuration">
<number>-1</number>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="DTitlebar" name="titlebar" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="4">
<widget class="QWidget" name="widget_menuList" native="true">
<property name="minimumSize">
@ -61,33 +93,21 @@
<property name="rightMargin">
<number>5</number>
</property>
<item row="6" column="0" colspan="6">
<widget class="QPushButton" name="menu_video">
<property name="text">
<string>Video</string>
<item row="1" column="0">
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="16" column="0" colspan="6">
<widget class="QPushButton" name="menu_download">
<property name="text">
<string>Download</string>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</widget>
</item>
<item row="12" column="0" colspan="6">
<widget class="QPushButton" name="menu_system">
<property name="text">
<string>Tools</string>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item row="13" column="0" colspan="6">
<widget class="QPushButton" name="menu_theme">
<property name="text">
<string>Beautify</string>
</property>
</widget>
</spacer>
</item>
<item row="5" column="0" colspan="6">
<widget class="QPushButton" name="menu_music">
@ -96,6 +116,20 @@
</property>
</widget>
</item>
<item row="7" column="0" colspan="6">
<widget class="QPushButton" name="menu_photo">
<property name="text">
<string>Graphics</string>
</property>
</widget>
</item>
<item row="11" column="0" colspan="6">
<widget class="QPushButton" name="menu_dev">
<property name="text">
<string>Development</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="icon">
<property name="minimumSize">
@ -115,22 +149,6 @@
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_8">
<property name="orientation">
@ -147,10 +165,30 @@
</property>
</spacer>
</item>
<item row="11" column="0" colspan="6">
<widget class="QPushButton" name="menu_dev">
<item row="0" column="2">
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="12" column="0" colspan="6">
<widget class="QPushButton" name="menu_system">
<property name="text">
<string>Development</string>
<string>Tools</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="6">
<widget class="QPushButton" name="menu_read">
<property name="text">
<string>Reading</string>
</property>
</widget>
</item>
@ -161,6 +199,33 @@
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pushButton_refresh">
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Reload</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../assets/icons.qrc">
<normaloff>:/icons/icons/refresh-page.svg</normaloff>:/icons/icons/refresh-page.svg</iconset>
</property>
</widget>
</item>
<item row="4" column="0" colspan="6">
<widget class="QPushButton" name="menu_chat">
<property name="text">
<string>Chat</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="pushButton_return">
<property name="maximumSize">
@ -176,7 +241,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<iconset resource="../assets/icons.qrc">
<normaloff>:/icons/icons/category_active.svg</normaloff>:/icons/icons/category_active.svg</iconset>
</property>
</widget>
@ -194,10 +259,24 @@
</property>
</spacer>
</item>
<item row="10" column="0" colspan="6">
<widget class="QPushButton" name="menu_read">
<item row="13" column="0" colspan="6">
<widget class="QPushButton" name="menu_theme">
<property name="text">
<string>Reading</string>
<string>Beautify</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="6">
<widget class="QPushButton" name="menu_network">
<property name="text">
<string>Network</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="6">
<widget class="QPushButton" name="menu_game">
<property name="text">
<string>Games</string>
</property>
</widget>
</item>
@ -221,31 +300,17 @@
</property>
</widget>
</item>
<item row="7" column="0" colspan="6">
<widget class="QPushButton" name="menu_photo">
<item row="16" column="0" colspan="6">
<widget class="QPushButton" name="menu_download">
<property name="text">
<string>Graphics</string>
<string>Download</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="6">
<widget class="QPushButton" name="menu_game">
<item row="6" column="0" colspan="6">
<widget class="QPushButton" name="menu_video">
<property name="text">
<string>Games</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="6">
<widget class="QPushButton" name="menu_chat">
<property name="text">
<string>Chat</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="6">
<widget class="QPushButton" name="menu_network">
<property name="text">
<string>Network</string>
<string>Video</string>
</property>
</widget>
</item>
@ -268,39 +333,6 @@
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pushButton_refresh">
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Reload</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/icons/refresh-page.svg</normaloff>:/icons/icons/refresh-page.svg</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -333,11 +365,11 @@
<number>0</number>
</property>
<item>
<widget class="QWebEngineView" name="webEngineView">
<widget class="QWebEngineView" name="webEngineView" native="true">
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
<property name="url">
<property name="url" stdset="0">
<url>
<string>about:blank</string>
</url>
@ -457,8 +489,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>889</width>
<height>849</height>
<width>886</width>
<height>865</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_17">
@ -952,8 +984,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>851</width>
<height>326</height>
<width>840</width>
<height>318</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
@ -1082,8 +1114,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>889</width>
<height>812</height>
<width>886</width>
<height>921</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_23">
@ -1354,38 +1386,30 @@
</item>
</layout>
</widget>
</widget>
</item>
<item row="0" column="1">
<widget class="DTitlebar" name="titlebar" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="ProgressLoad" name="progressload" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>3</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>3</height>
</size>
</property>
<property name="toolTipDuration">
<number>-1</number>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QWidget" name="applist_page">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QScrollArea" name="applist_scrollarea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="applist_scrollAreaWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>881</width>
<height>659</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -1416,7 +1440,7 @@
</customwidget>
</customwidgets>
<resources>
<include location="icons.qrc"/>
<include location="../assets/icons.qrc"/>
</resources>
<connections/>
</ui>

@ -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);
}

@ -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

@ -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;
}

@ -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

@ -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()
{
}

@ -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

@ -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.

@ -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
}

@ -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

@ -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>