Submitted by liuguangzhao on Sun, 2008-08-17 17:56
larbin是个高效的网络爬虫,可扩充性强,扩充的地方留置了接口。
这个项目虽然好,可已经很久没有更新了,现在的新编译器都编译不了,改了一些代码。
试运行了一下,抓取速度是快。
larbin还有些功能可能加上,它会变的更强大:
分布式抓取。
https支持。
Submitted by liuguangzhao on Sat, 2008-08-16 17:33
通过ports安装,不复杂,但费时间,硬盘空间也用了不少,总算正常启动起来了。
kde4 很好,很强,启动了kde4桌面,感觉不到在用什么系统,所能感受到的只有KDE4。
从安装kde4,到试用了一会,freebsd 做桌面还有很多问题,现在就没法正常使用flash插件看网上视频。
开发环境现在一般只用一个emacs全搞定,也就是开发环境肯定没问题。
2008-06-17
装上了在线歌曲播放器amarok 2.0 alpha 2 版,armarok是基于kde的在线播放器,上面上许多的歌曲源(现在都是英文的),支持直接搜索,支持自己添加歌曲源,支持播放本地音频、视频。
amarok 主页: http://amarok.kde.org/
2008-06-17
装上了 fcitx-3.5 中文输入法。
Submitted by liuguangzhao on Sat, 2008-08-16 17:13
文件夹同步算法研究
1) 扫描本地同步目录,记录目录下所有文件.
2)扫描远程同步目录,记录目录下所有文件,并发送到本地端.
3) 不分大小写的排序本地/远程同步目录的文件列表。
4) 通过归并比较,得到四组文件:本地仅有的文件,远程仅有的文件,本地新的文件,远程新的文件。
Submitted by liuguangzhao on Tue, 2008-08-12 23:49
OpenGL是一种渲染2D和3D图象的标准API。Qt程序可以使用QtOpenGL模块绘制3D图象,这依赖于系统的OpenGL库。这一节假设你已经熟悉了OpenGL。如果OpenGL对你来说是新的,一个开始学习的好地方是http://www.opengl.org/。
在Qt程序中使用OpenGL绘图是非常直接的:我们必须继承QGLWidget,重新实现几个虚函数,并把程序与QtOpenGL和OpenGL库链接。因为QGLWidget继承自QWidget,我们已经知道的大多数依然可用。主要的不同是我们使用标准的OpenGL函数执行绘图而不是用QPainter。
为了展示这是如何工作的,我们预览一下图8.17中展示的四面体程序的代码。该程序展示了一个3D的四面体,或者叫四边骰子,它的每一面都使用不同的颜色绘制。用户可以通过按下鼠标并拖动来旋转该四面体。用户也可以通过双击它并从弹出的QcolorDialog选择一种颜色设置其中一面的颜色。
图8.17 四面体程序

