Qt Widget In-Depth(深入了解QWidget)

盼望着,盼望着,春天来了。也把这个讲述QWidget内部实现原理的文档给盼到了

by 臭虫 Tags: , ,
Posted on March 9th, 2010 in Qt技术 | 2 Comments »

Box2D:给你的Qt程序加上物理引擎

看CuteQt博客的,估计大部分人也在看Qt Labs的博客,我也订阅了Qt Labs的博客,不过大部分时间都是过个标题,没有细看。今天有时间再去扫一遍就帖子,发现有个好东西Qt+Box2D被我错过了。

Box2D是一个开源物理引擎,它提供了碰撞,摩擦,加速度(含重力加速度)等物理特效。如果你对物理引擎还没什么概念的话,试玩一下下面这个游戏或者15个最佳Flash物理游戏,你就会明白了。

Box2D做为一个物理引擎,它本身并不提供可见的图形界面,但是提供了计算在物理世界中刚体(不会变形的物体)运动(旋转/移动)轨迹数据的能力。所以任何一个带图形界面的程序开发框架都可以使用Box2D提供的数据,将物理特效以直观的方式展现出来。目前javascript/flash/java/Python等都提供了对Box2D的支持。当然Qt也能将Box2D加入进来和Graphics View框架结合,而且因为都是C++代码,这种集成显得非常方便。下面是我在Linux上的一个测试过程.

1. 编译Box2D

step1. 项目地址在http://code.google.com/p/box2d,代码下载请点击Box2D_v2.0.1.zip Read the rest of this entry »

by 臭虫 Tags: ,
Posted on March 8th, 2010 in Linux技术, 新闻类 | 5 Comments »

做个可以动态扩展数据的Model

一般的Model都是针对固定的数据源, 这里所说的动态扩展数据的Model常用在数据源巨大的情况下, 比如大型数据库每个表可能有数万甚至百万级的数据, 如果是用基本的Model一次性把数据都取出来显示那将是一个恐怖的过程, 搞不好你的系统就玩完了。 记得笔者以前开发字典软件的时候也用过类似的技术, 只是用的QListWidget配合滚动条的事件来实现动态取数据, 这里我们要把这个方法用在Model上了。

Qt目前Model的架构里已经有了相关的设计, 用到的函数有:
bool QAbstractItemModel::canFetchMore ( const QModelIndex & parent ) const [virtual]
void QAbstractItemModel::fetchMore ( const QModelIndex & parent ) [virtual]
int QAbstractItemModel::rowCount ( const QModelIndex & parent = QModelIndex() ) const [pure virtual]

这几个都是虚函数, 也就是我们需要挑选一个Model的类作为基类进行派生, 在派生类里重写这些函数以替代默认的行为。
其中canFetchMore接受一个parent index作为输入参数, 需要返回在该parent下是否还有children数据未被取得, 如果有就返回true, 否则返回false;
fetchMore函数则负责具体的获取数据动作, 至于每次取得多少新数据文档里并没有任何建议, 笔者的建议是按照你的view一页所能容纳的数据量来定义, 因为该函数往往在用户点击滚动条翻页的时候被调用, 每次取得一页数据比较经济;
rowCount函数应该返回目前在view中能够显示的数据(注意, 这个数并不是所有的数据量, 而是已经被取出的数据量。)。

Qt提供的view类基本上能和这种动态扩展的Model互相配合, 以QTreeView为例, 它根据rowCount的值调用data函数获得需要显示的数据, 仅当用户滚动scrollbar到底端或者用户打开某个树节点查看其children时才fetchMore。 如果我们自己写view类需要注意, 别忘了用上这几个方法让自己的类能很好的融合其他的Qt类。

关于fetchMore的实现, 这里还需要罗嗦几句。 前面我们的blog里介绍了自定义Model的基本写法(参见: 大家一起来写Model), 其中有一点是在增删model数据时需要用beginInsertRows/endInsertRows把增删的语句括起来, 这一点对fetchMore同样适用。 大家写代码的时候要特别注意。

