月度归档:2019年07月

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 方法,传入你要执行的系统命令,根据回显内容设置以下编码就可以了。这样用起来还是比较方便的。