文档章节

Wt快速入门教程[中英文]

zhmsong
 zhmsong
发布于 2012/01/18 17:52
字数 9640
阅读 1218
收藏 2

Wt快速入门教程

A hands-on introduction to Wt

http://www.webtoolkit.eu/wt/doc/tutorial/wt.html



Table of Contents                                      【 内容

1. Introduction                                          【1.简介

2. Main components                                  【2.主要内容

2.1. Widgets                                             【2.1部件

2.2. Session management and lifetime        【2.2会话管理和生存周期

2.3. Deployment                                        【2.3部署

3. Hello, Wt                                              【3.HelloWt例子

4. Hangman                                              【4.Hangman例子

4.1. A first custom widget                           【4.1第一个自定义部件

4.2. Unleashing (some of) Wt’s power         【4.2进一步显示Wt的能量

4.3. Internal paths                                     【4.3内部路径

5. Summary                                              【5.总结


作者:{

    Pieter Libin < pieter@emweb.be>
    Koen Deforche < koen@emweb.be>

    Wim Dumon <wim@emweb.be>}

For Wt 3.2.0 (December 5 2011)                     【适用于Wt-3.2.0

1. Introduction


Wt is a C++ library for developing web applications. Admitted, C++ doesn’t come to mind as the first choice for a programming language when one talks about web development. Web development is usually associated with scripting languages, and is usually implemented at the level of generating responses for incoming requests. Since both requests and responses are text encodings, web programming is ultimately a text processing task, and thus conveniently expressed in a scripting language.

Wt是开发Web应用的C++库。不可否认,当我们讨论Web应用开发时C++从来不是第一选择。Web应用开发总是和脚本语言关联在一起,也通常是在接受请求/生成应答层面实现。因为请求与应答采用文本编码,所以,Web编程最终成为一种文本处理,也就非常方便地为脚本语言所表达。

Yet, from a functional point of view, and as a programming task, a modern web application resembles more a desktop application: it is highly interactive and no longer organized in pages (perhaps still conceptually, but not physically). Interaction with the server happens more and more in the background and not with complete page refreshes. Indeed, the browser is being transformed into a platform for applications where users interact with data and more often than not with each other.

然而,从功能上看,作为一项编程任务,现代Web开发更像是桌面应用:高度交互且再也不以页面为组织单位(或许概念上是,但实际上并非以页面存储)。与服务器的交互越来越多地发生在{background},且不会伴随有页面的刷新。另外,浏览器也变成了应用的平台,在这个平台上用户更多地与数据交互,而不是用户间的直接交互{more often than not with each other}

While some developers choose to implement the application in JavaScript and use a client-server protocol to access server-side resources, this has a number of inherent downsides. You need to let go of static typing (or add another layer, such as GWT). Type safety, enforced by a compiler, is invaluable as a project grows in complexity and number of developers. Moreover, you need to design a client-server protocol and minimize communication and associated round-trip latencies. Finally, this approach cannot work for applications that need to meet accessibility guidelines or need to be search engine optimized and thus require a HTML-only version of the application. Wt, as a server-side solution, overcomes these problems, and with little to no interactivity loss compared to a JavaScript application.

当前程序员们通常会选择JavaScript实现应用,并使用客户端-服务器端协议访问服务器资源,这种方案存在一些先天不足{inherent downsides}[1],比如你只得放弃静态类型转换(当然可以借助 Google Web Toolkit 避过)。但是,编译器强制保证的类型安全是无价的,特别是当项目越来越复杂,涉及开发人员越来越多时。进一步,用JavaScript时还必须涉及客户端-服务器端协议,最小化通讯延迟时间。最后,JavaScript在可访问性(浏览器兼容性低)和SEO优化方面不能满足需要,到最后只能搞出应用的HTML版本来应对。Wt,作为服务器端解决方案,克服了以上问题,{with little to no interactivity loss compared to a JavaScript application}

Wt’s main advantage over other server-side approaches is its widget abstraction. An application is defined as a hierarchy of widgets. Some of these widgets are generic and provided by the library (like push buttons, line edits, table views, …), and others are specialized for a particular application (e.g. an entire blog widget). A widget encapsulates the view and behavior aspects, consumes and produces events and also participates in URL handling and may interact with other HTTP resources. More often than not, a widget delegates the actual "logic" to a model, a layering approach typically known as MVC. A widget, unlike a page or “partlet”, is a truly reusable, customizable and extensible building block (as a poster-child of Object-Oriented Programming) for a modern web application. The actual request handling and rendering is abstracted, with as benefit that a full page rendering model (plain HTML) or incremental updates (Ajax/WebSockets) can be used depending on configuration and browser properties.

Wt的主要优点是 部件抽象{widget abstraction}。Wt应用被定义为一种基于部件的层级结构。有的部件是Wt内置的,如按钮、文本框、线框、表格视图等,有的则是为特别应用开发的,如博客部件。部件封装了视图和行为,生成和处理时间,同时参与URL处理及其他HTTP资源交互。通常,部件将代理执行实际的模型“逻辑”,实际是一种MVC模式。部件,不是通常说的页面或页应用一部分{partlet},而是一种真正能复用的、可定制的、可扩展的构建模块(OOP的典型模式),专门为现代Web应用开发。Wt将实际的请求处理和渲染过程予以抽象,还可以根据实际配置和浏览器特性充分利用全页面渲染模型(面向HTML)和增量渲染(Ajax/WebSockets)的优点。

