Wt C++工具包的简介

Wt C++工具包的简介

 

Koen Deforche和Wim Dumon

2006年1月

----------------------------------------------------------------------------------------------------

本文由 陶桃猪 翻译

如需转载及引用

请注明出处

谢谢

---------------------------------------------------------------------------------------------------

限于本人翻译水平

定有错漏不妥之处

欢迎指正

----------------------------------------------------------------------------------------------------

邮箱:taotaozhulz@163.com

----------------------------------------------------------------------------------------------------

博客:http://www.lonemover.com

----------------------------------------------------------------------------------------------------

2011年4月10日

----------------------------------------------------------------------------------------------------

 

1.简介

未来Web应用程序技术

C++已经确立为开发许多应用的语言,像桌面应用,电子邮件客户端,数据库引擎等等。但是C++开发Web应用是非常少的。在Web应用开发中占统治地位的语言是JAVA, PHP,Python和Perl。并且十分特别的是,PHP是一个专门为Web开发设计的语言。其他的一些语言都有各自的Web开发框架。例如,JAVA的J2EE和Structs,Perl的Perl::CGI或者Python的Zope。这些框架提供了会话管理,支持来自浏览器的HTML表单和Cookies传输数据的解析,并且能够帮助在回应中生成新的页面。

以上框架所遵循的模型如图1所示。

1

 

每一步,浏览器向服务器请求一个页面,并且可能提交包含一系列的表单值的请求。在服务器端,应用程序处理这个请求,标识这个会话,并且执行业务逻辑。最后,这个应用程序生成了一个响应页面。这个响应页面可能不仅包含HTML,还包括JavaScript来加强应用程序的交互性。但是JavaScript在不同的浏览器里表现是不同的,所以需要努力将其写的很好的兼容不同的浏览器。

然而,最新的获得很大成功的Web应用,像Google的Gmail或者Google地图,并不遵循这个页面接着页面的模式。Google使用的是一种将JavaScript和服务器端技术结合起来的,通常被称为Ajax的技术,使用这个技术可以在不刷新页面的情况下从服务器上获得数据并更新页面。Ajax的原理主要是利用JavaScript在后台向服务器发送HTTP请求(例如:有没有新的邮件?)。服务器端以XML的形式来生成合适的响应(例如:“Yes,2:(1)..,(2)...”)。最后,客户端的JavaScript解析这个响应并且通过操作DOM来完成页面的更新(例如在收件箱里加两条电子邮件的消息)。

 

在2005年,Ajax被大肆的宣传,导致了Web应用程序开发的根本改变。每一个事件都需要向客户端传送一个完整的页面已经不需要了。Ajax为Web加入了新的可能,因此Ajax有点时候是和新的更加交互性的Web,也就是Web2.0相关联的。同时,使用这个技术面临着很多挑战。为了使用Ajax,应用开发者需要学习并理解许多的技术,除了服务端的框架和HTML/CSS开发者需要学习使用JavaScript操作DOM的DHTML,为了能使用JavaScript生成有效的GET或者POST请求开发者需要学习一些HTTP协议的细节,最后需要学习XMLHttpRequest API。同时,需要考虑不同浏览器的兼容性,以及对旧版本浏览器的支持。

Wt来了!

和传统Web应用程序框架的页面模型相比,Wt或者一个传统的GUI库所遵循的模型是基于widgets的,看一下图2。

2

这些widgets在概念上是以树的形式组织的,并且回调函数和特定的事件相关。为了响应一个事件,回调函数会被调用,一些任务就会被完成,并且或者widget树会被修改。

Wt是一个针对Web应用开发的C++ widget库。Wt应用模型和现存的GUI库(例如微软的MFC或者TrolTech的Qt)是很相似的。同时,Wt为开发者隐藏了很多潜在的技术细节(HTML,Forms/CGI, JavaScript, Ajax等等),就像Qt库如何隐藏潜在的X库或者微软的Windows GUI的细节一样。