附件里给出了一个笔者写的动态扩展数据的Model的例子, 希望对大家有所帮助!

Example: fetchmore.tar.gz

by shiroki Tags: , ,
Posted on March 3rd, 2010 in C++, Qt技术 | 1 Comment »

Nokia:Symbian Qt应用智能发布工具Beta版

给Symbian写过Qt程序的人都知道,如果希望你的Qt程序能运行在Nokia Symbian手机上,有两种方法。要么首先给手机装上一个Qt的库,然后安装自己写的Qt程序;要么将Qt库和自己写的Qt应用做成一个打包程序。

然后这两种方法都对于个人做开发研究可以,但是要发布到网络上共享给其它人就很不方便,第一种方法,你没法保证下载你Qt应用的终端用户会自己安装Qt库,他(她)甚至完全不知道Qt是什么;第二种方法则使应用显得庞大,让人望而却步。我想这也是网上很少见到symbian Qt应用的一个原因。Nokia新推出的Qt应用智能发布工具(Nokia Smart Installer for Symbian)可以使这种局面得到改观。

Smart Installer主要原理就是,用一个应用安装管理器(ADM)自动检测Qt应用的依赖,会帮你自动从Nokia服务器下载合适的Qt库版本,保证应用能运行。而开发者不用担心找不到Qt库的问题。

看看这个安装工具是怎么工作的

开发者:
* 创建普通的Symbian Qt工程,调试编译
* 将Qt工程打包成.sis格式
* 将.sis安装包和 smart installer进行绑定
* 可以发布你的Qt应用了(大小约等于应用大小加10k左右)

终端用户:
* 下载你发布的Qt应用,安装
* smart installer检查Qt应用的依赖
* smart installer下载Qt应用缺少的依赖文件
* 安装完毕

智能发布工具的下载地址在此Binary download,对了,这个功能是随着Qt-4.6.2一起发布的,你想试用,请去下载Qt新的版本。

Nokia Smart Installer for Symbian

by 臭虫 Tags: , , , ,
Posted on February 21st, 2010 in s60 | No Comments »

Qt 图形特效(Graphics Effect)介绍

QGraphicsEffect也是Qt-4.6引入的一个新功能。它让给图形元素QGraphicsItem增加更佳视觉效果的编程变得非常简单。

先来看几张效果图。

上图中最上面的那个图片是没有使用QGraphicsEffect处理的原图,下面的四个图片分别代表了模糊,变色,透明和阴影效果。对应使用了QGraphicsEffect的4个子类QGraphicsBlurEffect, QGraphicsColorizeEffect, QGraphicsDropShadowEffect, 和 QGraphicsOpacityEffect.下面分别介绍它们。 Read the rest of this entry »

by 臭虫 Tags: ,
Posted on February 6th, 2010 in C++, Qt技术 | 1 Comment »

一个非常有趣的QTcpServer多线程编程问题

Qt 4.6自带的threaddedfortuneserver是个简单明了的 Qt C/S网络编程server端程序的例子, 该例子演示了 QTcpServer与QThread配合的方法。 代码不多, 但包含了Qt网络编程的几个关键点。

- FortuneServer类从QTcpServer派生, 调用QTcpServer::listen() 监听端口等待client连接
- FortuneServer重写了虚函数 incomingConnection()去接受client连接, 并创建线程处理该连接
- FortuneThread是处理client连接的子线程,  在该线程里向client端写入数据

结构非常简单。  笔者本来想照着这个架构写个接收client数据的小server, 在写的过程中发现了一个很有意思的问题, 且听我慢慢道来。

不知道大家有没有发现, 其实FortuneServer这个类看起来是QTcpServer类的简单包装, 并没有加入新的东西, 笔者就尝试去掉此子类直接使用QTcpServer。设想的程序架构是这样的:

- 使用QTcpServer监听端口等待client连接
- 在收到QTcpServer::newConnection信号时调用 nextPendingConnection获得socket 连接, 将socket 连接的fd传送给子线程
- FortuneThread是处理client连接的子线程,  得到连接的fd后创建一个QTcpSocket并用QTcpSocket::setSocketDescriptor, 这样就可以用QTcpSocket的方法来监控fd的动向了。
这里我们用QTcpSocket::waitForReadyRead等待client端发来的数据

为了得到与client的连接的socket fd, 调用了 QTcpServer::nextPendingConnection()方法获得一个QTcpSocket指针, 从该指针得到连接的fd, 再将该fd传送给子线程去处理。 看上去与原来的程序没什么区别, 但运行起来却发生了奇怪的问题, 那就是有时server的waitForReadyRead返回true时却读不到数据(bytesAvailable() = 0)似乎client发来的数据丢了一样。 真是让人百思不得其解。

说到这里, 不知道有没有同学意识到究竟哪里出了问题。 笔者研究再研究始终没弄明白, 只能隐约觉得和这个incomingConnection/nextPendingConnection 有关。 后来找了个高人帮忙才搞清楚, 原来问题确实出了nextPendingConnection上。

仔细回想一下我们的程序的架构, 在server进程里调用nextPendingConnection获得一个QTcpSocket的指针, 将此指针内的fd信息发送给子进程由子进程负责与client通讯。 大家再想想QTcpSocket提供了那么多的API包括signal等, 这意味着什么? 肯定Qt在底层对fd进行了监控啊, 也就是说在我们的程序里出现了两个QTcpSocket分别在两个线程里对同一个fd进行了监控和操作, 所以出现一些奇怪的现象也就不算奇怪了。 如果大家尝试对主线程的QTcpSocket进行处理就会发现, 所谓“丢失”的数据都可以在这个socket里得到, 即有一部分socket的数据由于线程切换的关系由主线程的socket截获了。

为了解决这个问题当然最好的办法还是沿用例子中的架构, 对QTcpServer进行派生, 因为在incomingConnection的参数里可以直接得到fd, 此时还没有创建QTcpSocket对此fd做任何操作, 是个干净的状态, 不会有任何冲突;

另外还有一个办法是在不改变现有程序架构的情况下把这两个QTcpSocket搬到同一个线程里。  这样也不会出现两个线程同时访问一个fd的情况。 具体是使用 QObject::moveToThread方法。 需要注意的是文档中对moveToThread有个说明, 有parent的object是不能被移动到其他线程中的, 所以还需要把QTcpSocket给setParent(NULL)一下再moveToThread.

经过实验, 第二种方法也可以很好的工作。

例子见附件 threadedserver.tgz

by shiroki Tags: , , ,
Posted on February 4th, 2010 in C++, Qt技术 | No Comments »

Qt利用JavaScript访问网页元素(百度博客下载例子)

昨天的文章Qt利用DOM API访问网页元素介绍了怎样使用QtWebKit中的DOM支持来实现读取百度博客中的博客标题和上下文链接。要实现网页处理的功能,QtWebKit还提供了更灵活的方法,就是利用JavaScript来处理网页,使用JavaScript能更方便的扩展你的应用,首先它不需要重新编译程序,在网页内容变化的时候,修改JavaScript文件就可以,其次也很容易实现插件话,比如我读取百度博客可以使用baidu.js来实现,要支持新浪的博客,只需另外增加一个sohu.js文件就可以。从代码使用上JavaScript也有一定优势,它能直接使用HTML里的JavaScript定义的变量,如果要用Qt代码几乎是不可能的。

要在QtWebKit中使用JavaScript,核心的一个函数就是evaluateJavaScript(QString),参数是要执行的JavaScript语句。有两个类定义了该函数分别是QWebElement和QWebFrame,也就是说可以对HTML文档中的一个节点或者整个文档调用evaluateJavaScript()。evaluateJavaScript()可以执行更改文档节点中的内容,比如修改背景色,图表数据等。evaluateJavaScript()还能返回数据,它执行的最后一条JavaScript语句的结果输出传给Qt代码,可以是一个字符串,也可以是一个XML对象,注意它的返回类型是QVariant。