{
Q_OBJECT
public:
protected:
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
private:
void draw();
int faceAtPosition (const QPoint &pos );
GLfloat rotationX;
GLfloat rotationY;
GLfloat rotationZ;
};
该Tetrahedron类继承自QGLWidget。initializeGL(), resizeGL(), 和 paintGL()函数都是重新实现了QGLWidget中的。鼠标事件处理器像通过一样是重新实现了QWidget中的虚函数。
Tetrahedron ::Tetrahedron(QWidget *parent )
{
setFormat (QGLFormat(QGL ::DoubleBuffer | QGL ::DepthBuffer));
rotationX = -21.0;
rotationY = -57.0;
rotationZ = 0.0;
faceColors[0] = Qt::red;
faceColors[1] = Qt::green;
faceColors[2] = Qt::blue;
faceColors[3] = Qt::yellow;
}
在构造函数中,我们调用QGLWidget::setFormat()来指定OpenGL显示上下文,并初始化该类的私有变量。
void Tetrahedron::initializeGL()
{
qglClearColor(Qt::black);
glShadeModel(GL_FLAT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
initializeGL()函数仅在paintGL()调用前调用了一次。这是我们可以设置渲染上下文,定义显示列表,和执行其他初始化的地方。
所有代码都是标准的OpenGL,除了QGLWidget的qglClearColor()函数之外。如果我们希望坚持使用OpenGL,我们可以在RGBA模式下调用glClearColor(),在颜色索引模式调用glClearIndex()。
void Tetrahedron::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat x = GLfloat(width) / height;
glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0);
glMatrixMode(GL_MODELVIEW);
}
resizeGL()函数在paintGL()首次调用前,在initializeGL()被调用之后被调用。它还在物件在任何调整尺寸的时候被调用。这是我们可以设置OpenGL视口、投影以及任何依赖于物件尺寸的其他设置的地方。
void Tetrahedron::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw();
}
paintGL()函数在任何物件需要重绘的时候调用。这与QWidget::paintEvent()很相似,但与QPainter函数相反,我们使用OpenGL函数。实现的绘图是在私有函数draw()中执行的。
void Tetrahedron::draw()
{
static const GLfloat P1[3] = { 0.0, -1.0, +2.0 };
static const GLfloat P2[3] = { +1.73205081, -1.0, -1.0 };
static const GLfloat P3[3] = { -1.73205081, -1.0, -1.0 };
static const GLfloat P4[3] = { 0.0, +2.0, 0.0 };
static const GLfloat * const coords[4][3] = {
{ P1, P2, P3 }, { P1, P3, P4 }, { P1, P4, P2 }, { P2, P4, P3 }
};
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -10.0);
glRotatef(rotationX, 1.0, 0.0, 0.0);
glRotatef(rotationY, 0.0, 1.0, 0.0);
glRotatef(rotationZ, 0.0, 0.0, 1.0);
for (int i = 0; i < 4; ++i) {
glLoadName(i);
glBegin(GL_TRIANGLES);
qglColor(faceColors[i]);
for (int j = 0; j < 3; ++j) {
glVertex3f(coords[i][j][0], coords[i][j][1],
coords[i][j][2]);
}
glEnd();
}
}
在draw()中,我们绘制该四面体,利用x, y, 和 z旋转和存储在faceColors数组中的颜色。所有都是标准OpenGL,除了qglColor()调用之外。我们本应该使用glColor3d() 或者 glIndex()中的一个,具体用哪个依赖于当前的模式。
{
lastPos = event->pos();
}
{
GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();
if (event->buttons() & Qt::LeftButton) {
rotationX += 180 * dy;
rotationY += 180 * dx;
updateGL();
} else if (event->buttons() & Qt::RightButton) {
rotationX += 180 * dy;
rotationZ += 180 * dx;
updateGL();
}
lastPos = event->pos();
}
mousePressEvent() 和 mouseMoveEvent()函数也是重新实现自QWidget以允许用户通过点击和拖动旋转该视图。鼠标左键允许用户沿x和y轴旋转,鼠标右键允许用户沿x和z轴旋转。
在修改了rotationX、rotationY 或者rotationZ变量后,我们调用updateGL()重绘该场景。
void Tetrahedron ::mouseDoubleClickEvent(QMouseEvent *event )
{
int face = faceAtPosition(event->pos());
if (face != -1) {
if (color.isValid()) {
faceColors[face] = color;
updateGL();
}
}
}
mouseDoubleClickEvent()重新实现自QWidget以允许用户通过双击它设置四面体各面的颜色。我们调用私有函数faceAtPosition()来决定哪一面,如果有的话,位于鼠标的下面。如果一个面被双击,我们调用QColorDialog::getColor()为该面获取一个新的颜色。然后我们用新的颜色值更新faceColors数组,再调用updateGL()来重绘该场景。
int Tetrahedron ::faceAtPosition(const QPoint &pos )
{
const int MaxSize = 512;
GLuint buffer[MaxSize];
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glSelectBuffer(MaxSize, buffer);
glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPickMatrix(GLdouble(pos.x()), GLdouble(viewport[3] - pos.y()),
5.0, 5.0, viewport);
GLfloat x = GLfloat(width()) / height();
glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0);
draw();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
if (!glRenderMode(GL_RENDER))
return -1;
return buffer[3];
}
faceAtPosition()函数物件上特定点所在面的序号,或者如果该点没有面则返回-1。决定这个值的代码在OpenGL中有点小复杂。本质上来说,我们所做的只是在GL_SELECT模式下渲染该场景以充分利用OpenGL的挑选功能,然后就可从OpenGL的命中记录中获取面的序号(它的“名字”)。
下面是main.cpp:
#include <QApplication>
#include <iostream>
#include "tetrahedron.h"
using namespace std;
int main(int argc, char *argv[])
{
cerr << "This system has no OpenGL support" << endl;
return 1;
}
Tetrahedron tetrahedron;
tetrahedron. setWindowTitle(QObject::tr("Tetrahedron"));
tetrahedron.resize(300, 300);
tetrahedron.show();
return app.exec();
}
如果用户的系统不支持OpenGL,我们向控制台打印一个错误消息并立即返回。
要把该程序与QtOpenGL模块和系统的OpenGL库链接,.pro文件中需要下面的一条:
QT += opengl
该四面体程序就完成了。为了更多关于QtOpenGL模块的信息,请看手册中QGLWidget, QGLFormat, QGLContext, QGLColormap, 和 QGLPixelBuffer的文档。
Submitted by liuguangzhao on Tue, 2008-08-12 23:40
在Qt中打印与在QWidget, QPixmap, 或者 QImage上绘图相似。它包含下面几步:
1.创建一个QPainter当作一个绘图设置。
2.弹出一个QprintDialog,让用户选择一个打印机并设置几个参数。
3.创建一个在Qprinter上执行操作的QPainter。
4.使用该QPainter绘制一页。
5.调用QPrinter::newPage()前进到下一页。
6.重复第4和5步直到所有页都绘制完毕。
在Windows和Mac OS X 上,Qprinter使用所在系统的打印机驱动程序。在Unix上,它生成PostScript并把它发送到lp或者lpr(或者到QPrinter::setPrintProgram()所设置的程序)。Qprinter也可通过调用setOutputFormat(QPrinter::PdfFormat)用于生成PDF文件。
图8.12 打印一个QImage

