记录学习利用 QWidget 类创建桌面壁纸程序

学习一短时间Qt后想做一个桌面壁纸的程序,由于没有学过 WinAPI 编程只会些Qt,所以取桌面的句柄和设置桌面的背景图只能先从网上寻找。功夫不负有心人还是找到比较满意的WinAPI的代码。然后根据这些代码学习了以下。下面是从网上寻找到的代码,我根据这些代码和在网上查资料做了些注释。毕竟基础不牢,感觉有很多的错误。

#include <windows.h>

/** 声明一个句柄类的 静态变量 */
static HWND g_workerw = 0;
/** 实现一个让 EnumWindows()使用的,获取我们要将程序设置到桌面位置的句柄回调函数 */
static BOOL CALLBACK EnumWndCallback(HWND tophandle, LPARAM topparamhandle)
{
    // 该函数的作用:由于我们要用的 窗口 是没有窗口名但我们知道我们所需要的窗口的类名是:"WorkerW",
    // 我们还知道我们所需要的窗口的子窗口的 子窗口的类名是:"SHELLDLL_DefView"
    // 我们还知道我们所需要的窗口的子窗口的窗口风格是 可见的窗口风格
    // 所以我们先寻找一个窗口的窗口风格是可见的窗口风格,再检测该窗口的子窗口有没有类名是 "SHELLDLL_DefView"的窗口,
    // 然后再检测该窗口的父窗口有没有类名是 "WorkerW" 的窗口,如果都吻合,那这个窗口就是我们所需要的窗口的子窗口,
    // 那变量 tophandle 存储的句柄所对应的窗口的 父窗口就是我们需要的窗口

    // 注意:topparamhandle形参之所以没有使用,是因为 EnumWindows函数并没有向
    // 存储获取我们要想窗口的句柄的变量或者是变量指针,而是传递了 0,0是无法存储数据的
    // 所以通过 EnumWndCallback() 函数获取的窗口句柄 只能通过 全局变量进行传递了

    // GetWindowLong() 函数可获取指定窗口的有关信息,
    // tophandle 目标窗口句柄也就是 EnumWindows() 枚举屏幕上的所有的顶层窗口的句柄
    // GWL_STYLE 的作用是获取传入窗口句柄的窗口的窗口风格
    long wflags = GetWindowLong(tophandle, GWL_STYLE);
    // 判断获取到的窗口风格是否是 可见的窗口风格
    // 判断方法是 获取的窗口风格 和 可见的窗口风格 WS_VISIBLE 进行 与(&) 操作
    // 如果 获取的窗口风格的二进制中的第 29 位是 1,
    // 那 进行 与(&) 操作 后的值 就是 WS_VISIBLE 所对应的值也就是不为 0 的值
    // 如果 获取的窗口风格的二进制中的第 29 位是 0,
    // 那 进行 与(&) 操作 后的值 就是 0
    // WS_VISIBLE(十六进制是 10000000, 二进制是 1 0000 0000 0000 0000 0000 0000 000 0)
    // 然后根据 ! 运算符进行反转
    if (!(wflags & WS_VISIBLE))
    {
        // 判断 获取的 窗口风格是否不是 可见的窗口风格
        // 如果 不是可见的窗口风格,就返回 TRUE, 就让 EnumWindows() 函数继续枚举新的窗口
        return TRUE;
    };

    // tophandle 是 EnumWindows() 枚举屏幕上的所有的顶层窗口的句柄
    // 利用 FindWindowEx() 寻找父窗口的句柄是变量 tophandle 存储的句柄的子窗口的类是 "SHELLDLL_DefView" 的窗口的句柄
    // 我们寻找的窗口的类名是 "SHELLDLL_Defview"
    HWND p = FindWindowEx(tophandle, 0, L"SHELLDLL_DefView", 0);
    // 判断 是否获取了句柄
    if (p != 0)
    {
        // Gets the WorkerW Window after the current one
        // 利用 FindWindowEx() 寻找子窗口的句柄是变量 tophandle 存储的句柄的父窗口的类是 "SHELLDLL_DefView" 的窗口的句柄
        // 我们寻找的窗口的类名是 "WorkerW" ,类名是 "WorkerW" 才是我们要用的窗口
        g_workerw = FindWindowEx(0, tophandle, L"WorkerW", 0);
        // 判断 g_workerw 中是否存储了我们想要的窗口枚举值
        if (0 != g_workerw)
        {
            // 传给 EnumWindows() 函数 FALSE
            // 停止 EnumWindows() 函数的枚举
            return FALSE;
        }
    }
    // 传给 EnumWindows() 函数 TRUE
    // 让 EnumWindows() 函数继续枚举
    return TRUE;
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    /** 利用 FindWindow() 函数获取一个 类是 "Progman" 窗口标题是"Program Manager"的窗口句柄 */
    HWND hwndPrgam = FindWindow(L"Progman", L"Program Manager");
    // 利用 SendMessageTimeout() 函数将指定的消息 0x052C, 发送 hwndPrgam 存储的枚举值的窗口
    // SMTO_NORMAL:调用线程等待函数返回时,不被阻止处理其他请求。 1000 为超时周期指定以毫秒为单位的持续时间
    SendMessageTimeout(hwndPrgam, 0x052C, 0,0,SMTO_NORMAL, 1000,0);
    // 上面的两行代码,根据查询的知识做了些注释但还是不清楚作用是什么,将上面的两行代码注释掉
    // 也可以实现 将程序设置到桌面壁纸上

    // 利用 EnumWindows() 枚举屏幕上的所有的顶层窗口,并将枚举的窗口传入 EnumWndCallback() 函数中
    // 也就是,不停的传窗口句柄给 EnumWndCallback() 函数直到找到需要的 窗口的句柄为止
    // 或枚举完所有的窗口也没有找到我们需要的窗口
    // EnumWindows() 函数第二个形参传入的是 0,意味着无法通过EnumWindows() 函数第二个形参获取
    // 我们想要的窗口句柄,只能通过 全局变量获取 EnumWndCallback() 函数中找到的我们需要的窗口句柄
    EnumWindows(EnumWndCallback, 0);

    // 判断是否获取了窗口句柄
    if (g_workerw == 0)
    {
        // 如果没有获取到句柄就结束该程序
        abort();
    }
    else
    {
        // 如果 获取了窗口句柄

        // 将该程序设置到桌面的壁纸层中
        SetParent((HWND)this->winId(), g_workerw);
    }

我用上面找到的代码,然后结合我自己学习的Qt的知识,可以实现将程序放到桌面壁纸上但是当关闭程序时,桌面壁纸还是程序上的图片,我用 Spy++ 查看,程序的句柄

已经没有了,所有我到 Windows10 个性化中将壁纸从新设置以下,发现桌面壁纸不在是程序上的图片了,所有我考虑是否可以用代码设置系统的壁纸。所以我在网上寻找到了下面的代码,并根据网上的知识做了些注释。

// 通过 setting 获取 Windows10系统的 "HKEY_CURRENT_USER\\Control Panel\\Desktop" 的注册表
    QSettings setting("HKEY_CURRENT_USER\\Control Panel\\Desktop", QSettings::NativeFormat);
    // 获取 注册表地址上名称是 "WallPaper" 的值,也就是 Windows10系统的
    // 当前桌面壁纸的路径
    QString src = setting.value("WallPaper",true).toString();
    // 将桌面壁纸的路径转换成宽字节,很重要,否则显示不了图片
    TCHAR *ptch = (TCHAR *)src.toStdWString().c_str();
    // 显示桌面壁纸  SPI_SETDESKWALLPAPER 表示要设置桌面墙纸,bmpfile 是要设置的图片的路径
    // SPIF_UPDATEINIFILE:把新的系统参数的设置内容写入用户配置文件
    SystemParametersInfo(SPI_SETDESKWALLPAPER, 0,ptch, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE );

使用上面的代码就可以实现从系统的注册表取出桌面壁纸的图片路径并又设置了一次桌面壁纸。

结合上面的两端代码,设置壁纸的核心就算搞定了,其他的可以用Qt 来写了。

为了验证自己的代码是否成功又添加了一些 Qt的代码,写成了下面写的Qt窗口类,毕竟只是验证一下,下面的代码还需要进行很多的完善。

头文件:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <QPainter> // Qt 的画家类
#include <QPixmap> // Qt 的 Pixmap 类

#include <windows.h> // Windows.h是一个最重要的头文件,它包含了其他Windows头文件
#include <QSettings> // QSettings类提供持久的独立于平台的应用程序设置。

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    /** 接收传过来的图片路径的槽函数 */
    void gengGaiTuPian(QString path);
    /** 接收传过来的图片路径的槽函数 */
    void guanBiChuangKou();

private:

    /** 图片路径 */
    QString tuPianLuJiang = "";

protected:
    /** 重写绘图事件 */
    void paintEvent(QPaintEvent *event);

};
#endif // WIDGET_H