Although implemented in C++, Wt’s main focus or novelty is not its performance, but its focus on developing maintainable applications and its extensive library of built-in widgets. But because it is popular and widely used in embedded systems, you will find that performance and foot-print has been optimized too, by virtue of a no-nonsense API, thoughtful architecture, and C++ …

虽然Wt用C++实现,但Wt的主要目的或创新点却不是它的性能,而是为了解决开发可维护性应用,以及提供一定数量的内置部件库。同时,Wt在嵌入式系统开发中很流行,所以可以发现Wt应用的性能和内存使用已经优化。以上这些,都得益于Wt高效的API、精心设计的架构、以及C++的一切优点。

 In this tutorial, we will use two small programs to illustrate how to use Wt to create web applications. After this tutorial you should have a good grasp of what the possibilities are, how Wt applications are built, and how it offers you a tool to manage complexity.

在这个教程中,我们使用了两个小程序来展示如何使用Wt创建Web应用,之后,你就能明白使用Wt的可能性、以及它在管理复杂应用时带来的好处。

The first application is the obligatory Hello World application and introduces two key concepts of the library: creating and updating a widget tree and reacting to user input. The second, slightly larger application is the classic hangman game. Both applications are included in the Wt source distribution.

第一个应用是 Hello World,这是必须的:-),它引入了Wt库的两个基本概念:创建和更新部件树、对用户输入作出反应。第二个应用,是稍微大点的应用,Hangman。两个应用的源代码都在Wt的发行版中。

But before we dive in, let’s quickly go over the main concepts of the library.

开始之前,让我们先了解一下Wt库的基本概念。

2. Main components


2.1. Widgets【部件

The user-interface, rendered in a browser window, is specified by creating and maintaining a widget tree. Every widgetcorresponds to a rectangular part of the user-interface, and manages its contents and behavior. At the heart, the library takes charge of two tasks within a single session: (1) rendering this widget tree as HTML/JavaScript in the web browser, and tracking changes as incremental updates, and (2) synchronizing user input and relaying events from the browser to these widgets.

Wt中,将浏览器窗口渲染用户界面,描述为,创建和维护部件树。

每个部件对应于用户界面的一个矩形部分,同时管理它的内容和行为。在一个会话中,Wt核心库有两个主要任务:(1)在浏览器中以HTML或JavaScript方式渲染部件树,并以增量渲染的方式追踪部件树的变化;(2)从浏览器到部件之间同步用户输入和广播事件。

Because of the clear separation between user-interface specification using the widget tree and the rendering of the tree, the library implements several optimizations for rendering when Ajax is available. Not only can the library update the interface incrementally, there are other tricks such as only rendering visible widget changes during in response to an event (or initial loading), and in the background render changes to hidden widgets. As a consequence, both the initial response is optimized and the appearance of subsequent widgets appears snappy.

Wt中将用户界面的表达(使用部件树)和部件树的渲染明确分开了,这样,Wt实现了很多针对Ajax渲染的优化。不仅能够增量渲染界面,而且还使用了很多技巧,如对可见部件的渲染安排在初始载入时或明确有响应事件时,而对不可见的部件也能够事先渲染,这样一来,不可见部件树的显示就变得很快。

2.2. Session management and lifetime【会话管理和生存周期

Another aspect that is entirely handled by the library is session management. For every new session, which corresponds to a new user accessing your web application, the library creates a new WApplication object. As a developer, you can implement an application pretty much as a single-user application, unless you let users interact with a common component (such as a database) or with each other, for which standard data-sharing mechanisms must be used.

另一个完全由Wt库处理的是会话管理。对于每个新会话(对应于一个新的用户访问Web应用),Wt都创建一个新的 WApplication对象。作为开发者,你可以将其实现为单用户应用,也可以实现为多用户交互应用,当然必须有数据库或其他标准数据分享机制。

Depending on the deployment model, the library will map sessions onto dedicated or shared processes. When using dedicated processes a new process is created for every distinct session, this provides the kernel-level isolation of different sessions, which may be useful for highly security sensitive applications. When using shared processes new sessions are allocated randomly to one of the processes available to the library. This reduces the danger for DoS attacks, but requires more careful programming as memory corruption affects all sessions in a single process, and sessions are not isolated by any other mechanism but correct programming.

根据部署模式的不同,Wt将会话映射到独享进程或共享进程上。当使用独享进程时,Wt为每个独立的会话创建一个新的进程,这种方式提供了kernel级的会话隔离,适用于安全级别要求很高的应用。当使用共享进程时,新的会话将被随机分配给某个进程,这种方式降低了Dos攻击的风险,但需要细心编程,因为一旦某个会话崩溃将影响它所在进程中的所有会话,并且该进程中的会话间没有任何机制保证其隔离,当然正确编程是肯定没有问题的。

Wt uses a keep-alive protocol between client and server to determine session lifetime. As long as the web page is displayed in the user’s browser, the session is kept alive. When the user closes his window, navigates away, or after a timeout when connectivity is lost, the session gets terminated. When a session is terminated, the application object together with the entire widget tree is deleted, therefore you should release resources held by your widgets or application in the destructors of these objects.

Wt在客户端和服务器端使用keep-alive协议决定会话的生存周期。只要页面在浏览器中显示则该会话就保持存在。而当用户关闭窗口或通过导航离开此页面或者页面过期失去连接,那么会话将终止。当会话终止时,Web应用对象以及整个部件树都将被删除,因此此时你应该保证释放部件树或Wt应用所拥有的资源,在Wt中使用C++的析构函数即可。

2.3. Deployment【部署


Several deployment options are available:

Wt有以下几种部署方案

  • The wthttp connector library implements a webserver that implements HTTP(S) and WebSockets. It is not only convenient during development, but also an efficient solution for deployments ranging from small embedded systems or mobile devices to multi-server deployments.

    wthttp连接器:这是Wt库自有的一种Web服务器,实现了HTTP(S)和WebSockets。wthttp不仅部署简单,而且是一种高效的服务器部署解决方案,范围包括小的嵌入式系统、移动设备、或多服务器部署。

  • The wtfcgi connector library implements the FastCGI protocol. It allows a Wt application to be integrated into an existing web server (such as Apache, Lighttpd or Nginx).

    wtfcgi连接器:实现了FastCGI协议。允许将Wt应用整合进现有的Web服务器(如Apache,Lighttpd或Nginx。)

  • The wtisapi connector library implements the ISAPI protocol. On Windows platforms, it allows a Wt application to integrate into Microsoft Internet Information Server (IIS).

    wtisapi连接器:实现了ISAPI协议,在Windows平台上,允许Wt应用整合进MS-IIS服务器。

3. Hello, Wt【Hello,Wt 例子

In this example, we show an application that prompts the user for his name. When he pushes the button, the greeting text is updated based on the name that was entered.

这个例子中,我们要显示一个线框部件(nameEdit_),让用户输入姓名;当用户点击按钮部件(button)时,问候语部件(greeting_)将根据用户输入被更新(渲染)。

img/hello.png

A new session starts with creating an instance of class WApplication. This object manages the root of the widget tree and contains other session information, such as the connected browser’s capabilities.

新会话开始于WApplication对象实例的创建,该对象管理着部件树的根,并包含有会话的相关信息,比如用户使用浏览器的具体信息等。

A complete "Hello world" application

下面是 “Hello world”应用的完整代码

#include <Wt/WApplication>
#include <Wt/WBreak>
#include <Wt/WContainerWidget>
#include <Wt/WLineEdit>
#include <Wt/WPushButton>
#include <Wt/WText>

class HelloApplication : public Wt::WApplication
{
public:
    HelloApplication(const Wt::WEnvironment& env);

private:
    Wt::WLineEdit *nameEdit_;
    Wt::WText *greeting_;

    void greet();
};

HelloApplication::HelloApplication(const Wt::WEnvironment& env)
    : Wt::WApplication(env)
{
    setTitle("Hello world");

    root()->addWidget(new Wt::WText("Your name, please ? "));
    nameEdit_ = new Wt::WLineEdit(root());
    Wt::WPushButton *button = new Wt::WPushButton("Greet me.", root());
    root()->addWidget(new Wt::WBreak());
    greeting_ = new Wt::WText(root());
    button->clicked().connect(this, &HelloApplication::greet);
}

void HelloApplication::greet()
{
    greeting_->setText("Hello there, " + nameEdit_->text());
}

Wt::WApplication *createApplication(const Wt::WEnvironment& env)
{
    return new HelloApplication(env);
}

int main(int argc, char **argv)
{
    return Wt::WRun(argc, argv, &createApplication);
}


You can build and run this application locally, if you want. All you need to do is compile the code above and link against the Wt library (libwt) and built-in HTTP server (libwthttp).
你可以直接编译和运行这个例子的代码,并连接到libwt和libwthttp。

On UNIX-like systems, you could do the following:

UNIX类似系统上,你可以照下面的方式进行:

$ g++ -o hello hello.cc -lwthttp -lwt
$ ./hello --docroot . --http-address 0.0.0.0 --http-port 9090

Let’s start with the last part of the application, the main() function.

下面让我们先来分析下 main() 函数。

In the main() function, we call WRun() to start the application server. This method will return when the application server shuts down (by catching the KILL signal or Windows equivalent).

在main()函数中,我们调用 WRun()来启动应用服务器{application server},WRun() 方法将在应用服务器关闭时返回(通过捕捉KILL信号实现)。


Inside WRun()【 WRun内部

WRun() is actually a convenience function which creates and configures a WServer instance. If you want more control, for example if you have multiple “entry points”, or want to control the server starting and stopping, you can use the WServer API directly instead.

WRun() 实际上是一种简易函数{convenience function},用于创建和配置WServer实例。如果你需要对其有更多的控制,比如说你希望有多个“进入点”{entry points},或者希望控制服务器的启动和关闭,那么你可以使用WServer 提供的API,而不是直接使用WRun()。

The WRun() function passes argc and argv (which for some connectors such as the built-in webserver configures the server), and accepts a function object as last argument. This function will be called when a new session is started and returns a new WApplication instance for that session. This function, in turn, has as input a  WEnvironment object, and this object can be used to customize the application or authenticate the user.

WRun()函数传递 argc 和 argv 参数(用于连接器配置服务器,如内置wthttp),同时最后一个参数接受一个函数对象{createApplication},creatApplication() 函数将在新会话启动时调用,并为会话返回一个WApplication实例。createApplication()函数有一个输入参数 WEnvironment(env)对象,env可以用来定制应用或认证用户身份。

Wt文档中的 Wt::WServer::WRun(int argc, char *argv[], ApplicationCreator createApplication) 定义,其中ApplicationCreator 的类型定义为 typedef boost::function<WApplication* (const WEnvironment&)> ApplicationCreator,实际为 Wt::WApplication *createApplication(const Wt::WEnvironment& env) 。


The example instantiates four widgets into the application’s root container: a text ("Your name, please?"), a line edit (nameEdit_), an empty text (greeting_) and a button (button). These three types of widgets are generic widgets provided by the library, and map directly on native HTML elements. In the hangman example below we will see how other custom and more specialized widgets are used in exactly the same way.