让我们以一个仅在打印单页的简单例子开始。第一个例子打印一个QImage:
void PrintWindow ::printImage(const QImage &image )
{
if (printDialog.exec()) {
QRect rect = painter. viewport();
QSize size = image. size();
size.scale(rect.size(), Qt::KeepAspectRatio);
painter.setViewport(rect.x(), rect.y(),
size.width(), size.height());
painter.setWindow(image.rect());
painter.drawImage(0, 0, image);
}
}
我们假设PrintWindow类有一个Qprinter类型的叫printer的成员变量。我们应该在printImage()函数中从栈上创建该Qprinter,但它可能在执行到另一个打印时无法记住用户的对上一次打印设置。
我们创建了一个QprintDialog并调用了exec()来显示它。如果用户点击了它的OK按钮它返回true,否则返回false。在调用exec()之后,该Qprinter对象就准备好使用了。(也可以不用QprintDialog就能打印,通过直接调用Qprinter的成员函数设置参数)。
下一步,我们创建一个在Qprinter上绘图的QPainter。我们把窗口设为该图象的矩形并以相同的比率把视口设置为一个矩形,我们在(0,0)位置绘制该图象。
默认情况下,QPainter窗口是初始化过的,因此打印机看上去与屏幕的处理方法相似(一般为72和100点每英寸),这使得打印工作可以容易的重用物件绘图代码。这里并没有什么关系,因为我们设置了我们自己的容器。
打印只有一页非常容易,但是许多程序都需要打印多页。为此,我们需要一次绘制一页,并调用newPage()进行到下一页。这产生了我们要决定每页打印多少信息的问题。在Qt中有两种主要方法能处理多页文档:
我们可以把数据转换为HTML并使用Qtextdocument渲染它,这是Qt的丰富文件引擎。
我们可以手工绘图和分页。
我们将依次预览这两种方法。作为一个例子,我们将打印一本花草手册:一系列花的名字,每个都有一个文本描述。手册中的每个入口都以"name:description"格式存储为一个字符串,例如:
Miltonopsis santanae: A most dangerous orchid species.
由于每种花的数据被表示为一个单独的字符串,我们可以使用一个QStringList表示手册中的所有花。下面是使用Qt的丰富文本引擎打印花草手册的函数:
void PrintWindow ::printFlowerGuide(const QStringList &entries )
{
QString title = Qt ::escape(fields [0]);
QString body = Qt ::escape(fields [1]);
html += "<table width=\"100%\" border=1 cellspacing=0>\n"
"<tr><td bgcolor=\"lightgray\"><font size=\"+1\">"
"<b><i>" + title + "</i></b></font>\n<tr><td>" + body
+ "\n</table>\n<br>\n";
}
printHtml(html);
}
第一步是把该QStringList转换为HTML。每种花变成一个有两个单元格的表格。我们使用Qt::escape()把特殊字符'&', '<', '>'替换为相应的HTML实体("&", "<", ">")。然后再调用printHtml()打印这些文本:
void PrintWindow ::printHtml(const QString &html )
{
if (printDialog.exec()) {
textDocument.setHtml(html);
textDocument.print(&printer);
}
}
printHtml()函数弹出一个QprintDialog并小心地打印一个HTML文档。它可被重用于任何Qt程序以打印任意HTML页。
图8.13 使用QTextdocument打印花草手册