源文件:

#include "widget.h"

/** 声明一个句柄类的 静态变量 */
static HWND g_workerw = 0;
/** 实现一个让 EnumWindows()使用的,获取我们要将程序设置到桌面位置的句柄回调函数 */
static BOOL CALLBACK EnumWndCallback(HWND tophandle, LPARAM topparamhandle)
{
    // 该函数的作用:由于我们要用的 窗口 是没有窗口名但我们知道我们所需要的窗口的类名是:"WorkerW",
    // 我们还知道我们所需要的窗口的子窗口的 子窗口的类名是:"SHELLDLL_DefView"
    // 我们还知道我们所需要的窗口的子窗口的窗口风格是 可见的窗口风格
    // 所以我们先寻找一个窗口的窗口风格是可见的窗口风格,再检测该窗口的子窗口有没有类名是 "SHELLDLL_DefView"的窗口,
    // 然后再检测该窗口的父窗口有没有类名是 "WorkerW" 的窗口,如果都吻合,那这个窗口就是我们所需要的窗口的子窗口,
    // 那变量 tophandle 存储的句柄所对应的窗口的 父窗口就是我们需要的窗口

    // 注意:topparamhandle形参之所以没有使用,是因为 EnumWindows函数并没有向
    // 存储获取我们要想窗口的句柄的变量或者是变量指针,而是传递了 0,0是无法存储数据的
    // 所以通过 EnumWndCallback() 函数获取的窗口句柄 只能通过 全局变量进行传递了

    // GetWindowLong() 函数可获取指定窗口的有关信息,
    // tophandle 目标窗口句柄也就是 EnumWindows() 枚举屏幕上的所有的顶层窗口的句柄
    // GWL_STYLE 的作用是获取传入窗口句柄的窗口的窗口风格
    long wflags = GetWindowLong(tophandle, GWL_STYLE);
    // 判断获取到的窗口风格是否是 可见的窗口风格
    // 判断方法是 获取的窗口风格 和 可见的窗口风格 WS_VISIBLE 进行 与(&) 操作
    // 如果 获取的窗口风格的二进制中的第 29 位是 1,
    // 那 进行 与(&) 操作 后的值 就是 WS_VISIBLE 所对应的值也就是不为 0 的值
    // 如果 获取的窗口风格的二进制中的第 29 位是 0,
    // 那 进行 与(&) 操作 后的值 就是 0
    // WS_VISIBLE(十六进制是 10000000, 二进制是 1 0000 0000 0000 0000 0000 0000 000 0)
    // 然后根据 ! 运算符进行反转
    if (!(wflags & WS_VISIBLE))
    {
        // 判断 获取的 窗口风格是否不是 可见的窗口风格
        // 如果 不是可见的窗口风格,就返回 TRUE, 就让 EnumWindows() 函数继续枚举新的窗口
        return TRUE;
    };

    // tophandle 是 EnumWindows() 枚举屏幕上的所有的顶层窗口的句柄
    // 利用 FindWindowEx() 寻找父窗口的句柄是变量 tophandle 存储的句柄的子窗口的类是 "SHELLDLL_DefView" 的窗口的句柄
    // 我们寻找的窗口的类名是 "SHELLDLL_Defview"
    HWND p = FindWindowEx(tophandle, 0, L"SHELLDLL_DefView", 0);
    // 判断 是否获取了句柄
    if (p != 0)
    {
        // Gets the WorkerW Window after the current one
        // 利用 FindWindowEx() 寻找子窗口的句柄是变量 tophandle 存储的句柄的父窗口的类是 "SHELLDLL_DefView" 的窗口的句柄
        // 我们寻找的窗口的类名是 "WorkerW" ,类名是 "WorkerW" 才是我们要用的窗口
        g_workerw = FindWindowEx(0, tophandle, L"WorkerW", 0);
        // 判断 g_workerw 中是否存储了我们想要的窗口枚举值
        if (0 != g_workerw)
        {
            // 传给 EnumWindows() 函数 FALSE
            // 停止 EnumWindows() 函数的枚举
            return FALSE;
        }
    }
    // 传给 EnumWindows() 函数 TRUE
    // 让 EnumWindows() 函数继续枚举
    return TRUE;
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    /** 利用 FindWindow() 函数获取一个 类是 "Progman" 窗口标题是"Program Manager"的窗口句柄 */
    HWND hwndPrgam = FindWindow(L"Progman", L"Program Manager");
    // 利用 SendMessageTimeout() 函数将指定的消息 0x052C, 发送 hwndPrgam 存储的枚举值的窗口
    // SMTO_NORMAL:调用线程等待函数返回时,不被阻止处理其他请求。 1000 为超时周期指定以毫秒为单位的持续时间
    SendMessageTimeout(hwndPrgam, 0x052C, 0,0,SMTO_NORMAL, 1000,0);
    // 上面的两行代码,根据查询的知识做了些注释但还是不清楚作用是什么,将上面的两行代码注释掉
    // 也可以实现 将程序设置到桌面壁纸上

    // 利用 EnumWindows() 枚举屏幕上的所有的顶层窗口,并将枚举的窗口传入 EnumWndCallback() 函数中
    // 也就是,不停的传窗口句柄给 EnumWndCallback() 函数直到找到需要的 窗口的句柄为止
    // 或枚举完所有的窗口也没有找到我们需要的窗口
    // EnumWindows() 函数第二个形参传入的是 0,意味着无法通过EnumWindows() 函数第二个形参获取
    // 我们想要的窗口句柄,只能通过 全局变量获取 EnumWndCallback() 函数中找到的我们需要的窗口句柄
    EnumWindows(EnumWndCallback, 0);

    // 判断是否获取了窗口句柄
    if (g_workerw == 0)
    {
        // 如果没有获取到句柄就结束该程序
        abort();
    }
    else
    {
        // 如果 获取了窗口句柄

        // 将该程序设置到桌面的壁纸层中
        SetParent((HWND)this->winId(), g_workerw);
    }

    // 给窗口去掉边框
    this-> setWindowFlags(Qt::FramelessWindowHint | windowFlags() );
    // 设置窗口全屏显示
    this->showFullScreen();
    // 为 tuPianLuJiang 赋值
    tuPianLuJiang = "://BeiJing.jpg";
}