这个例子实例化4个部件,并放入应用的根容器中,分别是:1个文本(初始值为“Your name, please?”),1个线框(nameEdit_),1个空文本(greeting_)和1个按钮(button)。以上三类部件都是Wt库提供的原始部件,直接关联到对应的HTML元素上。在Hangman例子中,我们将会看到其他自定义的部件。

After we instantiated the widgets, we specify that we want to react to a click on the button. We connect the greet()method to the button’s clicked() signal. Events propagate from one widget (the button) to other widgets or, as in this case, the application object, using signals. A glimpse at the reference documentation of a widget lists the signals that are exposed by a particular widget. For basic widgets, such as a push button, these are the typical mouse and keyboard events. Higher-level widgets may advertise other events (for example, a calendar widget) has aselectionChanged() signal), and you can add events to your own custom widgets.

实例化后,我们将要对按钮的点击做出反应。我们连接greet()方法到按钮的clicked()信号上。事件将从一个部件(按钮)传递到其他部件上,当然本例中,实际是传递到应该对象上。我们可以先行查看一下Wt的文档明确哪些部件都能够释放出哪些信号,这里,如按钮,他有典型的鼠标和键盘事件释放出信号。其他高级部件可能会发出其他事件,如selectionChanged()等,以此为基础,你可以增加这些事件到自己的定制部件上。

How event propagation works

事件传递是如何工作的?

When an event is triggered by the user, the web browser communicates the target object and corresponding signal together with all form data to the web server (using a full page refresh or Ajax/WebSockets). At the server, after the request has been validated as genuine, form data such as line edit text is first processed to update the widget tree state. Then, the event is propagated by emitting the signal on the target widget, which propagates the event to all connected methods, such as in our example the greet() method. Modifications to the widget tree are tracked, and after the event has been handled these changes are reflected on the rendered HTML DOM tree, again using a full page refresh or incrementally using Ajax or WebSockets.

当某个事件被用户出发后,Web浏览器将发送目标对象和对应的信号(连同所有的表单数据)给Web服务器{function(o, e)},这一过程可以使用全页面刷新,也可以使用Ajax/WebScokets。当请求被验证为正确后,在服务器端,表单数据(如线框文本)首先被处理进而更新部件树的状态。然后,事件通过释放信号的方式被传递给目标部件,信号将把事件传递给所有连接的方法,比如例子中的greet()方法。对部件树的修改将会被追踪,当事件被处理完成后,相应的变化将通过全页面刷新或增量渲染(Ajax/WebScokets)反映在渲染后的HTML DOM树上。

The most interesting thing about the implementation of the greet() method may be the code that is not there: no JavaScript to update the text using DOM manipulations or to re-render the page, no JavaScript code to post the event end line edit value using Ajax or WebSockets, no HTTP request decoding to interpret the line edit value or button event, and no security-related code. All this is handled instead by the library. While this could still be manageable for such a small example, imagine a situation where the page contains various form elements related to different tasks and thus managed by different widgets, and where during event propagation many unrelated widgets get updated.

最有趣的地方是 greet()函数的实现,其中不包含任何JavaScript代码,典型的我们用JavaScript操作DOM更新文本部件;也没有任何JavaScript代码调用Ajax或WebSockets发送事件设置线框值;也没有HTTP请求去解码线框值或按钮事件;也没有处理用户输入不安全因素的代码。可以说:所有以上这些都由Wt库代为处理了!

4. Hangman【Hangman例子

For those of you who forgot the game-play of hangman: the challenge is to guess a word. You can pick a letter, one at a time. If the word contains the chosen letter, it is indicated at the correct location(s). If the word does not contain the letter, you loose a turn and get one step closer to hanging. To win, you need to find the word before you die hanging. In our version, we will let the user choose a dictionary (English or Dutch), and we keep track of users and his high-scores.

简单介绍一下Hangman游戏:实际就是猜词,一次只能选一个字母,如果要猜的词中包含刚选的字母,那么它会显示在词中正确的位置(可能有多个位置[重复出现])。如果不包含该字母,那么玩家记为失败一次,一步步走向被吊死。所以,要想取胜,只能在被吊死前尽力找到所有包含的字母。在例子中,我们让玩家在已有的词典中随机选词,还提供了对用户活动的记录,比如高分榜等。

4.1. A first custom widget 【第一个定制部件


We first discuss the HangmanWidget, which is a custom widget that encapsulates the game itself: it allows a user to play one or more games. It does not deal with updating the user’s score, instead it indicates score update events to other widget(s) using a signal.

首先讨论 HangmanWidget,这是一个定制部件,封装了游戏本身:它允许用户在它的界面上多次玩这个游戏。但它不会去处理如更新用户得分的事情,它只是将得分更新事件通过信号传送给别的部件处理。

The following screenshot shows how the widget is composed of different widgets:

下面的截图展示了 HangmanWidget 包含的部件情况:

 

img/hangman.png

 

The HangmanWidget combines widgets provided by the library (WText: title_, statusText_, WComboBox: language_), and three custom widgets (WordWidget: word_, ImagesWidget: images_ and LettersWidget: letters). As you will see, custom widgets are instantiated and used in pretty much the same way as library widgets, including reacting to events generated by these widgets.

HangmanWidget 组合了Wt库提供的多种部件,包括 WText:title_、statusText_,WComboBox:language_和3个自定义的部件(WordWidget:word_,ImagesWidget:images_和LettersWidget:letters)。正如你将看到的,自定义的部件将被实例化和使用,其方式与Wt库提供的基本部件的使用一样,包括部件对事件的响应。

