Qt君


  • 首页

  • 关于

  • 归档

  • 搜索

通过索引优化查找性能

发表于 2019-09-21

Qt君最近为软键盘添加中文输入功能,由于字母对应拼音中文比较多。使用字母查找中文要快速响应,不然会影响界面交互。

  在网上找到了一个中文拼音字库,看了下里面的数据将近两万个。比如输入”a”字母,一般我们会遍历所有符合”a”字母的中文,这样将会遍历两万次。多多少少会影响界面交互的流畅性。

  看了一下规律,拼音中文对应字母可分为a到z共26大类。

1
2
3
4
5
6
7
安a
按a
不b
吧ba
产c
成c
...

  如果将a到z细分26大类,就可以将查找范围大大缩小,而a到z就是其索引。
插图

  建立a到z的索引,而查找的时候先查找某一个索引(字母),再通过索引进一步查找对应的数据,从而实现优化查找效率。

CRC16校验与数据解压缩

发表于 2019-09-20

使用Qt接口对数据进行CRC16校验与基于zlib算法进行解压缩。

CRC16校验

  • data:输入数据
  • len:输入数据长度
  • standard:实现标准
  • 输出:CRC16校验和
    1
    2
    3
    quint16 qChecksum(const char *data, 
    uint len,
    Qt::ChecksumType standard)

压缩数据

  • data:输入数据
  • compressionLevel:压缩等级0和9之间,其中9对应于最大压缩
    1
    2
    QByteArray qCompress(const QByteArray &data, 
    int compressionLevel = -1)

解压数据

1
QByteArray qUncompress(const QByteArray &data)

使用QRegExp正则的一个小技巧

发表于 2019-09-19

使用字符字面量’R’来避免繁琐的转义字符。

  使用Qt的正则表达式中需要转义的字符本身也可能需要转义。例如:

  • \d需要写成\\d
  • .需要写成\\.

需要写转义字符\

1
QRegExp rx("^\\d\\d?$"); /* 匹配整数0到99 */

可以使用字符字面量R来避免这种情况。

1
QRegExp rx(R"(^\d\d?$)");

RGB24图像颠倒解决方法

发表于 2019-09-18

RGB24格式图像输出颠倒可以使用以下方法转换过来。

实现

  • 将数据强制转换为3字节(符合RGB24的数据排列方式);
  • 使用std::reverse函数将每3字节数据从头到尾颠倒一次。
    1
    2
    3
    4
    5
    6
    7
    8
    struct Rgb24Byte {
    uint8_t r;
    uint8_t g;
    uint8_t b;
    };

    Rgb24Byte *rgb24Buf = (Rgb24Byte *)rgb24Data;
    std::reverse(rgb24Buf, rgb24Buf + bufSize); // 反转数据

相关知识

  • RGB24图像为每个像素占用8bit(1个字节),数据排列顺序为BGR BGR BGR ...。
  • reverse函数用于反转容器中的内容,包含在algorithm库中。效果:"123456" -> "654321"。
  • RGB32和RGB24相比没有本质区别,除了多了一个字节(透明度),每个像素占用32bit(4个字节),数据排列顺序为BGRA BGRA BGRA ...。

Qt代码风格

发表于 2019-09-17

Qt官方代码风格,可以参考一下。

缩进

  • 使用4个空格;
  • 注意:使用空格而不是制表符(Tab按键)。

声明变量

  • 在单独的行上声明每个变量;
  • 避免使用简短或无意义的名称(例如”a”,”rbarr”,”nughdeget”);
  • 单个字符变量名称仅适用于计数器和临时变量,其中变量的目的是为了显而易见;

  不恰当用法:

1
2
int a, b;
char *c, *d;

  纠正:

1
2
3
4
int height;
int width;
char *nameOfThis;
char *nameOfThat;

  • 变量和函数以小写字母开头。变量名称中的每个连续单词都以大写字母开头;
  • 避免使用缩写;

  不恰当用法:

1
2
short Cntr;  // 不明确的缩写
char ITEM_DELIM = ' ';

  纠正:

1
2
short counter;
char itemDelimiter = ' ';

  • 类始终以大写字母开头。如公共类以’Q’(QRgb)开头,后跟大写字母。公共函数通常以’q’(qRgb)开头;

  • 首字母缩略词是驼峰式(例如QXmlStreamReader,而不是QXMLStreamReader)。

