Qt君


  • 首页

  • 关于

  • 归档

  • 搜索

源码分析Qt窗口标题中文乱码的问题

发表于 2019-05-24

设置窗口标题中文乱码现象迟迟不能解决。网上找了又找,解决方案是可以找到。但是往往是不知原因。本文从源码剖释究竟是什么回事。

1. 接口

1
void QWidget::setWindowTitle(const QString &)

2. 常用设置窗口标题方式

  • 方式一:直接设置

    1
    window.setWindowTitle("中文")
  • 方式二:通过赋值设置

    1
    2
    QString title = "中文"
    window.setWindowTitle(title);

3. 为什么会乱码?

  • 字符编码不匹配导致乱码现象。
  • 让我们看看setWindowTitle源码是怎么实现的:
  • setWindowTitle接口:

    1
    2
    3
    4
    5
    /* 源码版本5.12 */
    void QWindowsWindow::setWindowTitle(const QString &title)
    {
    setWindowTitle_sys(QWindowsWindow::formatWindowTitle(title));
    }
  • setWindowTitle_sys接口:

    1
    2
    3
    4
    5
    6
    /* 源码版本5.12 */
    void QWindowsBaseWindow::setWindowTitle_sys(const QString &title)
    {
    qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << title;
    SetWindowText(handle(), reinterpret_cast<const wchar_t *>(title.utf16()));
    }
  • 从源码可以看出title.utf16()最终转换为utf16字符编码。

4. 解决方案

  • 使用QString::fromUtf16转换;
  • 或使用QString::fromLocal8Bit转换,这个接口需要注意的是如果系统是utf16字符编码就不会乱码。如果不是有乱码现象依然存在。
    1
    2
    QString::fromUtf16(u"中文");
    QString::fromLocal8Bit("中文");

5. 总结

  • 中文乱码大部分原因是字符编码问题;
  • 不同系统下字符编码可能不一样;
  • Qt5版本下设置窗口标题需要utf16编码。

愿景式编程方法

发表于 2019-05-23

愿景式编程方法,先实现主体(伪代码),再实现细节。在实现一个接口或方法时,但是它又过于复杂。这时候我们需要静下心来,先整体后局部。先分解出多个步骤,再实现每一个步骤。
有时候思路很重要,先将思路写出来,再从具体出发。

示例

  • 实现数据库写入数据功能
  1. 判断数据库是否存在;
  2. 打开数据库;
  3. 关闭数据库。

利用QByteArray分离十六进制数

发表于 2019-05-21

有时候从网络获取到一串的mac地址却是没有使用”:”或空格分隔开来,未免会让用户阅读困难起来。在Qt5.9及其以上版本QByteArray的toHex接口或许可以解决你的问题。

toHex接口

  • 返回QByteArray类型的十六进制编码副本。该十六进制编码使用数字0-9和字母a-f。
  • 如果分隔符不是'\0'或0,分隔符将插入十六进制字节之间。
  • 注意:该函数是在Qt 5.9中引入。
    1
    QByteArray QByteArray::toHex(char separator) const

示例

1
2
3
4
QByteArray byteArray = QByteArray::fromHex("1a2b3c4d5e6f");
byteArray.toHex(':'); // returns "1a:2b:3c:4d:5e:6f"
byteArray.toHex(0); // returns "1a2b3c4d5e6f"
byteArray.toHex('\0'); // returns "1a2b3c4d5e6f"

兼容Qt4/Qt5版本Qml控件Triangle

发表于 2019-05-21

三角形控件(Triangle),等腰直角三角形。底是高的两倍。

插图

Triangle 备注
导入方法 文件导入
兼容性 QtQuick 1.x
QtQuick 2.x
继承 Item
阅读全文 »

解决QProcess使用setProcessEnvironment环境变量不能生效的问题

发表于 2019-05-20

使用QProcess运行进程时,为了方便使用运行程序,直接使用程序名字运行。但是这时候需要设置环境变量。可是使用setProcessEnvironment怎么设置也不能设置成功。运行QProcess却是一直返回’文件找不到’的现象。下面让我们来窥探究竟吧。