With this information, we can implement the class definition.

HangmanWidget类的定义如下:

HangmanWidget declaration

HangmanWidget 的声明部分

class HangmanWidget : public Wt::WContainerWidget
{
public:
    HangmanWidget(const std::string &name, Wt::WContainerWidget *parent = 0);

    Wt::Signal<int>& scoreUpdated() { return scoreUpdated_; }

private:
    Wt::WText        *title_;
    WordWidget       *word_;
    ImagesWidget     *images_;
    LettersWidget    *letters_;
    Wt::WText        *statusText_;
    Wt::WComboBox    *language_;
    Wt::WPushButton  *newGameButton_;

    Wt::Signal<int>   scoreUpdated_;

    std::string       name_;
    Dictionary        dictionary_;
    int               badGuesses_;

    void registerGuess(char c);
    void newGame();
};


This widget is implemented as a specialized  WContainerWidget. This is a typical choice for widgets that combine other widgets in a simple layout. Following a customary practice for widgets, we take an optional parent container as the last argument of the constructor. We declare one public method scoreUpdated(), which provides access to the signal that will be used to indicate changes to the user’s score as he plays through games. A  Signal<int> used here, indicates that an integer value will be passed as event information, and will reflect the score update itself (positive when the user wins, or negative when the user looses). Any function or object method with a signature compatible with the signal may be connected to it and will receive the score update.

HangmanWidget作为WContainerWidget的子类实现,这种实现方式也是我们开发中一种典型的实现方式,用以组合各种其他部件于一个框架中。构造函数中,我们使用了一个可选的父容器作为最后一个参数。我们还声明了一个公共方法scoreUpated(),该方法提供了对更新用户分数信号的访问。这里使用了 Signal<int>,表明事件信息中包含一个整数值,该值将影响分数的更新,换句话说,当用户通关为正值,失败则为负值。与这样的信号匹配的函数都可以被连接,这里是更新分数。

The private section of the class declaration holds references to the contained widgets, and state related to the game.

私有成员部分包括了必须的一些部件和游戏状态表达部件。

The constructor implementation shows some resemblance with the hello world application we discussed earlier: widgets are instantiated and event signals are bound. There are some novelties however.

构造函数的实现与Hello World类似,各种部件被实例化,事件信号被对应绑定,但也有些不同之处。

HangmanWidget constructor

以下是 HangmanWidget的构造函数:

using namespace Wt;

HangmanWidget::HangmanWidget(const std::string &name, WContainerWidget *parent)
    : WContainerWidget(parent),
      name_(name),
      badGuesses_(0)
{
    setContentAlignment(AlignCenter);

    title_ = new WText(tr("hangman.readyToPlay"), this);

    word_ = new WordWidget(this);
    statusText_ = new WText(this);
    images_ = new ImagesWidget(MaxGuesses, this);

    letters_ = new LettersWidget(this);
    letters_->letterPushed().connect(this, &HangmanWidget::registerGuess);

    language_ = new WComboBox(this);
    language_->addItem(tr("hangman.englishWords").arg(18957));
    language_->addItem(tr("hangman.dutchWords").arg(1688));

    new WBreak(this);

    newGameButton_ = new WPushButton(tr("hangman.newGame"), this);
    newGameButton_->clicked().connect(this, &HangmanWidget::newGame);

    letters_->hide();
}


Wt supports different techniques to layout widgets that may be combined (see also the sidebar): namely widgets with CSS layout,  HTML templates with CSS layout, or  layout managers. Here, we chose to use the first approach, since we simply want to put everything vertically centered.

Wt支持不同技术实现部件的布局:CSS布局、Wt的 HTML Template、框架管理器等。这里使用了第一种,简单展示。

Other layout options 【 其他布局方式

Although layout managers are a familiar concept in GUI development, CSS is king of layout in web development. Some things are hard to do with CSS though, in particular vertical centering or vertical size adjustments. It is for this purpose that layout managers have been added to Wt. These layout managers use JavaScript to compute the width and/or height of widgets based on dimensions of other widgets.

虽然我们在GUI开发中很熟悉布局管理器,但在Web开发中CSS却是主导。因为有些东西使用CSS很难实现,如纵向居中或纵向尺寸调整,鉴于此Wt基于JavaScript引入了布局管理器,通过JavaScript计算部件及其间的宽度和高度。

The LetterWidget exposes a signal that indicates that the user chose a letter. We connect a private methodregisterGuess() to it, which implements the game logic of dealing with a letter pick. Notice how this event handling for a custom widget is no different than reacting to an event from a push button, making that widget as much reusable as the widgets provided by the library (assuming you are in the business of hangman games).

上面的LetterWidget部件可以释放出一个信号,该信号表明用户选择了一个字母。我们将该信号连接到私有的 registerGuess() 方法上,这个方法可以处理用户选中字母后的游戏逻辑。注意,这个自定义的部件的事件的处理与上面提到的按钮部件的处理没有什么不同,Wt将部件的复用做到最大极致。