我们还来看一个我要加载的baidu.js文件,里面用简单的JavaScript DOM读取博客的主体,标题和上下文链接。

function getBlog(){
    var blog_text=document.getElementById(“blog_text”);
    return blog_text.innerHTML;
}
function getTitle(){
    var blog=document.getElementById(“m_blog”);
    var list=blog.getElementsByClassName(“tit”);
    return list[0].innerHTML;
}
function older_href(){
    return “http://hi.baidu.com”+pre[3];
}
function newer_href(){
    return “http://hi.baidu.com”+post[3];
}

为了使用这里的JavaScript函数,可以使用如下Qt代码加载

    QFile file;
    file.setFileName(“baidu.js”);
    file.open(QIODevice::ReadOnly);
    QString jscript = file.readAll();
    file.close();
    mainframe->evaluateJavaScript(jscript);

在真正要读取博客中的内容的时候,去调用对应的JavaScript函数就可以,代码如下。下面的代码,我们使用了evaluateJavaScript()的返回值。

    QString js_str,js_return;

    js_str=QString(“getTitle();”);
    js_return=mainframe->evaluateJavaScript(js_str).toString();
    title->setText(js_return);

    js_str=QString(“older_href();”);
    js_return=mainframe->evaluateJavaScript(js_str).toString();
    prev->setText(js_return);

    js_str=QString(“newer_href();”);
    js_return=mainframe->evaluateJavaScript(js_str).toString();
    next->setText(js_return);

从上面的结构来看,我们很容易把下载百度博客的程序扩展到其他空间的博客上。只要实现一个相应的.js文件就可以。可编译执行的例子源代码下载地址在这里,没错就是昨天的代码,你需要在两种代码之间用指定的宏切换一下而已。

by 臭虫 Tags: , , ,
Posted on February 3rd, 2010 in Qt技术 | 2 Comments »

Qt利用DOM API访问网页元素(百度博客下载例子)

Qt-4.6.0新增的一个功能就是QtWebKit提供了利用DOM访问管理网页的接口。

所谓DOM(文件对象模型),就是把一个HTML网页内容以一个带层次结构的对象来处理,比如网页中的标题,段落,图表等都是这个层次对象中的一个节点。这些节点可大可小,顶级节点就是整个文档,最小的节点可以是网页中的一个链接,或者一个图片。利用DOM就能很方便的提取和处理网页中用户所感兴趣的内容。下面用一个对百度博客的处理来简单说明。

比如:http://hi.baidu.com/yobin/blog/item/9036520f8c85a1216159f366.html,这个网页里有博客的标题,博客的具体内容,以及前一篇和后一篇的链接。我们通过网页的源代码可以看到有

<div id=”m_blog” style=”overflow-x:hidden;”>
<div>这个城市(2)</div>
—-无关内容—

</div>

这里的<div>开始和</div>结束就是一个节点,id为”m_blog“在百度博客里指明是和博客内容相关的部分。对应在Qt里对应的对象类是QWebElement,如果我们要读取这里的标题可以用下面代码实现。