不能正确使用环境变量的代码

1
2
3
4
5
QProcess* myProcess = new QProcess(this);
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("PATH", "C:/AppPath;" + env.value("PATH"));
myProcess->setProcessEnvironment(env);
myProcess->start("App.exe");

可以正确设置环境变量的代码

1
2
3
4
5
QProcess* myProcess = new QProcess(this);
QString path = "C:/AppPath;" + qgetenv("PATH");
qputenv("PATH", path.toStdString().c_str());
myProcess->setProcessEnvironment(env);
myProcess->start("App.exe");

为什么会这样?

  • 先看下列源代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    bool QProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs)
    {
    if (modifyCreateProcessArgs)
    modifyCreateProcessArgs(cpargs);
    bool success = CreateProcess(cpargs->applicationName, cpargs->arguments,
    cpargs->processAttributes, cpargs->threadAttributes,
    cpargs->inheritHandles, cpargs->flags, cpargs->environment,
    cpargs->currentDirectory, cpargs->startupInfo,
    cpargs->processInformation);
    if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
    CloseHandle(stdinChannel.pipe[0]);
    stdinChannel.pipe[0] = INVALID_Q_PIPE;
    }
    if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
    CloseHandle(stdoutChannel.pipe[1]);
    stdoutChannel.pipe[1] = INVALID_Q_PIPE;
    }
    if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
    CloseHandle(stderrChannel.pipe[1]);
    stderrChannel.pipe[1] = INVALID_Q_PIPE;
    }
    return success;
    }
  • QProcess底层调用的是Window系统CreateProcess接口,但是该调用是为了获取可执行文件的完整路径或相对于当前工作目录的路径。

  • 虽然它确实改变了生成过程的环境, 但是却无法设置搜索可执行文件的环境。

怎么正确设置可执行文件的环境变量?

  • 使用qgetenv与qputenv设置。

兼容Qt4/Qt5版本Qml控件ComboBox

发表于 2019-05-19

组合框是一个组合按钮和弹出列表。它提供了一种向用户显示选项列表的方法,这种方法占用最小的屏幕空间。

ComboBox 备注
导入方法 文件导入
兼容性 QtQuick 1.x
QtQuick 2.x
继承 Item
阅读全文 »

Qml/js对象序列化与反序列化

发表于 2019-05-19

介绍对象和数据的序列化与反序列化。

序列化 反序列化
数组 JSON.stringify([1, 2, 3]) JSON.parse(“[1, 2, 3]”)
对象 JSON.stringify({a: 1}) JSON.parse(“{\“a\“: 1}”)

关于是否需要使用获取错误代码接口的思考

发表于 2019-05-18

常用的编程方式对于错误码的最多方式是通过返回值。通过一系列的示例来引发示例3的返回值接口思考。

常用获取错误代码方式

  1. 通过返回ErrorCode获得错误码。

    1
    ErrorCode exec();
  2. 通过返回一个string获得错误码字符串。

    1
    string exec();
  3. 还有一些方式是通过额外的getErrorCode和errorCode这类的名字接口获取错误代码。

    1
    2
    list<string> exec();
    ErrorCode errorCode();
  4. 使用参数引用/指针获取

    1
    void exec(ErrorCode &errorCode);

什么情况下使用额外的接口获取错误代码方式比较好?

  • 一般使用在上面的第三种方式中;
  • 当需要的返回值具有其他功能;
  • 当list<string>为空时并不能确定是内部返回的结果为空还是由于错误而返回的空值问题;
  • 有人会问,我可以在参数传入来获取。比如:

    1
    list<string> exec(ErrorCode &errorCode);
  • 的确这样可以解决问题,但是有些时候我们并不需要知道具体的错误,也就不必传入额外的errorCode的引用。

兼容Qt4/Qt5版本Qml控件RoundRectangle

发表于 2019-05-17

可设置矩形圆角位置的控件。

RoundRectangle 备注
导入方法 文件导入
兼容性 QtQuick 1.x
QtQuick 2.x
继承 Rectangle
阅读全文 »