把文档转换为HTML并用Qtextdocument来打印它是到现在为止打印报表和其他复杂文档的常用替代选择。在我们需要更多控制的时候,我们可以做出该页的布局并手工绘图。现在让我们看一下如何使用这种方法打印一本花草手册。下面是新的printFlowerGuide()函数:
void PrintWindow ::printFlowerGuide(const QStringList &entries )
{
if (printDialog.exec()) {
QList<QStringList> pages;
paginate(&painter, &pages, entries);
printPages(&painter, pages);
}
}
在设置完打印机并构造了绘图器后,我们就调用paginate()帮助函数来决定哪一条应该出现在哪一页中。这产生的结果是一系列的QStringList,每个QStringList持有每页的条目。我们把此结果传递给printPages()。
例如,假设花草手册包含6条,我们把它叫做A, B, C, D, E, and F。现在假设在第一页有放置A和B的空间,C、D和E放在第二页,F在第三页。Pages列表现在有索引位置为0的列表[A, B],索引位置为1的列表[C, D, E],索引位置为2的列表[F ]。
void PrintWindow ::paginate(QPainter *painter, QList<QStringList> *pages,
{
int pageHeight = painter->window().height() - 2 * LargeGap;
int y = 0;
int height = entryHeight(painter, entry);
if (y + height > pageHeight && !currentPage.empty()) {
pages->append(currentPage);
currentPage.clear();
y = 0;
}
currentPage.append(entry);
y += height + MediumGap;
}
if (!currentPage.empty())
pages->append(currentPage);
}
我们遍历这些条目并把它们追加到当前页,直到我们遇到一个放不下的条目,然后我们把当前页追加到pages列表并开始一个新页。
{
int textWidth = painter->window().width() - 2 * SmallGap;
int maxHeight = painter->window().height();
painter->setFont(titleFont);
QRect titleRect = painter ->boundingRect (0, 0, textWidth, maxHeight,
Qt::TextWordWrap, title);
painter->setFont(bodyFont);
QRect bodyRect = painter ->boundingRect (0, 0, textWidth, maxHeight,
Qt::TextWordWrap, body);
return titleRect.height() + bodyRect.height() + 4 * SmallGap;
}
enTRyHeight()函数使用QPainter::boundingRect()来计算每个条目所需的垂直空间。图8.14展示一个条目的布局以及SmallGap 和 MediumGap常量的意义。
图8.14 一条花目的布局

void PrintWindow ::printPages(QPainter *painter,
const QList<QStringList> &pages)
{
int firstPage = printer.fromPage() - 1;
if (firstPage >= pages.size())
return;
if (firstPage == -1)
firstPage = 0;
int lastPage = printer.toPage() - 1;
if (lastPage == -1 || lastPage >= pages.size())
lastPage = pages.size() - 1;
int numPages = lastPage - firstPage + 1;
for (int i = 0; i < printer.numCopies(); ++i) {
for (int j = 0; j < numPages; ++j) {
if (i != 0 || j != 0)
printer.newPage();
int index;
if (printer. pageOrder() == QPrinter::FirstPageFirst) {
index = firstPage + j;
} else {
index = lastPage - j;
}
printPage(painter, pages[index], index + 1);
}
}
}
printPages()函数的角色是以正确的顺序和正确的次数调用printPage()打印每一页。使用QPrintDialog,用户可能需要几个拷贝,指定一个打印区间,或者以反方向请求页面。使用QPrintDialog::setEnabledOptions()设置这些参数并禁用它们是我们的责任。
我们以决定打印区间为开始。QPrinter's fromPage() 和 toPage()函数返回用户选择的页序号,或者如果没有选择任何区域则为0。我们要减去1,因为pages列表是从0开始索引的,如果用户没有设置任何区间则把firstPage 和 lastPage设为整个区间。
然后我们就打印每一页。外部的for循环迭代任意多次以生成用户请求的拷贝数量。大多数打印机驱动程序支持多份拷贝,因此对些来说QPrinter::numCopies()总是返回1。如果打印机不能处理多份文档,QPrinter::numCopies()返回用户请求的拷贝数,并且程序是负责打印该数目的拷贝。(在本节前面的QImage例子中,为了保持简单性的原因我们忽略了QPrinter::numCopies())。
图8.15 使用QPainter打印花草手册

内部的for循环遍历所有的页面。如果该页面不是第一页,我们调用newPage()以刷新前一页并开始在一个新的页面上绘图。我们调用printPage()绘制每一页。
void PrintWindow ::printPage(QPainter *painter,
{
painter->save();
painter->translate(0, LargeGap);
printBox(painter, title, titleFont, Qt::lightGray);
printBox(painter, body, bodyFont, Qt::white);
painter->translate(0, MediumGap);
}
painter->restore();
painter->setFont(footerFont);
painter->drawText(painter->window(),
Qt::AlignHCenter | Qt::AlignBottom,
}
printPage()函数遍历所有手册中的条目并使用两个printBox()打印它们:一个用于标题(花的名字),另一个用于页面正文(它的描述)。它还绘制在页面底部中央的页码。
图8.16 手册页面布局

{
painter->setFont(font);
int boxWidth = | | |