QWebElement doc = mainframe->documentElement();
QWebElement m_blog = doc.findFirst(“#m_blog”);
QWebElement e_title = m_blog.findFirst(“.tit”);
title->setText(e_title.toPlainText());

这里mainframe是QWebFrame类型的对象,mainframe->documentElement()获得了整个文章的内容,是顶级节点。通过doc.findFirst(“#m_blog”)可以找到整个文章中m_blog第一次出现的地方。然后再在这个节点里寻找class为tit的子节点,toPlainText()能够获得节点中的内容。注意findFirst里的参数语法#号开头的查找id名的,点号”.”开头的则是查找class名字的,如果没有前面符号的则是查找标准HTML标记比如findFirst(“body”)。当然这些语法可以组合起来使用,符合CSS选择器的标准语法,请参考Standard CSS2 selector syntax。查找QWebElement子元素的方法还有 firstChild(),nextSibling (),parent (),previousSibling()等,具体内容可以查看QWebElement的类帮助。

下面再来看一下前后篇文章链接的HTML代码是如何的.

<div id=”in_nav”>
上一篇:<a title=”新股中签的回报率” href=”/yobin/blog/item/86ad9223e5add74fad34de92.html”>新股中签的回报率</a>
下一篇:<a title=”人民时评:且看美国的信息自由” href=”/yobin/blog/item/dc3491587b51cbd59c8204ba.html”>人民时评:且看美国的信息自由</a></div>

HTML中除了标签内容之外,还有关于这个标签的属性,可以用attribute()来得到,下面是Qt读取链接的代码

QWebElement e_nav=m_blog.findFirst(“#in_nav”);
QWebElement prev_nav=e_nav.findFirst(“a”);
prev->setText(“http://hi.baidu.com”+prev_nav.attribute(“href”));
QWebElement next_nav=prev_nav.nextSibling();
next->setText(“http://hi.baidu.com”+next_nav.attribute(“href”));

有了前后文链接,要下载博客里所有文章就不是难事了。下面是我的例子代码和截图

by 臭虫 Tags: , ,
Posted on February 1st, 2010 in Qt技术 | 1 Comment »

This class is not part of the Qt GUI Framework Edition.

最近在看WebKit的几个类文档时时,经常看到这么一句话。
This class is not part of the Qt GUI Framework Edition.

还是黑体的,比如:http://qt.nokia.com/doc/4.6/qwebelement.html正文里第三句。一下子没转过弯,到底是说这个类不属于GUI呢,还是不属于Qt。如果是如果理解成后者的话,意味着只是一个临时类,未来会被替换掉,那我要考虑是不是使用它写程序。

最后查了一下这句话是在Qt-4.6文档中引入的。之所以这样写是因为Qt商业版将Qt分为两种Framework,即Qt GUI Framework Edition和Qt Full Framework Edition。Qt GUI Framework 是Qt Full Framework的一个子集。很多模块比如QtWebKit,QtXml,QtScript都不属于Qt GUI Framework,但是属于Qt Full Framework Edition.详细划分请参考

http://qt.nokia.com/doc/4.6/commercialeditions.html

http://qt.nokia.com/doc/4.6/gui-framework-edition-classes.html

http://qt.nokia.com/doc/4.6/full-framework-edition-classes.html

据我分析这种划分可能是出于技术支持上的考虑,一些模块比如WebKit,自身的代码量就很大,要提供支持也许费用会不一样。因为在开源版本,并没有发现Gui Framework和Full Framework的区分。从代码上来看也没有区别。

所以看到”This class is not part of the Qt GUI Framework Edition.”不用管它,它还是Qt类库的一部分,直接使用就是了。

by 臭虫 Tags: ,
Posted on January 31st, 2010 in Qt技术 | No Comments »

在View中显示控件的tip

看到几次有人问这个问题了, 写在blog里Memo一下, 以免以后忘记。

先看一张截图:

这里是往 QTableView的第一个格子里填了个黄色的按钮。 具体的做法其实很简单, 用的Qt提供的现成的API, 只是这个API躲在文档里一个隐藏颇深的地方, 一般人不容易发现它, 而且Qt的例子里也没有用到这个函数。 但~ 这可是个相当重要且有用的函数哦, 大家一定要记住!

代码如下:

view->setIndexWidget(model->index(0,0), btn);

该函数是 QAbstractItemView的函数, 所以适用于其他的View类, 不仅限于QTableView.

设置的这个Widget有别于 Delegate提供的editor widget, editor widget是在用户双击cell的时候出现, 而这个index widget是一直显示的。 大家要根据实际的需要选择。

by shiroki Tags:
Posted on January 28th, 2010 in Qt技术 | 3 Comments »