因为Wt的API对底层的技术(Forms,JavaScript或者Ajax)做了抽象,Wt根据浏览器支持的技术来和浏览器通信。使应用能够在一大堆浏览器种运行的责任从应用开发者身上转移到了Wt库开发者身上。

在接下来介绍Wt的文章中,我们将会首先对主要的类和特性做一个梗概的介绍,同时也包括Wt在屏幕的背后做了一些什么。接下来,我们将会展示一下如何使用Wt来实现一个经典的名为“hangman”的猜词游戏。

 

2.库概要

主要的控件

整个用户接口被组织为一个具有继承性的WWidget对象的树。一个WWidget对应于一个可视化的实体,例如一个文本块(WText),一个表(WTable),一条可被编辑的行(WLineEdit),或者一个更加复杂的组合widget(实现WCompositeWidget的类)。用户接口,这里指的是Web的页面,是通过创建和维护一个widget树来实现的。每一个WWidget对应于一块矩形的区域,并且维护了这块区域里的内容和事件。

Wt这个库提供了许多直接对应于HTML提供的widgets的基本的widgets,这些widgets都是WWebWidget的子孙(WText, Wtable, Wimage,...).这些widgets间接的维护服务器端的HTML DOM。相对比而言,WCompositeWidget对象是通过组合其他的widget而实现的widget.虽然Wt库提供了一些组合widget(例如一个树列表和一个自动完成的编辑行),这些widgets并不属于Wt库,因为他们是在Wt之上实现的。

每一个Wt应用程序必须以一个WApplication对象的实例化作为开始。这个对象管理着widget树的根,浏览器容量信息并且使用地区设定以及消息资源束来管理国际化支持(后面会谈到)。

 

会话管理

和多个应用实例如何同时运行相似,Wt内核系统会为每一个独立的Web会话生成一个Wt应用。

每一个新的“会话”意味着一个起始于wmain()执行的路径已经产生,wmain()是Wt应用的入口。所以,程序员只需要实现单用户的应用程序,除非用户会和一个公共的组件或者和每一个人交互(例如数据库),在这种情况下必须使用标准的数据共享机制。

当前版本的Wt实现了使用不同的进程来运行不同的执行路径。所以,对于每一个新的会话来说,Wt会生成一个新的进程。这样的一个主要的好处在于可以享受到用户会话中的内核级别的内存保护。所以,简单的程序错误不会自动的危及会话保密性。这个方法的不好的一面在于它的的成本:当前的内核实现对于每一个进程来说,可能需要一定量的非可交换的内存。在未来,Wt会提供不同的线程实现的选择,包括用户级别的线程。

信号/槽 事件响应

Wt使用一个信号/槽的机制来实现事件的响应。用户界面的事件,例如鼠标点击,或者文本修改,会被Wt暴露为和特定widget相关的信号。为了响应事件,程序员将信号和一个槽连接。任何的拥有一个和信号相兼容的签名的对象方法,都可以用来作为槽。当信号被触发的时候,所有和这个信号连接的槽都会被调用。这个信号/槽的机制是一个建立良好的,类型安全的并且自我管理的回调机制。

国际化

考虑到互联网固有的广域特征,国际化和本地化是一个网站非常重要的属性。Wt通过提供消息资源束来帮助实现网站的国际化。一个WMessage对象提供了一个依赖当前本地的一份文本。为用户显示文本的控件(例如WText)会得到一个WMessage而不是一个自然的文本。翻译的不同区域的消息会以XML的形式存储在消息资源文件中,每一个区域一个文件。当需要改变应用的区域的时候,使用WApplication::setLocale(),这样的话应用程序会自动更新对应widgets的本地文本。

无干扰的升级

Web应用的一个主要的优势在于发布者可以很方便的升级应用程序的所有拷贝,仅仅是通过在他的网站上部署一个新的版本。通常情况下,发布者不希望在部署新的版本的时候终止现存会话的运行,并且为新的会话提供新的版本。这种无打扰的升级是Wt默认的升级方式。

 

会话生命周期