Widget::~Widget()
{
}

void Widget::gengGaiTuPian(QString path)
{
    // 判断 path 是否不为空
    if (!path.isEmpty())
    {
        tuPianLuJiang = path;
        // 通过 update() 函数调用 绘图事件
        this->update();
    }
}

void Widget::guanBiChuangKou()
{
    // 隐藏该窗口
    this->hide();
    // 通过 setting 获取 Windows系统的 "HKEY_CURRENT_USER\\Control Panel\\Desktop" 的注册表
    QSettings setting("HKEY_CURRENT_USER\\Control Panel\\Desktop", QSettings::NativeFormat);
    // 获取 注册表地址上名称是 "WallPaper" 的值,也就是 Windows10系统的
    // 当前桌面壁纸的路径
    QString src = setting.value("WallPaper",true).toString();
    // 将桌面壁纸的路径转换成宽字节,很重要,否则显示不了图片
    TCHAR *ptch = (TCHAR *)src.toStdWString().c_str();
    // 显示桌面壁纸  SPI_SETDESKWALLPAPER 表示要设置桌面墙纸,bmpfile 是要设置的图片的路径
    // SPIF_UPDATEINIFILE:把新的系统参数的设置内容写入用户配置文件
    SystemParametersInfo(SPI_SETDESKWALLPAPER, 0,ptch, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE );
    // delete 该类
    // 使用完该类后,delete 该类,句柄才会消失,当是主窗口时不用 delete
    delete this;
}

