原文链接:Thiago MacieiraSource code must be UTF-8 and QString wants it

先前我讨论过源代码的编码问题,认为C++语言缺少一个必要的基本设置。尽管如此,从本周一开始,在某种程度上,Qt5现在已开始强制要求源代码必须是UTF-8。

将QString的8-bit成员函数所用的编码改为UTF-8的提交(commit)终于融入到qtbase代码仓库中。这正是我们为Qt5所规划的,从Robin Burchell移除QTextCodec::setCodecForCStrings开始的,一系列变更(changes)画上了完美的句号。但明确一点:QString内部仍采用UTF-16存储数据且不会改变。

为了理解这个变更是什么,我们需要回顾一点点历史。四年前,我写了一篇叫做“字符串理论(String Theory)”的博客来介绍QString的历史。我写到:

你的文件是何种编码?即便是UTF-8被广泛使用的今天,我们仍然不能依赖于这个事实(Windows下的文本编辑器是最糟糕的例子)。

在2008年,我们仍然与源代码中的UTF-8编码抗争,和我们在2003年所做的一样,当时QTextCodec::setCodecForCStrings被引入到Qt3。原因是,在当时,文本编辑器通常只使用操作系统的本地(locale)编码来保存代码,而且很少支持编写其它东西。由于Unicode尚未被广泛使用,从而导致人们使用各种不同的编码。考虑到数据交换只在使用相同编码的人(来自同一个国家,使用相同的操作系统)之间发生,这在当时也不是个问题。

时代已经改变了。源于90年代后期哪些不具备编码标示的协议很快就变得过时了或者增加这种标记(我记得当时Kopete的开发者正挣扎于如何恰当地解码ICQ消息,而且俄罗斯用户通常以乱码(mojibake)结束)。设计于21世纪的协议都有一个此类标示,而且很快基于某个Unicode transforms形成规范。

去年,当重新审视这个主题时,我写到:

都2011了,为什么我们仍将自己限制在ASCII?我是说,即使你只是用英文写不需要翻译的消息,有时你仍需要非ASCII码点(codepoint),比如“微”符号(µ),度符号(°),版权符号(©),甚至是欧元符号(€)。[...]。除此之外,现在是2011,文字交换的事实(de-facto)编码是UTF-8。

博客中下一行就是结论:在Qt5中,我们将把QString的8位(8-bit)成员函数的默认编码从Latin 1改为UTF-8(注意到,直到15天以后我们才开始思考Qt 5)。这就是我本周一的提交(commit)最终完成的事情。

这对你意味着什么呢?好吧,第一件事情是它依赖于你是否使用了这些成员方法。如果你编译源代码时使用了宏QT_NO_CAST_FROM_ASCIIQT_NO_CAST_TO_ASCII,你将感受不到任何区别。我的意思真的是完完全全,彻彻底底:如果你使用了这些宏,你已经禁用了所有受我的变更所影响的函数。

如果你真的使用了被这些宏所禁用的函数,那么问题是这些字符串使用的何种编码。我在2008所做的假设现在仍然有效:源代码中的绝大多数字符串是7-bit,US-ASCII,英文文字。这些7-bit的文字完全不受影响:它将向往常一样被转换成QString的内部编码UTF-16。这可能会影响一点点性能,但就像我去年所说,我确实有优化UTF-8解码器的计划。尽管如此,如果你可以,我建议使用QLatin1String来封装这些字符串,尤其是你正将它们用于一个有QLain1String重载的QString的函数。

另一方面,如果你确实在QString的8-bit成员函数中使用了高位被设置的的文字,你可能需要修改你的代码。你要么用UTF-8重写你的代码,要么你需要用合适的QLatin1StringQTextCodec::toUnicode函数来封装这些字符串。我比较建议使用前一个选项:在你的源代码中使用UTF-8。你也将获得正确使用QStringLiteral的能力,无论如何,它需要源代码是UTF-8。

[作为历史的一个有趣的转折点,QStringLiteral的灵感源于我去年第二篇关于编码的博客——在本文前面所引用的要求改为UTF-8的第一部分之后,但它比本周一的变更更早地融入Qt 5之中。]

对于Qt自身的源码,我们已经决定应当只使用UTF-8,而且从几周前开始我一直在搜索并重写所有的非UTF-8的源码。 我将更进一步:如果你在自己的源码中不使用UTF-8,你需要自己负责。尽管使其工作是可能的,但不要向我们寻求帮助,也不要期待我们为其添加便利的函数。我也会忽略任何这种形式的争论“我的编辑器、IDE、OS、环境不支持UTF-8”。现在是2012而且我们生活在一个全球化的世界。任何此类的编辑器或环境应该留在属于它的地方:一个致力于上世纪80年代和90年代的博物馆。

Unicode万岁!

0 comments


原文链接 Geir Vattekar – QLALR Adventures – Using QLALR to generate a parser for a text adventure

QLALR是Qt所提供的应用程序之一,它是Qt解析程序生成器。过去它已经被用于Qt Script中,现在它还对QML的JavaScript和Qt中的XML流读取负责。很遗憾地是,因为它缺少文档,大多数Qt用户都不知道它。这篇文章就试图对这一问题进行补救。

QLALR可以根据一个按特有语法写成的文件生成与之匹配的C++代码。这个被接受的语法就是LALR,例如,一个前置口令。和yacc这样的解析程序生成器一样,QLALR对于处理联系和区别都很有帮助(例如变换/减少冲突)。在这里,我们不会讨论太深入的问题,只是来看看如何简单的使用QLALR来实现一个小型语言的解析器。

Plover(珩科鸟)

为了找到一个合适的示例语言,让我们回到上个世纪八十年代,那个时候文字探险(译者注:就是MUD了)还闪耀着光芒。文字探险就是一种玩家们通过输入命令进行交互的游戏。这些命令通常是比真实的人类语言简单得多的一种语言,通常情况下使用的是一种简化的伪英语。这种语法是典型的简单精确。因此我们发现它作为一个QLALR的实例非常合适。