Wt在客户端和服务器之间使用了一个keep-alive的协议来决定会话生命周期。只要Web页面在用户的浏览器中显示着,会话保持激活状态,否则会话将会被终止。另外,应用程序可以通过调用WApplication::quit()终止会话(例如为了响应用户的登出请求)。以上的方式,当一个会话终止的时候,主要的widget会被销毁。这样应用程序就可以释放资源了。

 

Wt库是如何工作的?

Wt实现了两个主要的工作:渲染并维护浏览器中的HTML DOM树和响应用户输入和事件,例如鼠标点击。

所有可以被捕获的事件都映射到信号,这个信号和特定的widget相关联。当一个事件被用户触发了之后,(例如点击OK按钮),浏览器和目标对象和对应的信号通信(例如OkButton->clicked),和所有表单数据传送到服务器(使用Ajax或者纯HTML表单提交)。在服务器端,对应的Wt应用实例处理所有的表单数据来更新widget树的状态。然后,通过触发目标对象的信号,这个事件就被触发了,

信号触发之后,所有连接的槽都会被触发。这些槽会执行业务逻辑并且修改widget树。对widget树的修改会被Wt库跟踪,并且会被转化为HTML DOM树的服务器端模型。最后,依赖于通信的方式,要么是DOM树改变了,要么是完整修改过的DOM树被传输到浏览器,完成整个事件循环。

因为在使用widget树的用户接口和渲染树的机制上的分离,当Ajax有效的时候,Wt会优化对增长响应的渲染。Wt在和浏览器第一次通信的过程中仅仅传送可视化的widget改变,通过这个方法实现上一句的目标。页面一被加载,剩下的隐藏的widgets改变会在后台被传送。所以,最初的响应被优化同时接下来的一系列widget的显示很活泼。

 

3.向导

向导这一章节讨论了两个小程序来验证不同的Wt库概念。第一个程序是Hello World程序,这个小程序主要介绍了两个关键的概念:widget树和信号/槽。第二个相对来说大一点的程序是一个在线版本的经典hangman游戏,这个程序包含了一个通过一个小的数据库支持的用户排名系统。这个游戏的在线版本是有效的(根据本人翻译时的测试结果,链接失效,提供了备选的[1])。这个hangman游戏验证了一个针对更加复杂的Web应用程序是如何被构建和管理的,如何实现自己的widgets,信号和槽,如何处理布局并且提供了一个数据是如何从数据库解析出来显示在网站上的。这个游戏包括注释的完整代码大概有900行。我们为本向导选取了最有趣的部分。

Wt文档页面包含了暴露在Wt API下的类,方法,信号和槽的完整版本。即使是这个hangman游戏仅仅使用了有效的类和方法的一小部分。完整的样例程序的源码和构建它们的Makefiles,都包含在了Wt源码的发行版本中了。

无所不在的Hello World

任何一个程序的入口都是wmain( )函数。最简单的Wt程序必须实例化WApplication对象,并且调用应用程序的死循环。对于hello world应用来说,一个WText对象和一个WPushButton加入到了widget树当中。这样它们就可以显示在浏览器中了。点击Quit按钮会终止会话。这是通过连接Quit按钮的点击信号和应用程序的quit()槽来实现的。

这里需要强调的是,没有其他的更加聪明的方法来显式的调用Quit这个函数。当用户浏览离开了当前的应用,Wt将会检测到keep-alive 消息不在接收了,并且Wt将会终止会话就好像quit()被调用了一样。

列表1. quit按钮的Hello World

#include <WApplication>

#include <WText>

#include <WPushButton>

 

int wmain(int argc, char **argv)

{

WApplication appl(argc, argv);

//Widgets can be added to a parent

//by calling addWidget() …

appl.root()->addWidget(new WText(“<h1>Hello, World!</h1>”));

 

// … or by specifying a parent at

// construction time

WPushButton *Button = new WPushButton(“Quit”, appl.root());

Button->clicked.connect(SLOT (&appl, WApplication::quit));

 

return appl.exec();

}

 

我的第一个widget

