Day 04 配置器和多语种实现
Day 04 配置器和多语种实现前面提到了我们在开发Qt应用时,只要有支持多语种的可能,就应当开发原生英文界面,并利用翻译机制来支持中文。今天就讨论如何实现它。在实现翻译之前,首先要知道希望使用的语言。Qt提供的默认机制是根据当前系统的位置和系统设置来决定选择哪种语言,这种做法通常不符合人们的要求。一般来说,我们更希望能够自己控制并记录这种设置,而不是让电脑自己做决定。这就涉及到系统设置的读取和保存。
配置器Qt提供了配置支持的基础类QSettings,它支持使用注册表或ini文件的方式,支持读写操作,更重要的是它会自动检测配置源的变化,再读取QSettings对象,会得到更新后的值,而这对客户端是透明的。但是,直接使用QSettings类需要我们每次访问属性时都要提供冗长易错的配置项名称,这个太容易错了。所以我们需要封装出自己的设置类。
接下来要考虑如何提供这个接口,本质上,配置器在系统运行期间应该是唯一的,始终存在的,将其实现为Singleton是很自然的。但是实现为Singleton对单元测试不是很方便——我们还是希望在测试的时候能够有多种配置可以使用,并且最好不要互相影响。所以 ...
Day 3 构造基本界面元素
Day 03 构造基本界面元素首先我们构建主程序界面。通过基本界面的构建,我们可以进一步理清需求,明确模块接口。
界面草图严格讲,软件的界面应当做专门设计。如何构筑界面,尤其是如何美化界面,并不是我们在本书中关注的重点。在我们的示例项目中,为了减少无关的内容,我们对内容做了尽可能的简化,只保留和要讨论的内容有关的必要部分。我们也不考虑界面的美观等内容,以实现基本功能为目标,只要界面能显示需要显示的信息就可以了。
我的观点一直是,Qt这个东西,在以相同的成本前提下,要做出一个美轮美奂的界面上面,距离WPF差了十万八千里。在做出一个能用的基本界面的前提要求下,开发成本距离Web技术更是差了几个十万八千里。所以,它的根本点就不应该是在界面的外观上面。本书也不会把精力放在这上面。
下面就是我们打算用来实现的一个简单的界面:
菜单条,工具栏,状态栏等系统组件
整个应用窗口垂直方向分成左中右三部分:
左边自上而下由两个组件:上面是一个显示照片预览图的控件,下面是一个图像化显示扫描工作进度的组件
中间部分是扫描图像显示窗口,我们会用来在扫描过程中显示得到的图片
右边部分会用来实时显示扫描过程中 ...
Day 2 项目启动
Day 02 项目启动项目视图按照分层设计原则划定软件的模块,分成四个模块:
12graph TB CellScanner --> Frameworks --> Adaptors
其中,
CellScanner:应用程序的界面部分。
Frameworks:程序逻辑部分
Adaptors:和外部设备的接口
其中,CellScanner是一个应用程序,而另外两个是DLL。
Qt提供了两种界面开发路径:基于传统C++的Widgets和新的QML。在本书中,因为各种原因,我们将技术栈设定为纯C++/Widgets的开发,但是为了以后或许有机会切换到QML(这并不是一个好主意,甚至可能根本不可行,我认为),我们明确划分前后台的职责,将界面无关的逻辑放到framework项目中,将外部接口相关的内容放到adaptor里面去(扫描仪控制,AI功能接口以及网络接口)。
项目结构如下:
123graph TB ScanSuit --> Adaptors & Frameworks & CellScanner & UnitTest ...
Boost.DI
Boost.DI 学习笔记Boost.Ext.DI是一个用来实现依赖注入的框架. 依赖注入在Java和C#中使用的十分普遍, 但是在C++中则出现得比较少. 这个框架也一直没有被纳入到Boost的正式版本中.
对C++而言, 依赖注入是否是一个很重要的功能是值得商榷的, 因为就目前而言, C++实际上极少用于Java, C#这种Bussiness场景, 而是更多地用于底层功能实现. 其类层次的规模基本上是可控的, 而且, 实际上也不大存在大量的接口-实现类这种使用模式–确实比较少见.
下面的内容主要来自于其网站教程.
使用下载Boost.Ext.Di之后, 只要在代码里面包含di.hpp就可以了:
12#include "boost/di.hpp"namespace di = boost::di;
DI要求C++最低版本为C++14.
结论目前看用不到。先不浪费时间了。
Day 1 分析和规划
Day 1 需求分析和计划概述软件开发的第一步是业务和需求分析,明确软件的使用场景,业务目标,并在此基础上总结处软件的需求。这一过程有各种名称,各种方法,取决于不同的软件,领域,内外部环境等。一般而言,承接的外部需求,或者需要对外分包的系统,需求分析的要求就很高。对于复杂的系统,变更代价高昂的产品,前期需求分析的要求也比较高,而对于简单的,企业内部的项目,软件需求分析通常没有太高的,过于形式化的要求。而对于需求变化充满了不确定性的产品,则需要考虑原型技术。我们这个系统本身是一个大的系统的一部分,有明确的上级需求输入,规模也不大,因此并不需要太正规和形式化的需求分析。需求分析和软件架构设计通常是耦合在一起的。尤其是,当规划者,设计者,实现者是同一个/批人的时候,这些工作通常是在一起进行的。
简单的来说,我们需要做下面的几项工作:
明确外部功能和性能需求
明确业务场景和使用模式
明确硬件设备约束
明确子系统间的接口
首先是明确外部需求和使用场景/模式。外部需求一般是从产品规划处得到。通常,市场规划和产品分析会给出产品要实现哪些功能和特性,要达到多高的性能,可靠性,成 ...
Day 0 前言
Day 0 前言题外话关于Qt是什么,Qt难不难,学起来容易不容易,应不应该学,网上有各种各样的讨论,本书不打算出版,所以不会在这种无聊的事情上浪费字数。既不会吹,也懒得黑。就我个人的经历感受看,一个人在工作种选择哪种语言,选择哪种技术,通常是身不由己的。你身处这个位置,被要求使用某种技术,不管你会不会,你喜不喜欢,你都要想会,要么你就走人。而从另一个方面来说,一种语言,一个框架,说到底是一个给人使用的工具,不管是上古的Cobel,Fortran还是现在火的不得了的Go,Rust(比如,最近稀宗同志就公然宣称C++不安全,要求改用Rust😓),本质上和一个钳工手里的锤子,电工手里的试电笔没有区别。我从未见过一个电工因为手里拿的是某世的试电笔就觉得自己可以傲视那些拿着杂牌试电笔的电工了,但是这种情况在程序员里面却十分常见,令我多年来都感到即有趣又费解。毕竟,这仅仅是工具,又不是名牌跑车皮包首饰啥的。
追根溯源,网络上关于语言的争论很多是培训机构在捣鬼,而一个企业从一种技术栈切换到另一种技术栈,一般来说也绝对不是纯粹的技术的原因,更多的是政治上的考量。更多的不再讨论。作为一名工程师,当前 ...
Hello World
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
Quick StartCreate a new post1$ hexo new "My New Post"
More info: Writing
Run server1$ hexo server
More info: Server
Generate static files1$ hexo generate
More info: Generating
Deploy to remote sites1$ hexo deploy
More info: Deployment
std::expected和boost::leaf
std::expected和Boost::Leaf 使用笔记一直没有时间认真看一下Boost::Leaf。抽着有时间,把它和expect一起对比以下,看看它们有什么关系。
背景Boost::Leaf和std::expected, std::optional, std::variant等的区别:
optional是C++17提供的, expected是C++23提供的
std::optional<T>持有对象T或没有. 当它持有对象T时, 存储的是T对象本身而不是指针.
std::variant<T1,T2,T3>, 表示的是某一时刻该对象可能是T1, T2, T3种的任意一个( 有没有可能不持有任何对象? 要再去看看)
std::any表示可以表示任意类型的对象
std::expected<T,E>正常情况下表示T, 非正常情况下表示E, 同一时刻只能表示一个值
Boost::Leaf的Result<T>, 好像它的意思是在result<T>中只提供期望值,非期望值不提供了。而是通过它使用的特殊的语法来获得。
std: ...
decltype的关注点
读书笔记
decltype的关注点两种场景decltype有两种推导场景: 标识符的类型和表达式的类型. 区别是带不带括号. 如果不带括号, 表示要推导标识符的类型. 如果带了括号, 表示要推导表达式的类型. 我们通常看得不仔细, 往往会忽略后面一点.
当要推导表达式时, 遵循下面的规则:
若表达式的值为左值, 则推导出T&
若表达式的值为纯右值, 则推导出T
若表达式的值类别为将亡值, 则推导出T&&
例如, 考虑下面的声明:
12345QPoint pt;QPoint* pPt = &pt;const QPoint* cpPt = & pt;QPoint& lrPt = pt;QPoint&& rrPt = {};
则表达式推导结果:
12345678910111213// 左值using T1 = decltype((pt)); // T1为QPoint&using T2 = decltype((pPt)); // T2为QPoint*using T3 = dec ...
CPP中的智能指针
重新读一下Modern Effective C++这本书。
CPP中的智能指针unique_ptr和shared_ptr
尺寸:
unique指针和普通指针相同大小, 除非定义了自定义析构函数.
shared指针为两倍大小: 它还包括一个指向引用计数的指针. 它的析构函数指针不在类中.
析构函数 - unique_ptr的析构函数是类型的一部分, shared_ptr的析构函数是外部的东西
使用shared指针的注意点:
尽量使用make_shared()而不是使用构造函数. 避免多次使用裸指针构造shared_ptr时出现的问题: 尽量避免裸指针可见.
千万不能将一个shared_ptr的指针传给一个shared_ptr的容器.
12345678910std::vector<std::shared_ptr<Widget>> widgets;class Widget{public: ... void process() { widgets.emplace_back(this); ...