空白符

  • 使用空行将语句组合在一起;
  • 始终只使用一个空白行;
  • 始终在关键字之后和大括号之前使用单个空格;

  不恰当用法:

1
2
if(foo){
}

  纠正:

1
2
if (foo) {
}

  • 对于指针或引用,始终在类型和*或&之间使用单个空格,但在*或&与变量名称之间没有空格;
    1
    2
    3
    char *x;
    const QString &myString;
    const char * const y = "hello";
  • 用空格包围二进制运算符;
  • 每个逗号后留一个空格;
  • 转换模式的使用后没有空格;
  • 尽可能避免使用C风格的转换;

  不恰当用法:

1
char* blockOfMemory = (char* ) malloc(data.size());

  纠正:

1
char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size()));

  • 不要在同一行上放置多个语句;
  • 控制流语句的主体上使用一个新行;

  不恰当用法:

1
if (foo) bar();

  纠正:

1
2
if (foo)
bar();

花括号

  • 使用附加花括号的情况:左花括号与语句的开头位于同一行。如果右花括号后跟另一个关键字,它也会进入同一行;

  不恰当用法:

1
2
3
4
5
6
if (codec)
{
}
else
{
}

  纠正:

1
2
3
if (codec) {
} else {
}

  • 例外情况:函数实现(但不是lambda)和类声明总是在行的开头有左括号;
    1
    2
    3
    4
    5
    6
    7
    8
    static void foo(int g)
    {
    qDebug("foo: %i", g);
    }

    class Moo
    {
    };
  • 仅当条件语句的主体包含多行时才使用花括号:

  不恰当用法:

1
2
3
4
5
6
7
if (address.isEmpty()) {
return false;
}

for (int i = 0; i < 10; ++i) {
qDebug("%i", i);
}

  纠正:

1
2
3
4
5
if (address.isEmpty())
return false;

for (int i = 0; i < 10; ++i)
qDebug("%i", i);

  • 例外1:如果父条件语句包含多行或换行,也可以使用花括号:

    1
    2
    3
    4
    if (address.isEmpty() || !isValid()
    || !codec) {
    return false;
    }
  • 例外2:支持对称:在if-then-else块中也使用花括号,其中if-code或else-code包含多行:

  不恰当用法:

1
2
3
4
5
6
if (address.isEmpty())
qDebug("empty!"); // 这里不对称,容易造成误解
else {
qDebug("%s", qPrintable(address));
it;
}

  纠正:

1
2
3
4
5
6
if (address.isEmpty()) {
qDebug("empty!");
} else {
qDebug("%s", qPrintable(address));
it;
}

  不恰当用法:

1
2
3
4
5
6
// 多级嵌套语句没花括号容易逻辑错误
if (a)
…
else
if (b)
…

  纠正:

1
2
3
4
5
6
if (a) {
…
} else {
if (b)
…
}

  • 当条件语句的主体为空时使用花括号

  不恰当用法:

1
while (a);

  纠正:

1
while (a) {}

括号

  • 使用括号对表达式进行分组:

  不恰当用法:

1
if (a && b || c)

  纠正:

1
if ((a && b) || c)

  不恰当用法:

1
a + b & c

  纠正:

1
(a + b) & c

switch语句

  • case与switch位于同一列;
  • 每个case必须在结尾处有一个break(或return)声明,例外:
    • 用于Q_FALLTHROUGH()表示故意不中断;
    • case后立刻进入下一个case。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      switch (myEnum) {
      case Value1:
      doSomething();
      break;
      case Value2: // case后立刻进入下一个case
      case Value3:
      doSomethingElse();
      Q_FALLTHROUGH();
      default:
      defaultHandling();
      break;
      }

跳转语句(break, continue, return, and goto)

  • 跳转语句后不要放’else’。

      不恰当做法:

    1
    2
    3
    4
    if (thisOrThat)
    return;
    else
    somethingElse();

  纠正:

1
2
3
if (thisOrThat)
return;
somethingElse();

  • 例外:如果代码本质上是对称的,则允许使用’else’来显示对称性。

换行

  • 保持一行短于100个字符并在必要时换行。
  • 注释/函数说明行应保持在80列实际文本之下。调整周围的文本布局,并尝试以避免“锯齿状”段落的方式流动文本。
  • 在换行后的末尾加上逗号;
  • 操作符从新行开始。如果编辑器太窄,则很容易忽略行尾的操作符。

  不恰当用法:

1
2
3
4
if (longExpression +
otherLongExpression +
otherOtherLongExpression) {
}

  纠正:

1
2
3
4
if (longExpression
+ otherLongExpression
+ otherOtherLongExpression) {
}

一般例外

  • 如果严格遵循规则会使您的代码看起来很糟糕,请随意打破它。
  • 如果任何给定模块中存在争议,则维护者对可接受的样式有最终决定权。

ref: https://wiki.qt.io/Qt_Coding_Style

翻译 | QMap与QHash小基准测试

发表于 2019-09-16

本文翻译自: https://woboq.com/blog/qmap_qhash_benchmark.html
作者: Olivier Goffart

  在我的Qt开发者日2012演示文稿(深入探讨QtCore)时,我做了一个比较QMap和QHash的基准。我认为在这篇简短的博客文章中分享结果会很不错。

在底层实现上

在Qt 4中QHash使用哈希表实现,而QMap使用跳跃表实现。

在Qt 5中,虽然容器的实现有所改变,但概念仍然相同。主要有以下区别:

  • QVector、QString和QByteArray现在共享相同的实现(QArrayData)。主要的区别是现在有一个偏移量,将来可能允许引用外部数据。
  • QMap的实现已经完全改变了。它不再是跳跃表,而是一个红黑树。

基准

  基准测试很简单,并且在一秒钟内在循环中进行大量查找并计算迭代次数。
  这不是真正科学严谨的。我们的目标只是展示曲线的形状。

结果

  在我的电脑上运行,gcc 4.7。越高越好。元素的数量是对数标度。对于QHash,人们应该期望它不随元素数量而变化,对于QMap,它应该是O(log N): 对数刻度上的直线。

Qt 4.8

插图
  QMap的执行稍微慢于std::map。对于少于10个元素,QMap查找比QHash更快。

Qt 5

插图
  将跳跃表更改为红黑树是一个好主意。与STL相比,Qt容器的性能基本相同。如果少于20个元素,QMap比QHash更快。

  如果比较Qt5和Qt4之间的数量,您会发现Qt5的性能更好。这可能与QString中的更改有关。

结论

  典型的规则是:仅当您需要对项进行排序,或者您知道您的映射中始终只有很少的项时,才使用QMap。


相关知识

  • 跳跃表:通过增加多级索引(会增加额外的空间)来提升插入与删除操作。
  • 红黑树:是一种特定类型的二叉树,进行插入和删除操作时通过特定操作保持二叉查找树的平衡。

附: 基准测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/* Copyright 2013 Olivier Goffart <ogoffart@woboq.com>
http://woboq.com/blog/qmap_qhash_benchmark.html
*/

#include <QtCore/QtCore>
#include <unordered_map>

#ifndef CONTAINER
#error CONTAINER must be defined to QMap, QHash, std::map or std::unordered_map
#endif

namespace std{
/* std::hash specialization for QString so it can be used
* as a key in std::unordered_map */
template<class Key> struct hash;
template<> struct hash<QString> {
typedef QString Key;
typedef uint result_type;
inline uint operator()(const QString &s) const { return qHash(s); }
};
}


int main(int argc, char **argv) {
if (argc < 2)
qFatal("Missing number of element to add");

QByteArray a = argv[1];
uint num = a.toUInt();

// creates an array of random keys
QVector<QString> strs(num);
for (int i=0; i < num; ++i)
strs[i] = qvariant_cast<QString>(qrand());

CONTAINER<QString, QString> c;
for (uint i = 0; i < num; ++i) {
QString &k = strs[i];
c[k] = QString::number(i);
}

quint64 it = 0;
const QString *arr = strs.constData();

QElapsedTimer t;
t.start();

while (t.elapsed() < 1000) {
const QString &k = arr[(++it)*797%num];
c[k]; // perform a lookup
}
qDebug() << it/1000;
}

Qt 6咨讯

发表于 2019-09-13

Qt 6发布前的Qt 5.14和Qt 5.15为承接Qt 5版本与Qt 6的重要桥梁。就像Qt 4.8与Qt 5一样。
Qt 6目标计划于2020年年底前发布版本。

新一代的QML

  • 引入强类型,简化维护成本,让编译器生成性能更好的代码。
  • 引入更多类似javascript解释器引擎,主要用于适配单片机等低端硬件。
  • 去掉了QML的版本控制,简化QML代码。
  • 优化QObject与QML的数据交互,删除QObject和QML之间重复的数据结构,避免运行时生成数据结构。
  • 支持把QML编译成高效原生的C++代码。
  • 为QML支持私有方法和属性,用于隐藏实现。
  • 更好的工具集成,加入更多和更完善的编译检错工具。