我们用登陆这个过程来开始我们对hangman这个游戏的讨论。登陆过程是由LoginWidget来处理。我们已经将业务逻辑减小到最少了,而且事实上是有一点简单了。LoginWidget请求使用他的名字和密码来向登陆返回用户,并且选择被用作游戏的词典里选择一个有效的字典。如果用户不在当前的数据库中,我们假设他是一个新用户并且自动把他加入数据库中。如果登陆成功,一个确认的信息将会被显示出来,否则就会提示出错并重试。

Hangman的LoginWidget验证了使用一个比较好的面向对象的风格来写一个自我维护的拥有清新的接口并且可以插入任何需要的地方的widget的可能性。拥有非标准的行为的LoginWidget可能不会立即成为一个可以重用的组件,但是至少证明了接口的规则。LoginWidget只有两个公共的成员函数。第一个是构造函数,构造函数的参数为它们的父类,和自定义的widgets一样这是固定的方式。第二个成员是一个当登陆成功以后会被触发的信号。这个信号同样拥有用户名,和选择的游戏字典。

GUI库的面向对象的widget树导致了很多的widget重用,从像KDE这样的大规模的桌面项目中来看是明显的。特别的,在widget覆盖十分简单的概念的地方,对一个widget的重用常常不比将一个widget实例化在一个合适的对象树的位置简单。程序剩下的部分通过安装回调函数来响应事件和使用widget的方法来和widget交互。传统的GUI widget例子是一个可以展开的树列表,一个打开文件的对话框,等等,这些都被包含在了GUI库里面。Wt为Web编程世界引入了一个很相似的模式,并且引导程序员把一个Web应用划分为定义良好并且自我维护的widgets。

 

列表2 LoginWidget类定义。

Class LoginWidget : public WContainerWidget

{

public :

LoginWidget ( WContainerWidget *parent = 0);

public signals:

Wt::Signal <std::string , Dictionary> loginSuccessful;

private slots:

void checkCredentials();

void startPlaying();

private:

WText *IntroText;

WLineEdit *Username;

WLineEdit *Password;

WComboBox *Language;

std::string User;

Dictionary Dict;

void confirmLogin(const std::string text);

};

在列表2中,我们展示了LoginWidget类的定义。LoginWidget定义了一个公共的信号,loginSuccessful,并且中间使用了一些槽来响应用户的事件。因此,我们必须直接或者间接的继承自WObject。然后我们就可以声明将会被类定义使用的信号和方法。

LoginWidget继承自WContainerWidget。WContainerWidget是一个可以装载和管理子widget的widget。一个widget的父亲总是一个WContainerWidget或者它的衍生类(例如WStackedWidget或者WTableCell)。在WContainerWidget中的孩子的布局是根据它们被加入的顺序来决定的。一个widget 的inline属性决定了它在容器里面默认的布局行为。In-line widgets的布局就像在文本里面的文字,根据行来排列并且包裹在容器的右边。非in-line widgets被格式化为一个新的段落。Widgets可以去除默认的布局流,通过各种不同的方式来定位,但是这里我们不讨论这个主题。反而,下面我们讨论构造函数,作为一个验证的例子,一个WTable将会用来创建一个更加复杂的布局。

这个登陆的widget,被Firefox渲染,如图3所示。

3

生成这个交互表单的代码完全包含在LoginWidget的构造函数中,如列表3所示。

列表3. LoginWidget构造函数的实现。

 

LoginWidget::LoginWidget(WContainerWidget* parent) : WcontainerWidget (parent)