void Widget::paintEvent(QPaintEvent *event)
{
    // 实例化 画家的对象
    // 需要先添加 #include <QPainter> 头文件
    // this 是设置在哪里画画是指定绘图的设备不是设置父类
    QPainter painter(this);

    // 实例化一个 QPixmap,并传入本地的一张图片
    QPixmap pixmap(tuPianLuJiang);
    // 将图片 通过画家类的实例绘制的 窗口上
    painter.drawPixmap(0, 0, this->width(), this->height(), pixmap);
}

由于对编程的爱好,学习了一段时间的编程,虽然学习的时间不短,但基础实在是太弱了。第一次写博客,这篇博客算是自己学习其他知识的一点总结。完善这个程序后再学习其他的Qt 知识。

(0)

相关推荐

  • 桌面窗口的特殊性

    桌面窗口是一个特殊的窗口 在Win32中,有一个比较特殊的API叫做GetDesktopWindow,我看到很多人都会误解它的使用方法. 举个例子,一些和桌面外壳相关的函数会接收一个窗口句柄用来进行和 ...

  • 【originOS】OriginOS首发评测:回归本源,改变一切

    [[[前言:被智能手机绑架的体验,是时候该改变了]]] 智能手机是什么?它是居家.出行.购物必不可少的随身伴侣:是与家人.与同事.与陌生人保持沟通.消弭误解的重要工具:同时还是闲暇时追剧.游戏,获得轻 ...

  • 来看看大神们是如何DIY手机桌面的

    试问哪个Android用户没有折腾过自己的手机桌面?不过虽然绝大多数Android厂商都会在手机中内置不少主题以供选择,但是很多时候这些主题的审美让人无力吐槽,多数可能都是下面这幅模样. 这个时候我们 ...

  • 外网搞来的,终于有了!

    壁纸软件我推荐过不少, 静态的 cuto. Wallpapers, 动态的 火萤视频桌面 大家也一直都在说 Bing 壁纸好看 所以今天说说 Bing 新出的壁纸客户端, 安装之后没有正常的软件界面, ...

  • 火山PC中文编程学习:013如何创建控制台应用程序

    控制台命令程序这里简单理解就是类似黑色的DOS窗口的程序,在火山中创建程序的时候,选择创建控制台应用程序即可. 1.控制台应用程序先学习两个代码,标注输入和标注输出就行了,具体看代码,很简单.

  • 深度学习在图像分类中的应用ーー利用 Pytorch 从零开始创建 CNN

    重磅干货,第一时间送达 推荐阅读 31个Python实战项目教你掌握图像处理,PDF开放下载 opencv_contrib扩展模块中文教程pdf,限时领取 引言 本文将解释一个卷积神经网络(CNN)的 ...

  • 11张Excel快捷键桌面壁纸,非常实用,请查收!

    Hi,大家好,我是小雨.今天小雨为大家分享一组非常实用的Excel快捷键桌面壁纸.先来看一下我的电脑桌面设置了这款Excel快捷键壁纸之后,是什么样的. 有没有觉得既简洁又实用,还很上档次呢? 之前小 ...

  • 第5集丨认识和设置电脑桌面 桌面壁纸的设置

    第5集丨认识和设置电脑桌面 桌面壁纸的设置

  • 手绘东方古典女子桌面壁纸 23张 (天堂图片网)

    中国历代不乏"沉鱼落雁.闭月羞花"的美女,"翩若惊鸿,婉若游龙,荣曜秋菊,华茂春松.仿佛兮若轻云之蔽月,飘飖兮若流风之回雪"<洛神赋>将东方古典女子 ...

  • 龙飞虎语录 记录学习

    龙飞虎经典语录                衡量能否职业炒股很重要的一条就是能控制回撤幅度.小时候我们都学过龟兔赛跑的故事,进入股市,比的就是稳定性.不怕你跑得慢,就怕你老跑回头路.         ...

  • 如何设置魅族18Pro手机桌面壁纸

    小伙伴们你们知道魅族18pro如何设置桌面壁纸,今天小编很乐意与大家分享魅族18pro设置桌面壁纸方法,感兴趣的可以来了解. 1.长按桌面空白处,进入主屏幕编辑状态. 2.点击更换壁纸. 3.选择喜欢 ...

  • revit空心放样剪切不了实体怎么办?关于Revit在相同路径下利用放样曲线创建空心形状剪切实心形状

    来源丨益埃毕教育 在Revit软件中,样条曲线作为绘制工具之一,在很多时候是会使用到的.那么怎么在相同路径下利用放样曲线创建空心形状剪切实心形状?下面进行详细讲解. 01.选择公制常规模型族样板新建族 ...

  • Java学习——38、类的抽象性

    抽象是研究复杂对象的基本方法,从复杂对象中抽象出本质特征,忽略次要细节,使某些信息和实现对于使用者不可见.抽象层次越高,其软件利用程序越高. 1.抽象类 使用关键字abstract声明的类为抽象类. ...