游戏之作-QVariant的解析和更新
游戏之作: QVariant的解析和更新纯属好玩的实现.
我有一个很大的数据结构, 有很多的成员变量, 几十个. 中间涉及到更新, 更新的数据源是外部传过来的一个json对象, 被解析为一个QVariantMap或QVariantHash. 对于每个属性, 我们需要判断传进来的Map中有没有包含, 如果包含了, 还要判断是否有意义(例如, 对QString, 是否为空). 只有都满足了, 才会使用它来更新结构中的属性. 属性类型包括整数, 浮点数, 日期类型, 字符串和QByteArray码流. 这里一个很麻烦的问题是, 比如, QString, QDate, 判断值有效性是不同的函数. QString和QByteArray是isEmpty(), 而QDate和QDateTime则是isValid().
如果每个都使用if...else判断, 实在太麻烦, 还要记住每个成员的类型. 这个问题只有C++能够给出令人满意的解决方案.
实现0:可以用宏实现. 这里主要的问题是需要自己处理类型. 仍然需要记住每个变量的转换函数. 在C++11之前, 大概只能这么实现.
实现1:利用C++ ...
游戏之作-Restful命令处理对象
游戏之作: Restful命令处理对象我们的产品有几十条Restful命令, 凭心而林, 命令太多, 不正交. 更大的问题是命令的api更杂乱.对上层代码而言, 屏蔽Restful命令差异的方式当然是封装接口函数. 但是为每一个函数封装一个接口, 又有大量的样板代码. 虽然前期我是这么做的, 但是随着接口越来越多, 也越来越让人感到厌烦.比如, 下面是一条命令的实现:
12345678910111213141516171819202122232425262728bool KaryoServices::modifyCaseInfo(const QString &case_id, const QVariantMap &info){ QJsonObject rsp; QJsonObject param = QJsonObject::fromVariantMap(info); TRACE() << param; QString path = QString("/api/case/%1").arg(case_id) ...
MagicTool的实现
MagicTool实现MagicTool为仿照某产品的实现而提供的功能, 目的是尽量减少使用者切换工具的操作, 能够利用鼠标以不同的操作手势来实现不同的行为.
规格:按下Magic按钮, 进入Magic模式.
染色体修型/切割: 在背景上按下鼠标, 画线经过一个染色体, 并在背景上释放鼠标.将经过的第一个染色体拆分, 并抛弃小于阈值的部分. 识别:
flyovers.size>=3.
从染色体外起步
从染色体外结束
中间经过至少一个染色体
要切割的就是遇到的第一个染色体
染色体增补: 在一个染色体内部按下鼠标, 拖动鼠标到这个染色体外部区域, 描绘区域再回到这个染色体放开鼠标.将鼠标围起来的染色体外部的区域叠加到染色体上面.
从染色体里面进入
进入其他的染色体或背景
从同一个染色体结束
连接染色体: 在染色体内部按下鼠标, 移动鼠标到相邻的另一个染色体量两个染色体连接到一起.
从一个染色体内部开始
经过其他的染色体和空白
在另一个染色体内结束
分割染色体: 在一个染色体内部按下鼠标, 在这个染色体内部移动鼠标并画一个封闭区域, 然后松开鼠标.
...
看图工具ImageView的设计与实现
看图工具ImageView的设计与实现1 概述干镜扫描出来的是一个包含数千张甚至数万张高分辨率图片组成的图像数据. 在查看时, 我们不可能一次性的加载所有的图像去显示, 只能使用滑动窗口的方式, 仅加载当前显示的部分, 并且释放掉已经不再需要查看的图像数据, 以节省内存的占用.
目前的版本是从本地获取图像数据,以后要考虑数据存储在网络上的情况。
我们使用金字塔方式的文件结构, 逐级构造出金字塔方式的档案文件.
我们后来才查找到TIFF也支持类似的存储方式, 但是找不到详细的资料, 而且也没有能够直接使用的图像显示操作控件, 所以最终还是自己从头实现.
2. 基本架构ImageView核心由及部分组成:
ImageView, 是QGraphicsView的一个派生类, 用于显示图片
TiledPixmapItem, 是QGraphicsItem的子类, 用于显示一张Tile的Item
ImageProvider, 用于封装瓦片文件的按需读取
3. 基本实现思路实现的基本思路是利用了Qt的QGraphicsScene/QGraphicsView的机制. 在Scene ...
Marshal的设计
ImageMatcher设计与实现1. 概述干镜的扫描软件需要做连续扫描, 并将结果拼接成一张完整的大图形式. 涉及到几个难点:
图像数据量很大. 最低的20倍镜头, 扫描玻片全片大约是2200张图片. 40倍则是接近10000张, 100倍则是6万张上下. 每张图片是2448x2048的分辨率, 24bit彩色图像, 原始数据是15M上下.
扫描仪扫描图片的速度是极快的, 以20倍扫描为例, 2200张图片大约90秒就能够完成扫描. 总的数据量超过了30G, 已经超过了电脑的内存大小. 如果再加上其他过程需要的内存, 完全会将系统撑死. 因此, 图片的处理必须能够尽量跟的上扫描的速度.
图像的拼接. 扫描仪的硬件精度不是很好, 在运行扫描过程中, 帧与帧之间大约又±50个像素的误差, 因此无法直接利用硬件(步进电机)的位置作为拼接, 只能作为一个拼接的基准值, 使用图像匹配的方式来做精确的匹配. 而图像匹配的速度是相对比较慢的. 比如想尽一切办法来进行优化. 甚至, 我们都要想办法来尽量拖慢扫描仪的扫描进度(因为它不归我们控制, 我们不好对第三方提出减慢扫描速度的要求).
扫 ...
实现批量命令的并行处理
实现批量命令的并行处理传统上, Restful的命令都是同步执行的, 一条命令执行完毕后再执行另一条.但是, 我们的产品的一条命令需要批量执行多条命令的查询, 下载几十甚至上百个文件. 如果串行执行, 会消耗大量的时间在等待上, 包括服务器的命令执行等待和网络传输的等待.
一种做法是简单地启动线程池, 每个命令一个线程, 最后等待完成. 但是并不好, 本身, Qt提供的QNetworkAccessManager支持异步处理. 我们需要实现批量发送命令, 并且同步等待所有结果完成. 即我们将整个的多个命令的发送和接受作为一个阻塞式的任务, 批量发送, 异步处理, 并且等待所有命令结果都收到后再返回.
下面是批量下载文件的接口的简化实现:
paths是要下载的图片的网络地址的数组.
id_list是每个要下载的图片的标识ID, 用于区分识别.最终返回的是一个QMap<id, QPixmap>.
1234567891011121314151617181920QMap<QString, QPixmap> DeepApi::batchDownloadImag ...
Marshal的设计
Marshal设计概述Marshal界面如下图所示:
使用Graphiscs Scene-View架构. 主要工作都在Scene类MarshalScene中完成.
主要类列表:
MarshalScene QGraphicsScene类
MarshalView QGraphicsView类
ChromosomePixmapItem 用于显示染色体Item的QGraphicsItem类
GroupLine: 表示一类染色体的Item.
ItemGroup: 表示一行的布局
PatternItem: 表示染色体模式图
Notation, NotationHead, NotationTail, NotationEdge : 批注对象
GroupTemplate : 定义Scene中布局的模板
MarshaSceneMarshalScene是用户对染色体做分类操作的主要界面后端Scene. 因为我们的View类几乎什么也没有做, 所以主要的用户交互都在Scene中实现.
同时, 它也是一个数据容器.
123456789QMap<int, GroupLine*> _li ...
Extract的设计
ExtractScene设计染色体Item设计染色体显示对象在界面中, 在分析视图和提取视图中需要显示. 它们都是QGraphicsItem的派生类.
ExtractScene中的染色体显示很简单, 在基本场景下, 只需要在分裂相的图片上面标出染色体的轮廓即可. 只有少数场景下需要显示染色体的图片(对染色体图片做了忒别的增强处理).
我们通过染色体识别, 本身就得到了染色体在图像中的轮廓, 这个信息直接被用于构建Item. 最接近的就是QGraphicsPolygonItem. 因此,
定义类ChromosomeContourItem, 作为QGraphicsPolygonItem的派生类. 剩下的就是其他的一些控制信息, 比如是否显示轮廓, 轮廓的显示控制, 是否显示类别标签, 是否显示图片, 右键菜单等. 基本上没有特别需要关注的内容.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 ...
QtConcurrent中的run()函数的使用
QtConcurrent中的run()函数的使用有两种模式: Basic模式和Promise模式.
好像Promise模式是Qt6里面新增的, 以前没有
Basic模式的使用:使用1: 使用一个外部全局函数123456789// 最简单的场景: func1()是一个无参数的函数extern void func1();QFuture<void> future = QtConcurrent::run(func1);// 稍微复杂一点, 带参数场景:extern void func2(int arg1, double arg2);int arg1 = ...;double arg2 = ...;QFuture<void> future = QtConcurrent::run(func2, arg1, arg2);
场景2: 重载的全局函数12345678910111213// foo有两个重载, 要使用foo/1:void foo(int arg1);void foo(int arg1, double arg2);// 方法1: 使用Lambda, QFutu ...
合集
Part I 创建模式
从函数中返回对象
Chapter 2 Builder
2.1 Scenario
2.2 Simiple Builder
2.3 Fluent Builder
2.4 Communicating Intent
2.5 Groovy-Style Builder
2.6 Composite Builder
2.7 Summary
Chapter 3 Factory
3.1 Scenario
3.2 Factory Method 工厂方法
3.3 Factory
3.4 Inner Factory
3.5 抽象工厂Abstract Factory
3.6 Functional Factory
3.7 总结
Chapter 4 Prototype
4.1 构造对象
4.2 Ordinary Duplicate
4.3 Duplication via Copy Construction
4.4 Serialization的问题
4.5 Prototype Factory 原型工厂
4.6 总结
Chapter 5 Singleton
5.1 Sin ...