{

setPadding(WLength(100), Left | Right);

WText *title = new WText(“Login”, this);

title->decorationStyle().font().setSize(WFont::XLarge);

IntroText = new WText(

“<p>Hangman keeps track of the best”

“players. To recognize you, we ask you ”

“to log in. If you never logged in”

“before, choose any name and ”

“password. </p>”,

this);

WTable *layout = new WTable(this);

Wlabel *usernameLabel = new Wlabel (“User name:”, layout->elementAt(0, 0));

layout->elementAt(0, 0)->resize(WLength(14, Wlength::FontEx), WLength());

Username = new WLineEdit(layout->elementAt(0,1));

usernameLabel->setBuddy(Username);

WLabel *passwordLabel = new WLabel(“Password:”, layout->elementAt(1,0));

Password = new WLineEdit(layout->elementAt(1,1));

Password->setEchoMode(WLineEdit::Password);

passwordLabel->setBuddy(Password);

 

Wlabel *languageLabel = new WLabel(“Language:”, layout->elementAt(2,0));

Language = new WcomboBox(layout->elementAt(2,1));

Language->insertItem(0, “English words (18957 words)”);

Language->insertItem(1,”Nederlandse woordjes (1688 woorden)”);

languageLabel->setBuddy(Language);

 

new WBreak(this);

WPushButton *LoginButton = new WPushButton(“Login”, this);

LoginButton->clicked.connect(SLOT(this, LoginWidget::checkCredentials));

}

 

构造函数引入了一些新的概念。我们碰到了WText和WPushButton已经在hello world的例子里面了。新的widgets拥有一些让人惊奇的地方。WLineEdit提供了一行文本的编辑输入,WComboBox提供了一个下拉的选择框。后者是通过使用insertItem()来填充的。我们使用WLabel类来为这三个输入域提供标签,并且使用setBuddy()将它们和对应的输入域绑定。通过使用WLabel而不是WText,用户可以通过点击相应的标签来获得对应输入域的焦点。WTable是一个表,在这个例子中,WTable是用来布局的。表单元,可以用elementAt(row, column)来访问,是一些文本,行输入和组合框 widget的父类,因此它们是存储在一个数组当中的。最后,WBreak和HTML里面的行断标签(<br />)等同的,会使接下来的widgets序列开始一个新的行。

调用setPadding()可以在LoginWidget的边界以及它的孩子们之间添加空白。WLength类提供了设置大小的CSS方法的一个接口。默认的构造函数会创建一个‘自动’的长度。当使用参数来构造的时候,一个值和单位(WLength::Pixels)将会被指定。所有的CSS单位(pixels, font height, font width, cm, percentage, ...)都是被WLength类所支持的。在setPadding()下面几行,WLength类又一次的出现了,在这一行表单元大小被重置 了。表的宽度被设置为14个字宽单元,但是高度保持默认。

Wt最后一个重要的一方面是在它的构造函数中使用decorationStyle()来访问widget的样式属性,我们这里使用它来设置标题的字体大小。在hello world例子中,我们使用<h2>...</h2>旧的的风格,但是这里我们验证了你可以应用基于CSS的样式。这个方法还会在其他的函数中出现。

我们使用setEchoMode()来用*掩饰输入的密码。最后,connect()调用和hello world应用里面的很相似,但是这里我们将clicked信号和LoginWidget::checkCredentials槽相连接。

 

列表4. checkCredentials()和confirmLogin()方法。

Void LoginWidget::checkCredentials()

{

User = Username->text();

std::string pass = Password->text();

Dict = (Dictionary) Language->currentIndex();

if(HangmanDb::validLogin(User, pass)){

confirmLogin(“<p>Welcome back,”+User+”</p>”);

}else{

IntroText->setText(“<p>You entered the wrong “

“username/password, please try”

“agin.</p>”);

IntroText->decorationStyle().setForegroundColor(Wt::red);

Username->setText(“”);

Password->setText(“”);

}

}

 

void LoginWidget::confirmLogin( const std::string text)

{

clear();

WText *title = new WText(“Loging successful”, this);

title->decorationStyle().font().setSize(WFont::XLarge);

new Wtext(text, this);

(new WpushButton(“Start playing”, this))->clicked.connect(SLOT(this,

LoginWidget::startPlaying));

}

 

在checkCredentials()中,我们验证输入的用户名和密码。因此,这是关于如何获得用户输入以及网页如何会由于用户的输入而被修改的一个比较好的例子。在前三行,由用户提供的用户名,密码和语言选择被WLineEdit和WComboBox widgets获得,它们分别使用的是text()和currentIndex()方法。这些方法在没有程序员介入的情况下总是返回给这些widgets最新的值。这个对于一个GUI库来说并不是很让人惊奇,但是和传统的令人乏味的基于表单的内容获取的Web应用框架比起来,这是一个很大的简化。