To support internationalization, we use the tr("key") function (which is actually a method of WWidget which callsWString::tr(), to lookup a (localized) string given a key. This happens in a message resource bundle (seeWMessageResourceBundle), which contains locale-specific values for these strings. Values may be arg() method of WString, as used for example for the “hangman.englishWords” string which has as actual English value “English words ({1} words)”.

为了支持I18N,我们使用了 tr("key") 函数,这个函数实际上是调用WString::tr()根据给定的key去寻找本地化字符串,在哪里寻找呢?实际是通过 WMessageResourceBundle指定的文件中。另外,arg()方法返回一个字符串值,这个值用于替换本地化字符串中的标示第几个参数的一个表达式。本例中 tr("hangman.englishWords")实际得到的本地化字符串是“English words ({1} words)”,(因为作者是英语写作),里面有个表达式{1},arg()结果就取代{1},具体值是18957,表示英文词典中共有18957个单词。

For completeness, we show below the rest of the HangmanWidget implementation.

为了完整,我们贴上HangmanWidget的实现代码,如下:

void HangmanWidget::newGame()
{
    WString title(tr("hangman.guessTheWord"));
    title_->setText(title.arg(name_));

    language_->hide();
    newGameButton_->hide();

    Dictionary dictionary = (Dictionary) language_->currentIndex();
    word_->init(RandomWord(dictionary));
    letters_->reset();
    badGuesses_ = 0;
    images_->showImage(badGuesses_);
    statusText_->setText("");
}

void HangmanWidget::registerGuess(char c)
{
    bool correct = word_->guess(c);

    if (!correct) {
        ++badGuesses_;
        images_->showImage(badGuesses_);
    }

    if (badGuesses_ == MaxGuesses) {
        WString status(tr("hangman.youHang"));
        statusText_->setText(status.arg(word_->word()));

        letters_->hide();
        language_->show();
        newGameButton_->show();

        scoreUpdated_.emit(-10);
    } else if (word_->won()) {
        statusText_->setText(tr("hangman.youWin"));
        images_->showImage(ImagesWidget::HURRAY);

        letters_->hide();
        language_->show();
        newGameButton_->show();

        scoreUpdated_.emit(20 - badGuesses_);
    }
}


4.2. Unleashing (some of) Wt’s power 【进一步显示Wt的能量

Until now, we introduced a rather unique way to develop web applications, and a powerful building block for reuse – the widget. The next widget in the Hangman game that we will tackle, is one we’ve already used just earlier: theImagesWidget. It illustrates an important aspect of the library that highly enhances the user experience for users with an Ajax session (which should be the majority of your users). One of the most appealing features of popular web applications like Google’s Gmail and Google Maps is an excellent response time. Google may have spent quite some effort in developing client-side JavaScript and Ajax code to achieve this. With little effort – indeed almost automatically – you can get similar responsiveness using Wt, and indeed the library will be using similar techniques to achieve this. A nice bonus of using Wt is that the application will still function correctly when Ajax or JavaScript support is not available. The ImagesWidget class, which we’ll discuss next, contains some of these techniques. Hidden widgets are prefetched by the browser, ready to be displayed when show() is called.

介绍到此,我们明确了采用Wt使用一种非同寻常的方法开发Web应用,一种强大的部件模块的复用方式。下面要解读的是游戏中ImagesWidget,该部件展示了Wt库的一种重要性能,能够进一步提高Ajax会话的用户体验。这个用户体验的最明显的例子是Gmail和Google Maps,估计Google花费了很大努力开发客户端JavaScript和Ajax代码以获得这个效果。但在Wt中,你可以通过很小的努力-实际上不用努力,Wt自动实现了,不过话说穿,与Google一样使用了相同的技术。另外,使用Wt还有另一个好处,即使是浏览器不支持Ajax或JavaScript时Wt应用照样运行。ImagesWidget部件就是这些技术的体现。实际上,这个部件的窍门是:预先载入,但隐藏,等需要时(show()被调用时)显示出来。

ImagesWidget: implementation 

ImagesWidget的实现如下:

ImagesWidget::ImagesWidget(int maxGuesses, WContainerWidget *parent)
    : WContainerWidget(parent)
{
    for (int i = 0; i <= maxGuesses; ++i) {
        std::string fname = "icons/hangman";
        fname += boost::lexical_cast<std::string>(i) + ".png";
        WImage *theImage = new WImage(fname, this);
        hangmanImages_.push_back(theImage);

        theImage->hide();
    }

    WImage *hurray = new WImage("icons/hangmanhurray.jpg", this);
    hurray->hide();
    images_.push_back(hurray);

    showImage(HURRAY);
}

void ImagesWidget::showImage(int image)
{
    image(image_)->hide();
    image_ = index;
    image(image_)->show();
}

WImage *ImagesWidget::image(int index) const
{
    return index == HURRAY ? images_.back() : images_[index];
}



In the constructor, we meet one more basic widget from the library: WImage, which unsurprisingly corresponds to an image in HTML. The code shows how widgets corresponding to each state of the hangman example are created and added to our ImagesWidget, which specializes a WContainerWidget. Each image is also hidden – we’ll want to show only one at a time, and this is implemented in the showImage() function.

构造函数中,我们使用了 WImage,实际就是HTML中的图像。代码显示对应于每种状态的图片是如何创建并加入到 ImagesWidget中,该类也是 WContainerWidget 的一个衍生。每张图片都预先隐藏,未来就一次一个的显示,使用 showImage() 方法。

But why do we create these images only to hide them? A valid alternative could be to simply create the WImage that we want to show and delete the previous, or even better, to simply manipulate the image to point to another URL? The difference has to do with the response time, at least when Ajax is available. The library first renders and transfers information of visible widgets to the web browser. When the visible part of web page is rendered, in the background, the remaining hidden widgets are rendered and inserted in the DOM tree. Web browsers will also preload the images referenced by these hidden widgets. As a consequence, when the user clicks on a letter button and we need to update the hangman image, we simply hide and show the correct image widget, and this no longer requires a new image to be loaded. An alternative implementation would have caused the browser to fetch the new image, making the application appear sluggish. Using hidden widgets is thus a simple and effective way to preload contents in the browser and improve the responsiveness of your application. Important to remember is that these hidden widgets do not compromise the application load time, since visible widgets are transferred first. A clear win-win situation thus.

但是为什么我们创建这些图像并先隐藏起来,很简单,就是为了较快的反应时间。Wt库首先渲染可见部件,之后,再渲染不可见的,并悄悄插入到DOM树中。当然浏览器也会预先载入部件所代表的图片。这样做的结果就是,未来根据用户的选择,图片的出现就成了简单的显示/隐藏,根本不需要新图片的载入。如果不这样,浏览器会重新载入图片,应用就会变慢。请注意:最重要的是隐藏部件不会消耗应用的载入时间。

4.3. Internal paths 【内部路径

Ignoring the login screen for a moment, then our application has two main windows: the game itself and the high scores. These are implemented by the HangmanWidget which we discussed earlier, and a HighscoreWidget (which we will not be discussing in this tutorial). Both are contained by a WStackedWidget, which is a container widget which shows only one of its contained children at a time (and which, in all honesty we should have used to implement the ImagesWidget, were it not that we wanted to explain a bit more about preloading of contents). Unless we do something about it, a Wt application presents itself as a single URL, and is thus a single-page web application. This is not necessarily bad, but, it may be better to support multiple URLs which allows a user to navigate within your application, bookmark particular “pages”, or put links to them. It also is instrumental to unlock the contents within your application to search engine robots. Wt provides you with a way to manage URLs which are subpaths of the application URL, which are called “internal paths”.

考虑这个例子,忽略登陆页面,那么就剩下两个主窗口:一个是游戏页面本身(HangmanWidget),另一个就是高分榜页面(HighscoreWidget),这两个页面/部件都存放于 WStackedWidget中,它也是个容器部件,实现一次只显示一个子部件。Wt应用通常情况下是单页面Web应用,这也并不坏,但如果支持多个URL会更好,那便会允许用户针对URL进行导航,或是利用收藏夹/书签收藏特定页面,另外还可以辅助向搜索机器人解锁内容。Wt利用内部路径机制提供了这一功能。

 

img/internalpath.png

 

Internal paths are best used in combination with anchors (provided by another basic widget, WAnchor). An anchor can point either to external URLs, to private application resources (which we’ll not discuss but are useful for dynamic non-HTML contents), or to internal paths. When such an anchor is activated, this changes the application’s URL (as one could expect), and the internalPathChanged() signal is emitted. Thus, to respond to an internal path change, we connect an event handler to this signal.

内部路径最好和 WAnchor配合使用。通常锚可以指向外部URL,也可以指向应用的资源,更可以指向我们所说的内部路径。当用户点击锚时,internalPathChanged() 信号将被释放,通过获取这个信号,我们得以处理页面变化。

Internal paths: a perfect illusion 【 内部路径:完 障眼法

Normally, when a user navigates a link, the browser fetches the document linked to and replaces the current HTML page with the new page. This system of "full page refreshes" causes the browser to re-render the whole page each time, and is exactly what Ajax came to avoid. Using new features in HTML 5 (JavaScript History support), and falling back to tricks involving URL fragments in older browsers, Wt creates the illusion of navigating to a new page, but instead uses Ajax to update the page to reflect the URL change and navigation events. At the same time, search engines and plain HTML sessions will view your application using full page refreshes.

正常情况下,当用户导航到一个链接时,浏览器将获得获得所链接的新内容,并替换当前的HTML页面,这就是全页面刷新,它将引起浏览器每次都重新渲染整个页面,当然这也就是Ajax产生的原因。通过使用HTML5(JavaScript历史记录支持)的新特性,以及以前浏览器的一些通过#号URL处理的特殊技巧,Wt创造了切换到新页面的障眼法,即通过Ajax更新页面来反映URL的变换和对应的导航事件。同时,搜索引擎和纯HTML会话将以全页面刷新的方式看待Wt应用。

This is the implementation of the method that we connected: 

例子中内部路径的具体应用如下:

HangmanGame: internal path handling

void HangmanGame::handleInternalPath(const std::string &internalPath)
{
    if (session_.login().loggedIn()) {
        if (internalPath == "/play")
            showGame();
        else if (internalPath == "/highscores")
            showHighScores();
        else
            WApplication::instance()->setInternalPath("/play",  true);
    }
}


Thus, if a user is logged in, we show the game when the path is “/play” and the high scores when the path is“/highscores”. For good form, we redirect all other paths to “/play” (which will end up triggering the same function again). In our game we make authentication (whether a user is currently logged in orthogonal to the internal paths): in this way a user may arrive at the game using any internal path, log in, and automatically proceed with the function for that internal path. You may imagine that this is what you want in a complex application: the login function should not prevent the user from directly going to a certain “page” within your application.

这样一来,如果用户是登陆状态,那么,当路径是“/play”时就游戏页面,当“/highscores”时就是显示高分榜页面。为了形式良好,我们把其他路径(特别输入错误的路径)都指向“/play”。在游戏应用中,我们做了一些身份认证工作,也就是用户是否登录进来。这里,你可以想象,对于复杂应用:login认证可以有效阻止用户直接进入某些(可能是我们不想使之进入的)页面(功能块)。

We did not discuss other parts of the hangman game example application: namely how user scores are stored, and the authentication system. Database access is implemented using a Wt::Dbo, which is a C++ ORM that comes with Wt. This tutorial introduces the database layer. The authentication module, Wt::Auth, as used in this example, is introduced here.

对于本例的其他部分我们就不讨论了:如如何存储用户得分、如何认证。数据库的访问使用了Wt::Dbo,它是Wt独有的C++ORM。认证模块使用了Wt::Auth。

5. Summary 【总结】

In this tutorial we provided you with the basic techniques for creating web applications using Wt, from small to more complex. While a tutorial is no place to discuss a real-life complex application, with a small leap of faith, it should be clear that the same techniques of creating an application using widgets as building blocks, provides an effective way to manage complexity (and evolving features), while freeing the application developer of many technical aspects and quirks associated with the web platform. Because of the many similarities between Wt and other GUI toolkits, developers can treat the web browser in many aspects as just another GUI platform.

本教程中我们向您讲述了Wt创建Web应用的基本技术,有简单的也有复杂的。虽然我们还没有讨论真正的复杂的项目,但相信你能够清楚复杂项目使用的技术是相同的,无非就是部件模块的搭建。至此大家应该会看到,Wt提供了管理复杂应用的高效手段,同时让开发者免于面对开发平台相关的技术细节和陷阱;最后,Wt也让开发者像开发桌面GUI应用一样开发Web应用,浏览器也就是我们的GUI平台。

In this tutorial we touched on many important Wt features. But Wt adds much more to your toolbox that weren’t mentioned: file uploads, dynamic resources, painting, tree and table views and their models, a charting library, animation effects, WebSockets, built-in security measures, authentication, etc… For more information, please see theonline documentation.

本教程涉及到了Wt的基本特点,但Wt还在不断地增加新的内容,如文件上传、动态资源、绘画系统、树表视图及其模型、图表库、动画、WebSockets、内置安全、认证等等,更多内容请阅读在线文档。

Last updated 2011-12-14 11:25:08 CET
翻译时间:2012-01-19


参考文献

[1] 谷歌推出Dart :剑指JavaScript  http://tech.it168.com/a2011/1018/1260/000001260105.shtml

[2] SEO搜索引擎优化与JavaScript http://www.net366.net/blog/post/12.html

[3] 痛陈javascript的缺点 http://www.php100.com/html/webkaifa/javascript/2009/0302/1681.html

[4] Partial-Page Rendering Overview http://msdn.microsoft.com/en-us/library/ie/bb386573.aspx

[5] What is WebSocket? http://websocket.org/index.html

[6] Convenience function http://en.wikipedia.org/wiki/Convenience_function

© 著作权归作者所有

zhmsong
粉丝 41
博文 126
码字总数 65130
作品 0
海淀
程序员
私信 提问
加载中

评论(1)

IT追寻者
IT追寻者
太牛了,有QT的感觉!
入门开源ERP最全面的ADempiere 资料和compiere 资料全集

Compiere ERP&CRM截图如下: ADempiere ERP截图如下: Compiere ERP&CRM3.3软件的安装在线视频: http://www.humanerp.com/compiere/installation_tutorials.html oracle11g软件及jdk1.6和c......

opensoruce
2010/08/27
19.1K
17
干货分享,数据可视化零基础到精通学习路线

     数据可视化,是关于数据视觉表现形式的科学技术研究。其中,这种数据的视觉表现形式被定义为,一种以某种概要形式抽提出来的信息,包括相应信息单位的各种属性和变量。 它是一个不断...

蓝胖子讲大数据
2017/11/07
0
0
Docker 学习资源整理

作者:林梓 链接:https://zhuanlan.zhihu.com/p/23508637 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 Docker 是近年来非常火的容器技术,而且啊 Do...

落叶追风
2016/11/22
31
0
PYTHON资源入口汇总

官网 入口 官方文档 英文 document 2.7.6 入口| 标准库 document 3.x 入口 The Hitchhiker’s Guide to Python 入口 Python Monk 入口 中文 document 2.7 入口 非官方 google的python文档 Go...

好铁
2016/02/12
99
0
我的Python3萌新入门笔记

我的Python3萌新入门笔记 Python自动化运维2017-12-128 阅读 Python教程 先来个镇楼图! Python3萌新入门笔记是一系列真实的自学笔记。 当然,它也是比较全面的入门教程。 到上一篇笔记为止,...

Python自动化运维
2017/12/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

好程序员Java教程分享Java的两种跳转语句

好程序员Java教程分享Java的两种跳转语句,Java跳转语句用于实现循环执行过程中程序流程的跳转,在Java中的跳转语句有 break语句和 continue语句两种。接下来分别进行详细地讲解。 1、Java的第...

好程序员IT
11分钟前
2
0
BCFTools安装

1 缺少bzlib.h:sudo apt-get install libbz2-dev 2 缺少lzma.h:sudo apt-get install liblzma-dev 安装时需同时具备bcftools和htslib工程,在bcftools文件夹中执行 #make #sudo make insta......

悲催的古灵武士
15分钟前
1
0
django2.2数据库删除以后无法重新创建问题处理

https://blog.csdn.net/androidstarjack/article/details/89216892

平头哥-Enjoystudy
17分钟前
0
0
Amino在实际项目中的安装和使用

Amino作为性能卓越的无锁集合包,List线程安全集合执行速度之快远超CopyOnRightArrayList,但它唯一的缺点就是无法用maven直接安装。 我们需要在其官网下载源码,直接安装到我们本地的私有库...

算法之名
18分钟前
8
0
Intellij热部署插件JRebel

Intellij热部署插件JRebel 2018年08月30日 23:15:48 慕容雪_ 阅读数 9674 版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/weixin_42831477/article/details/82229436...

嘿嘿嘿IT
20分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部