我们将把我们的这种语言称作Plover(在那个用文字探险的时代,玩过的朋友都会眼前一亮的一个名字)。让我们来看几个Plover句子。

north
go north
eat the tasty apple
put orange peel in the trash bin

这里是BNF(巴科斯范式)格式的一个完整Plover语法:

Input ::= Sentence

Sentence ::= SingleVerbSentence
          |  OneNounSentence
          |  TwoNounSentence

SingleVerbSentence  ::= 'go' SingleVerb
                     |  SingleVerb

SingleVerb ::= 'north' | 'south' | 'west' | 'east'

OneNounSentence ::= OneNounVerb Noun

OneNounVerb ::= 'eat'

Noun ::= 'the' ObjectNameList
      | ObjectNameList

ObjectNameList ::= OBJECTNAME
                |  ObjectNameList OBJECTNAME

TwoNounSentence ::= TwoNounVerb Noun Preposition Noun

TwoNounVerb ::= 'put'

Preposition ::= 'in'

OBJECTNAME是一个可以为任意单词的口令,就像编程语言中的变量名一样。其它的口令大家通过拼写就知道意思了。

QLALR规范

QLALR的输入是一个包含语法、口令定义和产品完成时所执行的C++代码的文件(后缀为.g),可以用于构建一个符号栈并且保持对解析状态跟踪。另外,口令管理器必须单独实现,要么在这个.g文件中,要么在一个单独的C++源文件中。

让我们先来看看这么实现解析的类的定义。

class CommandInterpreter : public $table
{

public:
    CommandInterpreter();
    ~CommandInterpreter();

    Command parse();

    int nextToken();

    void setInput(const QString &input);

    inline void reallocateStack();

    inline QString &sym(int index)
    { return sym_stack [tos + index - 1]; }

private:
    int tos;
    QStringList tokens;
    QVector sym_stack;
    QVector state_stack;
    QString yylval;
};

这个类并不是生成的。我们用它来为Plover创建一个方便的前端。$table将会被QLALR所生成的类替代,其中包含了我们在parse()函数中所需要的常量、表和函数。

QLALR使用一个状态栈这样的有限的状态机实现了它的解析。我们自己保存这个栈,当然QLALR会告诉我们哪些状态需要推送到这个栈中和什么时候再次弹出这些状态。tos是用来指向这个状态栈的顶端。稍后会就这一点进一步解释。

sym_stack是一个符号栈,我们在这里保存了一些QString。sym()用来获取和当前产品相关的值。当遇到一个OBJECTNAME时,口令管理器就会设置yylval。我们用它来更新符号栈。

要开始一个命令(Command)的解析,我们需要设置输入(setInput())并且调用parse(),它会在解析的时候为我们构建Command。为了完整起见,我们先展示一下Command的类定义(这一部分没有在.g文件中声明)。

class Command
{

public:
    enum Verb { Eat, Go, Put, North, East, West, South };
    enum Preposition { In };
    enum State { Valid, Invalid };

    State state;
    QString errorMessage;

    Verb verb;
    QList nounNames; // First noun
    Preposition preposition;
    QList secondNames; // Second noun
};

让我们来继续看QLALR的输入文件:

%parser CommandParser
%merged_output commandparser.cpp
%start Input

%parser给出了由QLALR生成的类的名称。%merged_output规定了我们只想输出一个文件,它会同时包含.g文件中的C++的定义和实现代码。如果您想生成单独的头文件和实现文件,也可以使用%decl%impl%start规定了产品解析的开始位置。输出到声明部分的代码由/::/包含,实现部分由/../包含。

然后我们定义了这些口令:

-- The verbs

%token EAT "eat"
%token GO "go"
%token NORTH "north"
%token EAST "east"
%token SOUTH "south"
%token WEST "west"
%token PUT "put"

-- The preposition

%token IN "in"

-- Object names

%token THE "the"

%token OBJECTNAME "object"

口令由%token设定。它们将会变为生成的解析器的常量,并且会由口令管理器返回。注意这里给定的字符串只是用于错误处理,例如在解析自身的时候它们并不会被用到。

对于我们这个小语言,我们通过nextToken()函数实现了我们自己的口令管理器。因为我们是在解析期间自己调用的这个函数,如果需要的话,也可以使用任意的语法分析程序生成器,例如lex。

int CommandInterpreter::nextToken()
{
    if (tokens.isEmpty()) {
        return EOF_SYMBOL;
    }

    QString nextToken = tokens.takeFirst();

    nextToken = nextToken.toLower();

    if (nextToken == "eat") {
        return CommandParser::EAT;
    }

    ...

    if (nextToken == "the") {
        return CommandParser::THE;
    }

    yylval = nextToken;
    return CommandParser::OBJECTNAME;
}

tokens是一个QVector,包含了Plover语句的单词(例如[ 'put' 'gold' 'in' 'chest' ])。正如您所见到的,当识别出认识的口令,就简单地返回口令常量。注意当遇到一个对象名称的时候,就保存这个口令的值。后面会用到它。

语法

在这一部分中,我们将会看看Plover语法的实现。也就是在这里,QLALR(或者其它做同样事情的解析程序生成器)的使用真正帮到我们了。

Input: Sentence ;

Sentence: GO SingleVerbSentence ;
Sentence: SingleVerbSentence ;
Sentence: OneNounSentence ;
Sentence: TwoNounSentence ;

产品的每一部分(或者是一个口令或者是另外一个产品)是由空格(whitespace)分隔的。如果这个产品有其它选择,它们会按照上面所显示的Sentence产品那样给出。

当一个产品完成时,我们可以插入代码:

ObjectName: OBJECTNAME ;
/.
    case $rule_number: {
        sym(1) = yylval;
    } break;
./

/../中间的代码会被插入到生成的解析器中,并且当解析器匹配到之前的产品时执行这一代码。$rule_number将会被这个语法中代表这个产品的数字所替代。