在接下来的代码中,身份被验证了,并且当登陆成功的时候,我们会调用confirmLogin().否则,我们会使用setText()来改变在IntroText里面存储的信息,通知用户登陆失败了。另外,我们修改信息文本的颜色为红色,这样可以更好的提示用户。文本颜色是另外一个可以使用decorationStyle()来访问的属性,之前我们使用decorationStyle()改变标题字体的大小。最后,我们通过清空用户名和密码的文本来完成槽的实现。

这个槽的实现的最有趣的,也许是让人意外的方面可能不在这里的代码里。Wt拥有两种更新浏览器页面的可能的方式:一个是让浏览器跳转到一个新的页面,另一个通过JavaScript,Ajax和DHTML来更新当前的页面。因为代码没有指定使用的机制,只指定了想要的结果,Wt会依赖于客户端支持JavaScript 和 Ajax的程序来选择使用的机制。

我们特意把数据库的接口简化了。HangmanDb::validLogin()验证了用户名和密码是否存储在数据库中。如果失败了,我们就尝试通过HangmanDb::addUser()来增加新的用户。如果用户名已经存在数据库中了,这个调用将会失败。即使这些函数的实现不会在这篇文章中讨论,但是还是很值得在这里提一下:这里使用的是MySQL++库和一个MySQL数据库交互的。

当登陆成功的时候,我们显示一个欢迎的信息和一个登陆成功的提示,这个登陆成功的提示由confirmLogin()实现。如果你到目前为止对LoginWidget的实现消化的不错的话,你将会发现这个方法包含了一个新的特点。首先我们使用clear()来清理容器widget的内容。最后,一个语句我们就创建了一个新的按钮并且立即把clicked信号和startPlaying()槽连接了起来。

4

 

清单5.LoginWidget::startPlaying()槽的实现

void LoginWidget::startPlaying()

{

emit(loginSuccessful(User, Dict));

}

 

startPlaying()槽演示了在这种有参数的情况下如何触发一个信号。响应信号做些什么取决于和这个信号连接的槽。

这样就完成了LoginWidget的完整实现。在剩下来的向导里,我们会重用一些LoginWidget的一些相同的概念,来创建高响应速度的支持Ajax的Web应用。因为widget的抽象,你不需要学习JavaScript甚至需要了解的HTML的知识也是很少的。另外一个方面,Wt并没有很费力的对CSS概念做抽象,反而几乎直接使用decorationStyle()来暴露他们。因此,熟悉CSS会对你开发Wt应用有很大的帮助。

 

第二个widget:释放Wt的能量

直到现在,我们介绍了一个极其特殊的机制开发Web应用程序。我们演示了Wt对程序员来说的增加的价值。下面的一个widget同样列举了新的Wt的widget和特性,但是我们同样也演示了Wt能够高度加强用户体验的一个重要方面。像Google Email和Google Maps这样的应用的一个很重要的特性是它优秀的响应时间。Google在客户端的JavaScript以及Ajax上花费了很大的努力才实现了良好的响应时间。不需要花费你多大的努力,实际上是自动的,你可以使用Wt获得相似的响应时间,并且实际上Wt库将会使用相似的技术来实现这个目标。使用Wt的一个好处是当Ajax或者JavaScript无效的时候,Wt应用程序照样可以正确的运行。我们下面将会讨论到的HangmanWidget类就包含了一些这样的技术。

HangmanWidget包含了一个真实的hangman游戏的逻辑。图4是这个游戏的一个截屏。对于每一个新的游戏,程序随机选择一个秘密的单词给用户猜。从字母表上,用户猜一个字母,如果这个字母是单词的一部分,那么单词中的这个字母就会被揭露。如果是猜错了,你就更进一步的接近一个吊死的人。最后,我们会更新用户在数据库中的得分,给用户提供重新开始新的游戏的机会。HangmanWidget包含很少新奇的东西了,除了我们如何处理hangman的图片的。

在构造函数中,我们构建了一个用户接口。构建图片的部分独立在了方法createImages()中了。