下一代图形

  • 优化2D与3D图形。
  • 它能在编译时根据目标硬件预处理这些素材并在需要时使用。
  • 计划引入统一的主题样式引擎,这将允许我们在桌面和移动平台上获得Qt Widgets和Qt Quick的原生外观。

统一并且一致的工具库

  • Qt 3D Studio和Qt Design Studio合并。
  • 设计工具将集成Photoshop、Sketch、Illustrator、Maya、3D Max等工具。
  • CMake替换QMake成为官方编译工具(QMake依然支持)。

增强已有的C++ API

  • 支持C++17。
  • QML一些功能将会引入到C++中。

语言支持

  • 继续支持Python语言。
  • 新增WebAssembly支持(浏览器运行Qt程序技术)。

兼容Qt 5和增量改进

  • 将删除Qt 5中已经废弃的大部分功能(函数、类或模块)。
  • 让Qt 6与Qt 5.15 LTS足够兼容。

ref:https://www.qt.io/blog/2019/08/07/technical-vision-qt-6

圆形按钮样式

发表于 2019-09-13

大家中秋好。月饼圆,月亮圆,中秋团团圆圆。今天就分享关于圆的东西(圆形按钮样式)。

插图

主题效果 默认颜色 悬停颜色 按下颜色
红色 #f44336 #d5d5d5 #f2f2f2
蓝色 #008cba #d5d5d5 #f2f2f2
绿色 #4caf50 #d5d5d5 #f2f2f2
灰色 #e7e7e7 #d5d5d5 #f2f2f2
黑色 #555555 #d5d5d5 #f2f2f2

红色圆形按钮样式示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
QPushButton {
min-width: 100px;
max-width: 100px;
max-height: 100px;
min-height: 100px;
border-radius: 50px;
font-size: 15px;
color: white;
background: #f44336 ; /* 默认颜色 */
}

QPushButton:hover {
font-size: 18px;
color: white;
background: #d5d5d5; /* 悬停颜色 */
}

QPushButton:pressed {
background: #f2f2f2 /* 按下颜色 */
}

关于更多

  • 源码示例
    https://github.com/aeagean/QSS.git
  • Qt君公众号后台回复”QSS“获取更多相关信息。

NULL与nullptr二义性问题

发表于 2019-09-11

在编程逻辑世界,有因必有果,如果一个结果含糊不定(二义性),显然是我们不想要的。C++11中引入nullptr是为了解决NULL的二义性问题。

NULL二义性的体现

1
2
void func(int) {}
void func(int *) {}
当函数调用func(NULL)时会是怎样执行?

先看C++对NULL的定义:

1
2
3
4
5
#if defined(__cplusplus) 
# define NULL 0 /* C++中使用0作为NULL的值 */
#else
# define NULL ((void *)0) /* C中使用((void *)0)作为NULL的值 */
#endif

我们可以看到C++的NULL被宏定义为0,所以函数func(NULL)会因为NULL为0而导致调用func(int)函数,这是我们不想要的结果。

那怎么解决问题呢?

使用nullptr(空指针常量),当函数调用func(nullptr)时则会调用func(int *)函数。

分享将Windows95系统打包为应用运行

发表于 2019-09-10

Github标星16k的项目,由javascript制作而成,支持系统Windows,Mac,Linux。

  Windows 95从1995年8月24日发布到今天已经有24个年头了,第一次接触到的电脑系统正是Windows 95,那时候每天放学回家都会拍拍这个大块头电脑,然后按下开机键捣鼓起来了。

预览

  怀旧经典的扫雷游戏,现在还使用的不屈记事本,迸发灵感的画图软件,还有和现在文件管理器操作一脉相承的感觉。那时候,爸爸妈妈去上班,我在家里玩游戏,爸爸妈妈都下班,我关上电脑使劲吹让它快点凉起来,不然我就凉凉了。想想那时候也觉得太刺激了。


有兴趣的可以下载体验

1
https://github.com/felixrieseberg/windows95/releases

下载地址

项目地址

1
https://github.com/felixrieseberg/windows95

1…678…32
Qt君

Qt君

313 日志
41 标签
© 2019 Qt君
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4
粤ICP备 - 16070052号