sym()函数会访问符号栈。它会获取和当前产品相关的值,例如sym(1)总是代表当前产品的第一个元素。我们的符号栈是由一组QString组成的。我们只保存OBJECTNAME的值,所以对于语法中的其它部分这个栈总是包含空字符串。

我们将不会查看所有的产品,因为我们已经用BNF格式展示了Plover语法。但在我们离开产品之前,让我们看看对象和句子是如何被解析的吧。

ObjectList: ObjectList ObjectName ;

Noun: THE ObjectList ;
Noun: ObjectList ;

ObjectList: ObjectName ;
/.
    case $rule_number: {
        command.nounNames.append(sym(1));
    } break;
./

...

TwoNounSentence: PUT Noun IN Second ;
/.
    case $rule_number: {
        command.verb = Command::Put;
        command.preposition = Command::In;
    } break;
./

注意产品的每一个部分都将会在符号栈中拥有一个单独的值。

parse()函数

在这个parse()函数中,我们使用了由QLALR所生成的表来进行真正的解析。QLALR还生成了一些帮助函数。

这个解析函数有三个部分:

  • 找到一个产品
  • 执行找到这个产品的代码(正如我们之前所看到的)
  • 处理解析错误
Command CommandInterpreter::parse()
{
    Command command;

    const int INITIAL_STATE = 0;
    int yytoken = -1;

    tos = 0;
    state_stack[++tos] = INITIAL_STATE;

    while (true) {
        const int state = state_stack.at(tos);
        if (yytoken == -1 && - TERMINAL_COUNT != action_index [state] ) {
            yytoken = nextToken();
        }

        int act = t_action (state, yytoken);
        if (act == ACCEPT_STATE) {
            command.state = Command::Valid;
            return command;

        } else if (act > 0) {
            if (++tos == state_stack.size())
                reallocateStack();
            state_stack[tos] = act;
            yytoken = -1;
        } else if (act < 0) {

这部分看起来也许有一点可怕并且难以理解,但是不要怕,这部分代码通常对于解析器来说都是一样的。所以就把它们复制粘贴一下,然后就过去了。这里的要点是t_action()会从当前状态找到下一个状态。当它需要一个新的口令时,它会返回正值;当一个产品完成时,它会返回负值。零值意味着在语法中当前口令失败(也就是一个错误)。

这个while循环会一直运行,知道发生错误或者到达ACCEPT_STATE(例如,输入和语法匹配,并且我们找到了一个有效的命令)。

        } else if (act < 0) {
            int r = -act - 1;
            tos -= rhs[r];
            act = state_stack.at(tos++);

            switch(r) {
./

Input: Sentence ;

...

/.
            } // of the switch

            state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT);
        } else {

在一个产品被找到并且相应的代码(如果有的话)执行之后,我们删除这个已经完成的产品所使用的状态,并且继续处理接下的产品。这是通过更新tos和调用nt_action()完成的,nt_action()会获取在新的tos位置的动作。再一次,简单地复制和粘贴这部分代码到您的解析器就可以了。

最后,我们来看看错误处理:

      } else {
         QString merrorMessage;
          int ers = state;
          int shifts = 0;
          int reduces = 0;
          int expectedtokens[3];
          for (int tk = 0; tk < TERMINALCOUNT; ++tk) {
              int k = taction(ers, tk);

          if (! k)
            continue;
          else if (k < 0)
            ++reduces;
          else if (spell[tk]) {
              if (shifts < 3)
                expected_tokens[shifts] = tk;
              ++shifts;
          }
      }

...

为了在特定状态的语法中检查哪些口令被确认了,我们对所拥有的全部口令调用t_action(),并且之后把接受的口令添加到一个数组中,使其可以用于错误信息。在这里我们不用再显示错误信息代码的部分。Plover的错误信息对于其它语言没有什么用处。例如“我不理解这句话”这样的消息对于C编译器输出来说时非常恼人的。我们为口令所指定的字符串已经被添加到一个数组spell中,数组的索引值和口令值一致。因此如果要获取一个口令的文本名称,可以写spell[PloverParser::TO]

更进一步

如果想要一个更为复杂的实例,您可以看看Qt Script(通常发布在Qt 4的src/script/parser目录下)中的.g文件。您还可以在util/qlalr/examples目录下找到4个实例。如果您的目标是成为QLALR的专家,可以研究一下src/corelib/xml/qxmlstream.g。

源代码

本文中提到的实例的源代码可以在Qt Quarterly的网站下载:qq33-qlalr.zip

关于作者

Geir Vattekar是Nokia, Qt的一名科技作者。在写作之余,他很喜欢交互科幻(文字探险)和函数编程。

译者注:大家还可以参考一下这篇文章 Kent Hansen - Developer Daze(tm) presents: A Closer Look at QLALR

0 comments


Qt 5, C++和Qt Widget

Posted by Zhen Zeng on 2012/04/23

原文链接 Lars KnollQt 5, C++ and Qt Widgets

我们发现大家非常关注我们在Qt5中对C++及QWidget所作出的承诺并且有很多疑问。我想现在正好可以在这里就就大家所关注的热点问题进行一下解答。

Qt 5 alpha文章重点讲了我们在Qt 5加入并启用的东西。

让我在这里明确的说明一下:

QWidget及其全部派生类是桌面Qt 5的核心部分

您仍然完全可以像在Qt 4.x时那样编写您的程序。

我们承诺我们会尽最大努力的保证与Qt 4.x的源码兼容性。这当然包括了QWidget和您用C++写的程序。这些东西都没消失。是的,我们的Qt 5中的Qt Widgets模块现在已经被标记为“完成”,这意味着此时此刻我们没有人在为这个模块的新特性工作了。但是如果有一天这领域有人感兴趣了或者需要进行更多开发了的时候,上述的情况就会发生改变。

Qt 5中我们增加了QML这种应用程序开发方式。我个人坚信,假以时日,越来越多的程序会是用这种新方式写成的,就像我相信对长远来说,QML是一个更好的方法。

像Windows 8 Metro UI这样的新用户界面并不能简单的加入到我们现有的QWidget基础架构中。使用QWidget在这些用户界面上添加各种动画的支持真的极其困难,这是由于QWidget是在好多年前为了大量的以静态为主要单元的用户界面而设计的。

流畅的动画用户界面现在已经存在于那些智能电话和平板设备上了,也开始渐渐出现在桌面环境了。而这些用QWidget基础架构也很难简单实现。

这意味着我们需要点新东西。QML/Qt Quick就是我们想到的解决问题的方法。这也是我们对这方面感兴趣的原因。

过去几年我们使用QML的经验告诉我们QML是创建UI的长期优秀技术。如果您想,您可以简单的把它用作.ui文件的强力替代。同时您仍然可以在需要时用JacaScript写一些程序逻辑。

这确实是由每个人对Qt 5所给出的选项做出选择,并以自己最喜欢的方式去使用它。

C++是且会一直是我们的主要编程语言

我们做了显著的工作,添加了很多新的C++ API。我们在许多地方添加了C++11的支持。而且我们还会持续的做这些事情,因为很长一段时间以来Qt写的大型程序大多数都是C++写成的。这没什么不好的。

我们对Qt 5所做的是混入了另外一种选择。我们想让JavaScript的使用得到像C++一样的支持。我们并不是让它变成更优越的或者唯一的支持方式。这是件好事,它为您编写应用程序提供了新的选择。

只要你想,就可以在QML内加入小段的程序逻辑。这种方式可以快速的做出原型,之后也可以在最终实现上替换为C++实现。在QML中插入小段程序逻辑赋予了程序脚本运行的能力。而且对于很多程序而言,它同样给出了用QML与JavaScript写出程序绝大部分的可能。

总结

使用Qt 5,如果您希望像以往那样使用Qt,请继续。这是一个得到完整支持的编程方式。

但是我相信我们在更长远的未来会需要更多的东西。对应用程序UI的想法会大幅改变,尤其是创建面向消费者的程序的时候。

我们希望Qt能保持稳定,那必须得有解决方法。而这就是Qt 5所全部考虑的。

Cheers,
Lars

3 comments


Qt 5 Alpha

Posted by Zhen Zeng on 2012/04/16

原文链接 Lars KnollQt 5 Alpha

今天我们发布了Qt 5的Alpha版本,这是自进驻Qt Project以来推出的第一个主要版本。大家的努力工作使得这个发布成为可能。这个alpha版本大量的工作与引入的特性是由那些非Nokia员工所贡献的。很高兴的看到这个项目已经成为一个交流的平台,能让人们共同推进Qt。

此次Qt 5 Alpha发布的主要目的是获取反馈以帮助我们改进以后的发布。对于Alpha版,我们着重于Qt Essential模块,这些模块构成了Qt5提供的功能的基础。。

Alpha版本可以在http://qt-project.org/wiki/Qt-5-Alpha下载。请注意此alpha版只以源码形式的发布,没有可供下载的二进制版本,所以您得自己编译二进制文件。编译介绍参见http://qt-project.org/wiki/Qt-5-Alpha-building-instructions

自从去年5月我在一篇博客中概述了这个想法以来,我们已经在Qt 5上工作了差不多9个月了。那篇博客讲了我们在Qt 5中的几个目标,现在我想稍微反映些我们已经实现的部分。

理念

有一个基础的理念引领了大部分的Qt 5开发工作:

”Qt 5应当成为全新应用开发方式的基础。 一方面使用C++提供本地化Qt的全部能力,同时重点应当转移到一个模型上,这个模型使C++主要被用来实现Qt Quick模块化的后台功能。“

我要说的是我们在Qt 5上有一个好方法向着这个理念靠近。这个模型在那些UI是全屏的嵌入式Qt上工作良好。对于桌面环境,我们已经针对所需的这个模型打下了许多基础,但是我们在5.1或者5.2发布的时候才能确切地让这个模型可用。

开放的开发

我们希望在开放中开发Qt 5,围绕着一个强健的社区。自推出qt-project.org以来,我们看见了一个充满活力的社区,并且我们在Qt 5中的许多补丁与新特性都是由社区提供的。

架构的四大变化

我们已经着手对四个主要的Qt内部架构进行修改:

  1. 把全部的Qt接口迁移到Qt Platform Abstraction(QPA)层之上 —— 使得Qt能更容易移植到另外的视窗系统和设备上

    使用QPA,我们从根本上改变了Qt如何集成相关操作系统的视窗环境。QPA在Qt 4.8中作为QWS/Qt嵌入式的一种替代引入,但是现在完全使用在所有平台上。这个迁移带来了大量的工作并且迫使我们重写了很大一部分的纯平台相关的代码。但是它也帮助我们创造了一个更干净的架构,使得平台依赖代码能够得到很好的抽象。可以看到,新的抽象化能显著地简化对于新视窗系统的集成工作,并且为QNX,Android和IOS编写的后台证明了这点。

  2. 重新设计了图形堆栈 – 与Qt 4相比提高了性能,使用Qt Quick和OpenGL (ES) 2.0

    Qt 5为Qt Quick引入了全新的图形架构,使用了基于OpenGL的场景图(Scenegraph)。最低需求:OpenGL (ES) 2.0。QtGui现在包含了一组QOpenGL*类,用以替代老的QGL*类(为了兼容性这些类仍然可以使用)。我们还引入了一个比QApplication更轻量级的新类QGuiApplication和一个处理屏幕上顶层窗口的类QWindow。以QWidget为基础的那些类仍然像Qt 4.x一样工作,基于QPainter。然后QPainter比起以前支持更少的后端。它现在限制了于使用软件光栅(Raster bankend)来绘制屏幕、像素与图像,一个OpenGL后端提供GL接口以及一个提供PDF生成与打印的后端。平台依赖的后端比如X11或者CoreGraphics已经去掉了。这使得我们可以引入新的长期支持的图形架构的同时保证Qt 4.x的QWidget部分的完整兼容性。

  3. 更加灵活的模块化库结构,满足桌面和移动的融合 – 按照需要添加或删除用户特定的模块,Qt mobility API的完整实现

    这主要是一些内部的清扫工作,一般不会被Qt开发者们直接看到。但是模块化Qt库使得我们能够更容易更独立的推进Qt的不同部分。这在Qt 5稳定过程以及我们发布Qt5.0开始保持二进制兼容性的时候显得日益重要。模块化的工作还没有完全搞定,特别是qtbase库仍然包含了很多需要被拆分的模块。所以这部分工作很可能在5.0出来之后还会继续。Qt的模块化还使得从第三方模块到Qt的集成容易了很多。它同样能很好地响应来自笔记本、平板和手机的不同需求趋势,比如有关手机特殊特性的地点、传感器等需求。Qt 5中我们能看到Qt mobility API集成进了Qt —— 其中部分API是一组被称为“Qt Essentials”模块的一部分。提供了这些模块之后,其它的模块就能以简单方式加入了。我们现在已经确认Qt5能比以往任何Qt版本提供更丰富的功能特性清单。请注意此alpha版本就是重点关注Qt Essentials。

  4. 拆分全部QWidget相关的功能到其自有的类库

    通过拆分QWidget到独立的库中,我们不但确保了那些喜欢QWidgets的朋友可以继续使用它们,也为全部使用QML和Qt Quick的UI模型提供了途径。拆分QWidget为基础的功能到其自有的类当中是达到一个长期保持Qt 5架构干净的一个衡量之后的好措施。

这些改变的发表带来了许多反馈,早前我们已经给出了关于Qt 5和架构改变产生的一些常见问题。

新功能

除了架构的改变之外,Qt 5同样提供了许多新功能。在这里我突出介绍其中的一部分,详细的清单在我们的wiki中有描述。

  • Qt Core

QtCore已添加了许多新特性。现在有一个QStandardPaths类来提供对应平台的媒体、文档之类的标准位置。Core还包含了一个以二进制进行速度优化的JSON解析器。我们引入了对插件形式和文件内容的Mime类型的识别。Core同时还加入了一种新的可以在编译时检查信号/槽的连接的语法,以及一个完整的新的Perl兼容的正则表达式引擎。我们重写与优化了许多数据结构以获得更好的性能。我们也有意义的加入了C++11的支持,同时Qt也能继续在C++98兼容的编译器上编译运行。

  • Qt Gui

所有基于QWidget的类都被拆分到了QtWidgets库中。QtGui通过QWindow类获得了顶层界面的支持,同时现在有了内置的OpenGL支持。

  • Qt Network

我们加入了对DNS查找的支持并移除了QHttp和QFtp类(对那些还需要使用它们的人来说这些类可以独立使用)。还有许多和上面提到的类似的优化。

  • Qt Widgets

移植到了新的QPA架构之上而且可以像在Qt 4.x时那样使用。

  • Qt Quick

来自Qt 4.x时代的Qt Quick现在也完美兼容并叫做Qt Quick 1模块。这个模块已经完成不会再得到任何更新了。现在这部分的焦点是新的Qt Quick和Qt Qml模块。Qt 5中我们把Qt Quick的图像部分从QML和JS语法中拆分到不同模块了。新的JS类(QJSEngine和QJSValue)现在使用Google的V8引擎作为场景的后台,提供了更好的JavaScript性能。Qt Quick模块包含了基于OpenGL的场景图和在Qt 4.x就有了的全部基础项。我们加入了对基于GL的阴影、粒子和其它许多特效的支持。在QML侧大部分东西都是源码兼容的,但是如果用C++写的QML项则需要一些修改来适应这个新的场景图。

  • Qt 3D and Qt Location

一些额外的模块加入到了Qt Essentials组中,其中值得一提的是Qt 3D用来整合Qt和3D内容,和Qt Location用来提供GPS访问,地图和其它基于地址的服务。

  • Qt Webkit

自Qt 4.x以来Webkit的C++ API就没有变化过,但是Qt Webkit得到了来自webkit.org的版本更新,带给了我们许多性能的提高以及更好的HTML 5支持。这次的alpha版本在Windows关闭了这个选项,因为目前的编译工作还稍显复杂。我们正在努力工作,以确保在Beta版本中能重启这个功能并正常工作。

从Qt 4.x 到Qt 5的迁移

在Qt 4.x与Qt 5之间有少量的二进制以及源码兼容性差异。但是我们努力调整使得能够简单平滑的把现有代码过渡到Qt 5支持的形式。一个例子就是目前我们有Qt Creator,它用同样的代码编译在Qt 4.x和Qt 5之上。

如果您想在您的项目上尝试Qt 5,您可以在这里查看详细的迁移指导手册。

同时也请注意没有必要现在就直接把您的程序迁移到Qt 5,Qt 4.8仍然被社区以及Digia这样的公司所支持。但是我们深信Qt 5有足够的好处值得让您迁移。

下一步

可以看到自从Qt 5.0开始发展以来已经发生了很多东西。我为我们目前的成就感到高兴。您现在就可以开始下载alpha,试用它并给予反馈来帮助我们完善Qt 5。

所有的反馈应当发在qt开发人员列表(development@qt-project.org, 参见lists.qt-project.org)或者简单地在我们的bug跟踪系统中提交一个bug。当然非常欢迎任意的补丁和bug修复,请提交到codereview.qt-project.org

Alpha版本是迈向Qt 5最终发布的第一步,从现在起重点就在解决剩下的问题以便我们能尽快交付一个最终的Qt 5.0出来。

为了达到这一点已经有很多的人参与进来了,但是我想说的是,感谢每一位对这个发布做出贡献的人。

Enjoy!

Lars

1 comment


原文链接 Gareth StockwellPimp my video: shader effects and multimedia

介绍

最近一个引起了想当关注的话题就是在Qt Quick程序中应用着色代码的效果。一旦掌握了基础的着色编程语言,在您的项目中使用Qt Quick嵌入着色效果就非常的简单了,结果就是绝妙的虚拟效果用令人惊叹的一小段代码就能实现。

在这篇博客中有之前没有提及到的部分,就是在多媒体内容(视频回放,或者视频取景器)上应用着色效果就和在其它任何QML元素上应用一样简单。本篇博客演示了混合着色编程与QtMultimedia后大家都能实现的一些效果。

大家都能实现的效果的最好的证明就是……来一段演示。让我们现在就开始!

在桌面Linux,Qt 5环境下运行的qmlvideofx演示
上面的视频展示了一个Qt Quick 2演示程序,运行在桌面Linux环境上。除了一些辅助的C++代码(监听scene graph信号以计算帧率和从文件系统中读取着色程序)之外,程序整个由QML写成。

实现细节

之前也提到过,Qt Quick 2通过ShaderEffect这个QML元素内置了对着色效果的支持。在视频或取景器中使用这些元素来实现着色效果确实不比在其他任何QML元素上实现更复杂。为了说明这一点,下面的代码片段展示了对文件中播放的视频剪辑中应用时变抖动效果(time-varying wobble effect)。

import QtQuick 2.0
import QtMultimedia 5.0

Rectangle { width: 600 height: 400

MediaPlayer {
    id: mediaPlayer
    autoplay: true
    source: "test.ogg"
}

VideoOutput {
    id: videoOutput
    anchors.fill: parent
    source: mediaPlayer
}

ShaderEffect {
    anchors.fill: parent

    // Properties which will be passed into the shader as uniforms
    property real amplitude: 0.02
    property real frequency: 20
    property real time: 0

    NumberAnimation on time {
        loops: Animation.Infinite
        from: 0
        to: Math.PI * 2
        duration: 600
    }

    property variant source: ShaderEffectSource {
        sourceItem: videoOutput
        hideSource: true
    }

    fragmentShader: "
        uniform highp float amplitude;
        uniform highp float frequency;
        uniform highp float time;
        uniform sampler2D source;
        uniform lowp float qt_Opacity;
        varying highp vec2 qt_TexCoord0;
        void main() {
            highp vec2 p = sin(time + frequency * qt_TexCoord0);
            highp vec2 tc = qt_TexCoord0 + amplitude * vec2(p.y, -p.x);
            gl_FragColor = qt_Opacity * texture2D(source, tc);
        }
    "
}

}

在取景器流而不是视频回放中应用这个效果仅仅是简单地将Video元素替换为Camera元素的事。

在qmlvideofx演示中,每一种效果的实现继承自ShaderEffect的QML元素;这些元素在用户从菜单选择了对应效果之后动态加载、应用在视频内容上。类似的,不同的输入(图像,视频以及摄像头)也是在需要的时候由QML元素动态地加载。

每种特效支持的参数设置(比如像素化中的“粒度”、放大镜中的“半径和衍射”)暴露给一个ListModel,这个ListModel被用于创建一些滑动条来调整这些参数。

代码(Qt 5.x)

qmlvideofx演示的源码保存在qtmultimedia仓库中:

https://qt.gitorious.org/qt/qtmultimedia/trees/master/examples/video/qmlvideofx

在Qt 5.0发布之前,您得从源码中编译、运行这个演示。如何编译的介绍可以以这里作为参考。必要的Qt模块的子集可以按照下述方式克隆:

$QTDIR/init-repository --module-subset=qtbase,qtdeclarative,qtjsbackend,qtmultimedia,qtxmlpatterns

代码(Qt 4.x)

由于Qt Quick 1没有内置着色效果的支持,所以需要Qt4.7.4及后续版本提供的Qt.labs.shaders插件。Qt 4版本的演示可以从这里获取:

https://qt.gitorious.org/qt-mobility/qt-mobility/trees/master/demos/video/qmlvideofx

除了能在桌面环境(已在Linux与Windows上测试)运行之外,Qt 4版本的演示也能在移动设备上运行——下面的视频展示了运行在Symbian设备(Nokia C-701)上的演示程序。

在塞班(Nokia C-701)设备,Qt 4环境下运行的qmlvideofx演示

眼尖的读者可能注意到了这个演示程序和桌面版本有一些不太明显的区别——感谢Qt Quick的灵活性,使得这个不同可以通过替换描述布局的一个单一QML文件来实现。

需要指出的是,这个演示不能在现有版本的Symbian设备上运行。原因就在于它需要视频解码器能够使用OpenGLES引擎作为纹理的输出。在之后的Symbian发布中会加入一个EGL扩展来允许视频与图形栈以这种方式合作(EGL_NOK_image_endpoint2)。一旦合作可用,QtMultimediaKit会自动选择使用它(详见QTMOBILITY-1818),之后演示程序就能正常工作。

然而,您可以现在就在Nokia N9上运行这个演示程序 – 就像下面的视频里的那样。

在MeeGo Harmattan(Nokia N9)设备,Qt 4环境下运行的qmlvideofx演示

进一步的信息

如果您之前还没有对着色效果的潜力感到兴奋,希望现在能激动起来。下面是一些其他人结合Qt/QML使用的例子的链接。

0 comments


调试Qt Quick 2 – 控制台API

on 2012/3/8

原文链接 Christiaan Janssen – QML Profiler update Qt Quick带来了许多灵活快速的开发方式。传统的方式需要不停的进行“编译、打包、部署、运行”的循环,而现在,使用Qt Quick,就算是在运行时您也可以对源码做出增量修改!在Qt Creator的一个又一个版本中,我们努力更好的释放Qt Quick的能量。然而,任何编程语言都有其缺点。QML中脚本的编译错误与运行时错误,有时您总是在QtDeclarative的绑定循环上绕圈子,以及JavaScript的那些您不想被卷入的阴暗角落等等……所有的这些潜在的陷阱意味着您真心希望能有一把称手的调试、分析、检查的武器:) 本篇博客的重点是调试与那些最简洁的调试技术:控制台API。很有可能您已经在您最初的helloworld.qml文件中使用过它了:console.log()是调试技术的一部分。最近我们用FireBug控制台API(事实上的网络浏览器标准)中的一些最有用的方法更新了Qt 5。 日志 从Qt Quick 1.0起我们已经支持了console.log和console.debug。QtScript里的print()函数仍然有效,但是在网络浏览器中执行JavaScript代码时它有略微不同的含义,所以尽量别用它。我们给Qt 5多添加了几个日志函数。所有这些调用都会被转发给它们在Qt中对应的部分: JavaScript函数 Qt/C++副本 console.log() qDebug() console.debug() qDebug() console.info() qDebug() console.warn() qWarning() console.error() qCritical() 是的,眼下console.log()、console.debug()以及console.info()具有相同的功能!因为它们是最常用的方法并且被无数JS代码片段使用,所以我们选择了支持他们。也许我们应该进一步考虑在qlogging.h中添加额外的日志等级;) 分析 想要知道一段特定代码的执行需要多少时间?在您的onCompleted方法中写个逻辑例子。 Component.onCompleted() { console.time(“onCompleted”); // … console.timeEnd(“onCompleted”); } 会以毫秒输出耗时。字符串参数既是特定测量(也可以是交叉测量)的识别码,也是输出的前缀: onCompleted: 401ms 如果需要深入点的分析,可以使用console.profile()、console.profileEnd()。 Component.onCompleted() { console.profile(); } Component.onDestruction() { console.profileEnd(); } 这一对API分析了QDeclarativeEngine的状态与V8的方法调用。然而为了取回数据,你必须在调用console.profileEnd()之前将QML分析工具连接到您的程序否则会丢失分析数据。Christiaan Janssen已经在之前的博文中描述了Qt [...]

0 comments 阅读完整文章 →

Qt HTTP内部构架

on 2012/3/8

原文链接 Peter Hartmann – Inside the Qt HTTP stack Qt HTTP组件是Qt中所有HTTP通信的基础,例如被用于Qt Webkit中。在Qt 5中,HTTP实现中有相当部分被重写,其中大部分的工作是woboq的Markus完成的。这篇文章将试图分析HTTP组件的内部结构,注意出于简化的目的,一些类被省略。 我们可以用如下的方法使用公开接口。 简单的例子 QUrl url(“http://qt.gitorious.org”); QNetworkRequest request(url); QNetworkAccessManager manager; QNetworkReply *reply = manager.get(request); QObject::connect(reply, SIGNAL(finished()), myClass, SLOT(replyFinished())); 1. 公开接口及其朋友 以上的例子展示了主要的接口:使用QUrl来创建被用来表示一个HTTP请求的QNetworkRequest。该请求被传递给QNetworkAccessManager,该类负责在网络上发送请求,并返回一个表示HTTP响应的QNetworkReply。根据URL所采用协议的不同,QNetworkAccessManager会创建QNetworkReply的不同内部子类;如果URL采用了“http://”或者“https://”协议,则会创建一个QNetworkReplyHttpImpl实例。该类是用来设置请求,并在发送请求前向其添加例如缓冲或者cookie等信息。 如果该请求被用作上传数据(例如使用HTTP POST或者PUT),该实现类会用到QNonContiguousByteDevice。这个非连续字节类能够用来在不执行memcpy操作的环境中读取文件、字节等。 这些接口可用如下的UML图表示: 2. 工作线程 在Qt 4.8中引入一个特性是多线程的HTTP后端:这个新的后端在一个单独的线程中收发数据,并进行HTTP消息解析(更多细节请参见这篇文章)。 QNetworkReplyHttpImpl会创建一个名为QHttpThreadDelegate的类,并将其放置在这个新的线程中(被称为HTTP线程)。这个QHttpThreadDelegate是一个Facade类,为所有在HTTP线程中的操作提供了一个接口。所有在QNetworkReplyHttpImpl和QHttpThreadDelegate之间的跨线程通信和数据传递都是通过信号和槽完成的。这意味着Delegate提供了一些槽,由HttpImpl发出的信号所触发,反之亦然。 每当QNetworkReplyHttpImpl被创建时,它都会创建一个相应的QHttpThreadDelegate,链接相应的信号和槽,并将该Delegate移动到HTTP线程中去。 该Delegate提供了用来组建HTTP请求和响应的类,名为QHttpNetworkRequest和QHttpNetworkReply。这个名字会让人困惑,因为我们已经拥有了公开接口QNetworkRequest和QNetworkReply;这两个公开接口提供了大量HTTP特有属性的访问接口,例如设置HTTP流水线、状态码和其他HTTP头。而这两个内部类QHttpNetworkRequest和QHttpNetworkRply则用以解析从socket数据流中接受到的HTTP消息,以填充HTTP头和实体。 下图展示了将这几个类添加到类图后的结构。 3. 更底层 HTTP请求和相应是在所谓的“频道”上进行收发的;简单的说,每个“频道”就是一个socket,并附加了一些用于维护HTTP状态和特性的逻辑。对于普通的HTTP请求,通常采用QTcpSocket作为该频道上的socket,而对于“https://”则采用QSslSocket。 一系列连接到同一服务器的频道组成一个连接。对于和同一个服务器的通信,这里总是只有一个连接,以及最多同时有六个频道。此外,当HTTP流水线被启用的时候,还可以同时发出更多的请求。当通过socket接收到一个相应时,该socket并不会被自动关闭,而是默认被用作后续请求,从而节省socket的初始化时间并重用一个已经具有更大TCP窗口值的socket。 现在,HTTP内部结构的UML图已经基本完整了: 4. 其他 到目前还有两个重要的类未被提及: QNetworkSession:该类主要用于移动设备之中,特别是在缺少互联网的持续连接的环境中。当缺乏对互联网的连接时,QNetworkSession及其相关类会尝试创建一个连接(例如,程序可以连接QNetworkSession的信号,让用户在3G和Wifi连接之间进行选择)。该类被QNetworkAccessManager初始化。 QNetworkAccessAuthenticationManager:该全局类被用于存储能够被重用的认证信息。当服务器要求认证时,QNetworkAccessManager会发出一个信号(QNetworkAccessManager::authenticationRequired()),要求用户输入用户名和密码。而这个认证管理器类则会缓存此信息,并在后续的请求中自动将其发送给服务器。有趣的是,这个认证管理器也使用了QNetworkAccessCache,和QHttpNetworkConnection用来缓存连接一样。 因此,(本文中提及的)Qt HTTP内部构架的完整类图如下所示: [...]

0 comments 阅读完整文章 →

QML分析器的更新

on 2012/2/28

原文链接 Christiaan Janssen – QML Profiler update 大家好! 在过去几个月里我们改进了QML分析器,并着重于用户界面部分。我们希望最新的功能能更容易地浏览时间轴、定位关注点以及更有效的管理统计数据从而能更深入地了解自己的应用程序。QML分析器的一大新功能是可以从磁盘中读取与写入追踪信息,使得这些信息能够在开发者之间共享或者在项目开发过程中重新查阅。新分析器还提供了显示时间轴中正在运行的动画信息,既每秒帧数与正在运行的动画数。我们也集成了V8分析器,它将在单独的标签中显示输出。这个新功能仅仅支持运行在Qt5之上的程序,因为Qt5是使用V8 JavaScript引擎的第一个Qt版本。 下面的短视频对一些新特性做了简要的介绍。很抱歉视频的帧数不足,因为它是从我的笔记本上录制的,而视屏录制软件有时候不能处理高码率。同时我也不清楚如何按其源视频大小嵌到这里来,所以请全屏播放这个视频。 我希望这个视频能激励大家去测试QML分析器,并且用在自己的程序上。随时欢迎您对现有分析器的反馈,例如提出对您有用的新功能和建议~ (译者注:这段视频放在YouTube,可能某些地区的朋友看不到) 不久我们将会发布一篇博客,是关于我们最近正在开发的一些其他QML工具的,敬请关注。 意见与建议可以登录freenode服务器上的#qt-creator频道,与我们联系。

0 comments 阅读完整文章 →

Qt实验室中的Qt图像特效

on 2012/2/26

原文链接 Sami Lehtonen – Qt Graphical Effects in Qt Labs 引言 Qt图像特效项目是为Qt Quick 2.0 提供一组中性的虚拟效果的支持。 目前有超过20种现成可用的QML图形效果元素,涵盖了混合、蒙板、模糊、色彩等多种效果。元素仍然有提升与扩展的空间—欢迎提出任何想法、意见与建议,甚至提供实际的贡献! 包含的特效 特效的使用 对于开发人员和设计人员而言,只用了解基础的Qt Quick/QML就能够爽快地上手这些图形效果元素。 任意QML Item元素都能作为特效的源元素。下面是一个例子,给一张图片添加一个下阴影: import QtQuick 2.0 import QtGraphicalEffects 1.0 Item { width: 300 height: 300 Rectangle { id: background anchors.fill: parent } Image { id: butterfly source: “images/butterfly.png” sourceSize: Qt.size(parent.width, parent.height) smooth: true visible: false } [...]

1 comment 阅读完整文章 →

qbs介绍

on 2012/2/21

原文链接:Jörg - Introducing qbs 多年来我们对qmake产生了一种爱恨交织的感情。一方面它确实能工作,另一方面它也有一些怪癖,因此被广泛地认为无法维护下去了。博文 [TMQB] 包含了一个有希望取代qmake的方法的列表。我们研究了市面上的各种各样的工具,但是没有一个能符合我们的需求 – 详见 [WNCM] 。所以不久之前我们启动了一个内部项目用以实验一些新的想法,成果就是:Qt编译套件,qbs(读作 “Qubes”) 它不是qmake 和qmake不一样,qbs没有绑定Qt版本,它从项目文件的高级项目描述中生成一个正确的编译表(依赖表)。同样,传统的MakeFile生成工具比如qmake和CMake生成了makefile文件,然后将实际的命令留给make或者ninja这样的工具去执行。Qbs的另一方面就是充当了并行生成与直接调用编译器、连接器以及其他工具的角色,非常像SCons和Ant做的事情。 Declarative语言 qbs的语法是一个简化版本的qml,提供了对IDE友好的软件项目的展示。它同样提供了自由使用任何JavaScript表达式进行属性绑定的支持。项目文件编辑器能够理解如何对纯字符串数组文字进行处理。对于更复杂的结构,项目文件编辑器能够“回滚”,使用文本编辑器打开项目文件。 files: ["foo.h", "foo.cpp", "main.cpp"] // 可从 IDE 编辑 files: generateFileList().concat(["extra.cpp"]) // 只能在文本编辑器中处理 对于大多数应用场景来说,可以编写对IDE友好的的项目文件。如果你需要更多的控制,你也可以自由的释放你高超的JavaScript技巧。 现在我们来看一个必须的“Hello World”项目: // helloworld.qbp import qbs.base 1.0 CppApplication { name: “HelloWorld” files: “main.cpp” } import语句使我们可以使用类似“Application”一样的项目,我们给项目起名为“Hello World”,并添加了一个C++源文件。 语言的详细部分说明在文档的“语言介绍”中。 可扩展性 在 qmke中进行代码生成与资源编译相关操作是大家想尽量避免的。而在qbs里,你可以很容易地编写一些规则来将一种特定类型的文件转换为另外一种。qbs既可以调用一个外部工具(例如:rcc)来进行转换操作,也可以直接用内含的JavaScript来转换。下面是一个简单的例子,演示了如何将Qt Creator源码树的.pluginspec.in文件转换为.pluginspec文件。 Rule { … prepare: { [...]

0 comments 阅读完整文章 →