摘要:本人希望能够每天更换桌面背景,又觉得常规手动步骤太过麻烦(从网上找图片->下载至本地->打开本地路径->右键设为壁纸),鉴于必应壁纸质量很高,索性写了一个程序自动实现其全部过程。(文末附本程序完整源代码的链接)
本文介绍了从网上爬取图片的实现,即Qt的http网络通讯的简单应用。后半部分调用本地系统API设置壁纸的过程,在其他文章另有介绍。

本人亲测平台包含: Win10,Ubuntu16.04LTS
运行环境:Qt5.7.0

第一次Get

通过QNetworkAccessManager类的get函数,爬下Bing主页全部源码至本地的一个txt文件中,便于后续的分析处理。小贴士:用QNetworkRequest类的setUrl函数设定url。

QNetworkAccessManager manager;//要用到connect,所以在头文件中提前声明此对象
void WallPaper::getWholeWebSource()
{
    showStatusTips(PICTURE_DOWNLOADING);//一个用于显示当前状态的函数,详见文末链接程序github源码
    QUrl url("http://cn.bing.com/");
    QNetworkRequest request;
    request.setUrl(url);
//将完成信号与我自定义的槽函数findPicUrl相连,用finished信号传递reply信息至槽函数中
    connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(findPicUrl(QNetworkReply*)));
//在连接了信号与槽之后,就可以进行get操作。get一旦完成,会自动触发finished信号,从而转至槽函数
    manager.get(request);
}

bing主页的源码分析

一旦对应第一步当中的get的finished信号被触发,则转至该槽函数findPicUrl,同时传递过来一个对象QNetworkReply *reply。通过reply->readAll()读取get到的内容。

分析源码,会发现壁纸URL并不完整,只是一个片段,尚无法通过它获取壁纸图片,需要自己再稍微拼接一下。因此,具体步骤为:1.通过一定的算法找到此片段;2.截取之;3.与http://cn.bing.com/拼接;

void WallPaper::findPicUrl(QNetworkReply *reply)
{
    disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(findPicUrl(QNetworkReply*)));
    if(reply->error() == QNetworkReply::NoError)
    {
        QByteArray webContent = reply->readAll();
        QFile file("测试文件.txt");//写成一个可阅读的txt文档,用于程序测试
        file.open(QIODevice::ReadWrite);
        file.write(webContent);
        file.close();
        QByteArray targetUrl = "/az/hprichbg/rb/";//目标url片段,通过它抓取到完整的壁纸网址
        int indexStart = -1;
        int indexEnd = -1;
        indexStart = webContent.indexOf(targetUrl);
        if(indexStart != -1)//若在获取到的网页源码中找到了所需url,则get之
        {
            qDebug("找到了壁纸的url:");
            webContent = webContent.mid(indexStart);//截取右侧
            indexEnd = webContent.indexOf("jpg");
            indexEnd = indexEnd + 2;
            QUrl url("http://cn.bing.com" + webContent.left(indexEnd + 1));//继续截取并拼接
            qDebug()<<url;
            emit urlFound(url);//发射了一个信号
        }
        else
            showStatusTips(URL_NOT_FOUND);//一个用于显示当前状态的函数,详见文末链接程序github源码
    }
    else
        showStatusTips(NETWORK_CONNECTION_FAILED, reply->errorString());
}

上文中你发现,由于findPicUrl函数也发射了一个信号,urlFound(url),因此在构造函数处加一个connect

//当最终计算出壁纸的目标url后,则转至downloadPic函数,正式开始下载壁纸
connect(this, SIGNAL(urlFound(QUrl)), this, SLOT(downloadPic(QUrl)));

第二次Get

当urlFound信号发射后,转至downloadPic函数,get的用法与前文一致

void WallPaper::downloadPic(QUrl url)
{
    QNetworkRequest request;
    request.setUrl(url);
    connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(savePic(QNetworkReply*)));
    manager.get(request);//用finished信号传递reply
}

IO操作,保存至本地

当返回信号finished时,将bing返回的reply信息,即图片,保存至本地

void WallPaper::savePic(QNetworkReply *reply)
{
    disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(savePic(QNetworkReply*)));
    if(reply->error() == QNetworkReply::NoError)
    {
        QPixmap picture;
//读取IO信息
        QByteArray picData = reply->readAll();

        if(!picture.loadFromData(picData, "JPG"))//用QPixmap定义的对象picture,读取从bing网站传过来的数据,格式为jpg的图片
        {
            showStatusTips(LOAD_FROM_DATA_FAILED, QString("%1").arg(picData.size()));
            return;
        }
//定义保存路径filepath
        filePath = QDir::homePath() + "/" + "Pictures/";
        QDir dir;
        dir.mkdir(filePath);
        filePath += QDateTime::currentDateTime().toString("yyyy-MM-dd") + "_Bing_txmy.jpg";
//save函数保存至本地
        if(!picture.save(filePath))
        {
            qDebug()<<"图片保存失败!";
        }
        else
        {//软件其他操作
            showStatusTips(WALLPAPER_READY);
            showStatusTips(COUNT_DOWN_TO_CLOSE);
            emit picSaved(filePath);

            DataCenter &dc = DataCenter::getInstance();
            QStringList paths = dc.getPicFilePaths();
            int index = paths.indexOf(filePath);
            if(index != -1)
                dc.setCurrentIndex(index);
        }
    }
    else
        showStatusTips(DOWNLOAD_FAILED, reply->errorString());
}

——————