清单6.预获取隐藏的widgets,当调用show()的时候再显示它们

void HangmanWidget::createHangmanImages(WContainerWidget *parent)

{

for(unsigned int I = 0; i<=MaxGuesses;++i)

{

std::string fname = “icons/hangman” + boost::lexical_cast<std::string>(i) + “.png”;

WImage *theImage = new WImage(fname, parent);

HangmanImanges.push_back(theImage);

}

HurrayImage = new WImage(“icons/hangmanhurray.png”, parent);

resetImages(); // Hide all images

}

void Hangmanwidget::resetImages()

{

HurrayImage->hide();

for(unsigned int i = 0; i<HangmanImages.size();++i)

HangmanImages[i]->hide();

}

 

这个函数引入了一个新的widget:WImage,这个widget对应于HTML里面的图片。代码演示了这些widgets是如何被创建以及如何插入HangmanWidget中的。根据到目前为止我们讨论的内容,我们可以假设所有的图片都是在同一时间加载的,当然这种情况不是我们想要的。因此,我们在图片被创建之后调用resetImages(),并且这个方法对每一个图片调用了hide(),调用了之后所有的这些图片都变成了不可见的状态了。游戏的逻辑将会显示和隐藏这些图片,所以只有正确的图片才能在游戏的某个时间点内是可见的。每一个WWidget能够隐藏,隐藏的WWidget能够调用show()来重新显示。但是为什么我们要显示和隐藏图片呢?为什么我们不可以创建我们的图片然后删除图片呢?可选的是,我们可以对一个图片的来源进行修改从而改变这个图片。答案取决于响应时间,至少当Ajax是有效的时候。Wt首先传送的是可视的widgets信息给浏览器。当浏览器的可视部分被渲染了之后,剩下来的隐藏的部分也传送了过来并且已经插入了DOM树里面。大多数浏览器将会预先加载隐藏部分的widgets。所以当用户点击一个字母按钮并且我们需要更新这个hangman图片,我们仅仅是隐藏和显示正确的图片widget.

然后,只有一个小规模的HTTP请求在浏览器和服务器之间通信。一个可选的实现方法可能会导致浏览器获取新的图片,向服务器发出第二次的round-trip的请求,外加下载图片的时间。hangman游戏频繁的使用隐藏widgets的规则,例如当你在高分和游戏之间切换的时候。在任何时候,只有一部分widgets被显示了出来,用户使用底部的菜单来实现切换。通过传送小规模的完整实现给浏览器,使用的是所谓的静态的信号/槽连接,Wt能够实现在一些情况下减少反应时间,但是哪方面的内容本向导不会涉及到。

最后总结一下,隐藏widgets 的使用是一个简单并且高效的实现Wt应用程序的方式。隐藏widgets并不会影响应用程序的加载时间,因为可视的widget是先被传送的。

我们将会跳过HighScoresWidget 和hangmanGame的实现,因为他们演示不了另外的基础的特性。HighScoresWidget显示最高排名的用户,HangmanGame将LoginWidget,HangmanWidget以及HighScoresWidget连接起来。急于寻找信息的用户可以浏览一下HangmanGame的源文件,因为这个程序使用文本(而不是超链接)来实现一个菜单条并且演示了WStackedWidget的使用,WStackedWidget是WContainerWidget的一个衍生。

4.总结

Wt库提供了一种高效的方式实现Web应用程序并且将开发者从和新的Web技术像JavaScript,Ajax和DHTML这样的技术关联的新技术中释放了出来。因为Wt和其他GUI库的相似性,开发者可以把Wt应用当做是另外的一个GUI平台。

这个向导演示了很多重要的Wt 特性,但是和所有的Wt特性比起来,这些只是冰山一角。静态槽,能够更加深入的改善响应时间,文件上传和动态资源,国际化,完全CSS支持和很多没有讨论的widget只是我们不得不跳过的一个部分。想要获得更多的信息,我们推荐读者参考在线的Wt文档资源。

备注:

[1] http://playhangmangames.com/

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值