一个好的Qml文件(翻译文)

发表于 2019-05-16

本文翻译自https://www.vikingsoftware.com/a-good-qml-file/

  什么样的Qml文件(通常称为组件)是一个高质量的文件?
  让我们看看示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import QtQuick 2.9
MouseArea {
anchors.right: parent.right
anchors.bottom: parent.bottom
id: button
Rectangle {
anchors.fill: parent
color: "lightgray"
}
width: 100
height: 20
property string text: ""
Text {
anchors.centerIn: parent
text: button.text
}
function foo() {
}
}

  再看看示例2:

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
import QtQuick 2.9
/**
Simple push button like component
No support for theming or icons are provided
*/
Item {
id: root
/// The text on the button
property alias text: textItem.text
/// The signal is emitted when the user clicked on the button
signal clicked()
implicitWidth: textItem.implicitWidth
implicitHeight: textItem.implicitHeight
width: implicitWidth
height: implicitHeight
Rectangle {
id: backround
anchors.fill: parent
color: "lightgray"
}
Text {
id: textItem
anchors.centerIn: parent
}
MouseArea {
id: clickArea
anchors.fill: parent
onClicked: {
root.clicked();
}
}
QtObject {
id: internal
function foo() {
}
}
}

  我希望您认同示例2更清晰、更易于使用。但是,让我们来看看它们有什么不同之处:

  • QML文件应该始终具有相同的顺序结构:
    1
    2
    3
    1. 应该从公共API(如属性、信号和函数)开始;
    2. 再到设置的派生属性;
    3. 最后是它包含的项。

其思想是,如果有人想要使用该组件,他只需要查看顶部部分。当你浏览代码的时候,它会变得简单很多。

  • 应该对公共API文档化(注释),就像所有高质量的代码一样。
  • 文件所包含的根控件,以“root”作为其id。 它是文件中通常使用最多的id。 如果总是使用相同的id,就会更容易。
  • 所有控件都有一个id集。 我这么做是因为让描述该控件的名称。
  • 控件的id总是在第一行。
  • 控件不应该包含一些与其他不相关的组件,应该是纯粹的组合关系。

    1
    示例1中根控件MouseArea包含了一些Rectangle和Text与原本意思不相符的东西。
  • 设置隐式大小并用作默认大小。 隐式大小主要用于动态布局。 使用时可以覆盖实际大小。 做为一个默认值使用会很方便。 相比之下,在一个不好的示例1中,组件已经假定它将如何使用默认大小并在这种情况下通过设置锚点。

  • 应隐藏不应从外部使用的属性和函数。 我这样做是通过将它们移动到我称之为内部的QtObject(对于我来说,类似于Qt C ++中的d指针)。 所以函数由internal.foo()而不是root.foo()或只是foo()调用。 另一种选择是使用双下划线(此处为__foo())来作为私有属性和函数的名称。
  • 对于text属性,使用别名来保存重复,内存和绑定工作。

  示例2稍微多一些工作。 但像往常一样,质量代码增加了一些额外的工作量。 一旦习惯了它,那就不是那么多了。 所以这个简短的列表已经涵盖了一个易于使用的编写良好的QML文件的许多方面。 遵循这些想法将有助于生成可重用且更易维护的QML组件。

译者总结

好的代码 好的示例 不好的示例
文件相同的顺序结构 示例2中属性->信号->函数->其他 示例1中自定义属性随便放
关键部分需要注释 例子2中关键部分属性与信号都有注释 例子1无注释难阅读
控件应该有一个id名字且明确 示例2 示例1
控件id应该放在第一行 示例2 示例1
功能明确且单一的 示例2 示例1中MouseArea包含多个与自己不相关的内容
使用隐式大小 示例2中使用了implicitWidth属性 示例1中直接设置width
锚点应由上层设置 示例2 示例1内部设置了锚点会导致意想不到的情况
隐藏私有属性和函数或使用双下划线标记 示例2中使用QtObject来存放私有属性 示例1
能使用别名的就别重新定义变量 示例2中property alias text 示例1中button.text
1…181920…32
Qt君

Qt君

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