作者归档:myCode

Windows 10 键盘 ALT 键与 WIN 键颠倒

最近重新装了家里台式机电脑系统,但是发现键盘左侧和右侧的 ALT 与 WIN 键功能都颠倒了,搜索了很久资料也没有找到为什么会出现这种情况,但是解决办法还是有的。导入以下注册表即可让 ALT 和 WIN 键对换功能

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]
"Scancode Map"=hex:00,00,00,00,00,00,00,00,03,00,00,00,38,00,5B,E0,5B,E0,38,00,00,00,00,00

以上注册表内容来自一篇文章,我根据这篇文章修改的具体内容,请参考:https://www.qiansw.com/windows-through-the-registry-to-make-ctrl-and-alt-swap.html

继续阅读

Qt for iOS 应用使用自定义动态库 Framework

本文内容适合当你想使用 Qt 调用 xcode 开发的动态库 framework 做 iOS 应用的场景,文中涉及到原始动态库 framework 文件的配置,以及 Qt 如何引入和打包动态库 framework 到自身程序中的方法。

动态库 framework 的配置

默认 xcode 创建的 framework 都已经是动态库形式了,所以这里不多介绍,请确保你的 Build Settings 中以下设置是无误的,特别是 Dynamic Library Install Name Base,它决定了动态库能否被应用成功搜索到。

对于 @rpath 的说明,请看这里:https://www.cnblogs.com/csuftzzk/p/mac_run_path.html

Qt 项目配置引入 framework

Qt 引入 framework 只需要给 LIBS 追加 framework 的路径和要链接的文件就可以了,配置如下,请注意使用 framework 参数是 -F 和 -framework(注意大小写)

LIBS += -F$$PWD/../../build/Products/Release
LIBS += -framework my_dylib.framework

这样引入还是不行的,因为 iOS 应用部署上去以后动态库的 framework 文件是不会跟随打包进去的,所以你还需要做一件事情就是把 framework 打包到你的应用中,配置如下:

MY_DYLIB_FRAMEWORK.files = $$PWD/../../build/Products/Release-iphoneos/my_dylib.framework
MY_DYLIB_FRAMEWORK.path = /Frameworks
QMAKE_BUNDLE_DATA += MY_DYLIB_FRAMEWORK

分别指定了 framework 文件的路径和要被打包进去的目标路径,这里是将 $$PWD/../../build/Products/Release-iphoneos/my_dylib.framework 这个 framework 打包到了应用的 /Framework 目录下。最终你看到的应用目录结构是这样的

├── Default-568h@2x.png
├── Frameworks
│   └── my_dylib.framework
│       ├── Info.plist
│       ├── ReadMe.txt
│       ├── _CodeSignature
│       │   └── CodeResources
│       └── my_dylib
├── Info.plist
├── LaunchScreen.nib
├── PkgInfo
├── _CodeSignature
│   └── CodeResources
└── cross-platform-demo

可以看到,mu_dylib.framework 文件已经在我们应用的 /Framework 目录下了,这样文件就被打包进去了,而且当你使用 Qt 编译程序的时候,在 Build Shadow 目录下会看到生成对应的 xcodeproj 文件,可以直接使用 xcode 打开,使用 xcode 打开项目后可以看到项目的 Build Phases 下面多了一条 Copy file to bundle 的项目:

但是当你尝试在 iOS 或者模拟机中运行这个应用时你会发现又有新的错误了,如下所示:

dyld: Library not loaded: @rpath/my_dylib.framework/my_dylib
  Referenced from: /var/containers/Bundle/Application/D0143CDE-FFFE-4343-BFD9-D70DA66C831F/cross-platform-demo.app/cross-platform-demo
  Reason: image not found
program received signal 6, thread:15362b

我明明已经把文件复制进去了,为什么还是会提示,还记得上面我们提到的 Dynamic Library Install Name Base 吗?这是动态库 framework 设置的,根据上面文章的资料,我们要在调用该模块的应用中设置 rpath 的搜索范围,让其能找到我们的动态库文件。Qt 项目中添加如下配置:

# 添加应用的 runpath 路径,因为 my_dylib 动态库 Framework 设置的 install path 为 rpath,所以应用使用时需要单独设置
QMAKE_LFLAGS += -Wl,-rpath,@loader_path/Frameworks

如此设置后,在 Qt 中就可以成功编译程序并运行在模拟器或真机上了,如果还有任何疑问欢迎留言我们一起讨论。

Qt Quick QSettings 配置信息保存位置

Qt Quick 给我们提供了非常方便的配置文件管理功能,它不仅仅可以在 C++ 中访问,也可以在 QML 中直接访问,最近在看 Qt Examples 目录下的 gallery 项目示例时,虽然知道用的是 QSettings 保存的持久化数据,但是不知道配置保存在哪里了,遂到 Qt 官网查询了一下,有英文阅读能力的可直接参考官网:https://doc.qt.io/qt-5/qsettings.html,以下为照搬翻译:

继续阅读

C++11 改造观察者模式(参考 In-Depth C++11)

这个示例在 In-Depth C++11 书中有很详细的说明和实现,这里只记录自己关心的部分。传统观察者模式基本上都是要提供一个接口类用于提供观察者继承从而在数据变化时让接口被调用。一个典型的例子如下:

class Subject;

class Observer
{
public:
    virtual ~Observer() {}
    virtual void Update(Subject* theChangedSubject) = 0;
protected:
    Observer() {}
private:
};

class Subject
{
public:
    virtual ~Subject() {}
    virtual void Attach(Observer* observer)
    {
        observers_.emplace_back(observer);
    }
    virtual void Detach(Observer* observer)
    {
        observers_.remove(observer);
    }
    virtual void Notify()
    {
        for (auto& iter : observers_)
        {
            iter->Update(this);
        }
    }

protected:
    Subject() {}

private:
    std::list<Observer*> observers_;
};

以上示例有诸多问题,比如如果想让被通知的接口参数化怎么办?是不是所有观察者都要是 Observer 的派生类?主要就是需要继承这种强关系和参数不够灵活。使用 C++11 可变参数模板来解决传参问题,使用 std::function 来解决继承问题。

// Observer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <functional>
#include <iostream>
#include <list>
#include <map>

class NonCopyable
{
protected:
    NonCopyable() = default;
    ~NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable operator=(const NonCopyable&) = delete;
};

template<typename F>
class Events : public NonCopyable
{
public:
    Events() {}
    ~Events() {}

    // 注册观察者,支持右值引用
    int Connect(F&& f)
    {
        return Assign(f);
    }
    // 普通注册观察者函数
    int Connect(const F& f)
    {
        return Assign(f);
    }
    // 移除观察者
    void Disconnect(int key)
    {
        connections_.erase(key);
    }
    // 通知
    template<typename... Args>
    void Notify(Args&&... args)
    {
        for (auto& iter : connections_)
        {
            iter.second(std::forward<Args>(args)...);
        }
    }

private:
    template<typename F>
    int Assign(F& f)
    {
        int k = observer_++;
        connections_.emplace(k, std::forward<F>(f));
        return k;
    }

private:
    int                 observer_ = 0;
    std::map<int, F> connections_;
};

struct T
{
    int a, b;
    void print(int a, int b) { std::cout << a << ", " << b << std::endl; }
};

void print(int a, int b) { std::cout << a << ", " << b << std::endl; }

int main()
{
    Events<std::function<void(int, int)>> event;

    // 第一种注册方式
    auto key = event.Connect(print);

    // 第二种 lambda 方式
    T t;
    auto lambdaKey = event.Connect([&t](int a, int b) {
        t.a = a; t.b = b;
        std::cout << t.a << ", " << t.b << std::endl;
    });

    // 第三种 bind 一个 function 的方式
    auto functionKey = event.Connect(std::bind(&T::print, &t, std::placeholders::_1, std::placeholders::_2));

    int a = 10, b = 20;
    event.Notify(a, b);
}

Python3 执行系统命令并获取实时回显

最近在改造一些打包的逻辑,原来在 Windows 下是基于批处理制作的,由于批处理用起来不是很方便,一些实时的计算基本无法胜任,所以转向 Python3。但在以前脚本的基础上很多是需要调用系统命令的比如 VS 编译一个项目,我们需要获取实时的回显知道编译的结果和进度。所以就有了以下方法:

@staticmethod
def __external_cmd(cmd, code="utf8"):
    print(cmd)
    process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while process.poll() is None:
        line = process.stdout.readline()
        line = line.strip()
        if line:
            print(line.decode(code, 'ignore'))

在使用时直接调用 __external_cmd 方法,传入你要执行的系统命令,根据回显内容设置以下编码就可以了。这样用起来还是比较方便的。

wordpress 异常访问 wp-login.php?action=lostpassword 导致站点流量异常无法访问

今早起来看了下博客的内容,发现站点打不开了,想想不对,昨天刚换好的服务器,怎么忽然就打不开了?ping 了一下服务器地址是通的,但是延迟非常高。随后 ssh 登录到服务器后查看站点的日志,发现有一个上海的 IP 大量的请求地址 wp-login.php?action=lostpassword,对 wordpress 的代码不是很了解,但看这个地址应该是一些暴力破解用着相关漏洞利用。

通过日志可以看到是一个 210.52.224.* 的 IP 段,查了一些资料,在 /etc/rc.local 中添加整个 210.52.224.* 的 IP 段屏蔽他们的请求,随后站点恢复正常:

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

iptables -I INPUT -s 210.52.224.0/24 -j DROP

如果你也遇到类似问题,在文件末尾像我一样增加这个 IP 段,然后重启服务器就可以屏蔽他的连接了。

Qt QML VideoOutput 显示自定义的 YUV420P 数据流

在一些传统应用中,如果想使用 Qt 在 QWidget 或者 QML 中显示自定义的视频数据流,需要引入 OpenGL 来实现。而实际 Qt 已经准备了 VideoOutput 类型可以很方便的调用系统摄像头和使用自定义数据流。在 Qt 官网中,VideoOutput 的介绍中说明,source 属性可以是一个自定义派生于 QObject 的子类,并提供一个类型为 QMediaObject 的属性命名为 mediaObject,或者是一个派生与 QObject 的子类并提供一个类型为 QAbstractVideoSurface 的属性命名为 videoSurface。其中任意一个方法都可以实现自定义视频数据流的播放,本文介绍第二种方法。参考资料:https://stackoverflow.com/questions/43854589/custom-source-property-for-videooutput-qml

继续阅读

C++ 和 QML 的正确交互方法

相关资料

继续阅读

Qt 模拟 HTTP 表单提交文字或文件到服务器

传统通过 HTTP 表单的方式来上传文件在 Web 中实现是非常简单的,一个表单中加几个域填写上对应的内容提交就可以了,但如果通过 Qt 来实现就相对麻烦一点,不过我都总结好了代码,直接使用就可以了。

需要用到的模块

  • QNetworkAccessManager 用来发起 GET/POST 请求
  • QNetworkReply 用来描述响应信息
  • QHttpMultiPart 用来模拟表单域
  • QNetworkRequest 用来构建请求地址等信息

继续阅读