Qtopia 入门指南

作者:Lorn Potter

Trolltech 公司的 Qtopia 是一个嵌入式应用程序框架,旨在为供应商提供最大的自定义能力,并让第三方开发者可以选择开发商业应用程序或免费的 GPL 应用程序。Qtopia 以双重许可方式提供。为 Qtopia 开发应用程序与任何 Qt/KDE 桌面开发一样简单,只是需要一些额外的步骤和工具。

我开始开发我的程序 Gutenbrowser,它是一个阅读器/下载器,用于从 Gutenberg 项目获取数千本免费电子文本。它最初是一个仅限 Linux 的应用程序,很快就扩展到了 Windows、Qtopia,然后是开放掌上集成环境 (Opie)。Opie 基于 Qtopia GPL,由社区开发。

Qtopia 构建于 Qt Embedded 之上,因此使用 Qt 创建的应用程序可以轻松地在 Qtopia 环境中运行。我花了大约两周的业余时间/开源开发者时间,让 Gutenbrowser 在 Sharp Zaurus 上运行起来,这还包括学习 Qtopia API 和跨平台编译!

如果您想将 Qt 4 应用程序移植到 Qtopia,最好等待 Qtopia 4 发布,因为 Qt Embedded 2.3 和 Qt Embedded 4 之间存在重大变化。Qt 3、KDE 甚至 Gtk+ 应用程序都已移植到 Qtopia 1 和 2 版本,但需要向后移植、类替换以及为 KDE 程序使用 microkde 源代码。

您需要的工具

要开始为 Qtopia 开发,您需要一些工具。Qtopia 目前仅适用于 Linux,因此您需要一个 Linux 桌面来进行开发。您显然还需要一个编辑器,例如 emacs 或 vi。对于这个项目,我选择了 KDevelop,因为它带有一个简单的 Qtopia 应用程序模板。

如果您正在为设备开发,则需要一个交叉编译器。我们的目标设备 Archos PMA430 使用 arm-linux-gcc 版本 2.95 用于 Qtopia。虽然 gcc 3 可以生成更好优化的代码,但我们希望在硬件上运行并与当前存在的软件兼容,因此 2.95,尽管它很旧,但也可以满足需求。您可以从 Internet 上的各种站点获取 ARM 交叉工具链。在本例中,Archos 在 www.archos.com/products/overview/pma_400_sdk.html 上提供了一个工具链,也可以从 qtopia.net 获取链接。

源代码还是 SDK?

当然,您还需要 Qtopia,但您可以选择下载源代码或使用现成的 Qtopia SDK。PMA430 的 SDK 提供商业版和 GPL 版,就像 Qtopia 本身一样。商业 SDK 可以从 www.trolltech.com/products/qtopia/pricing.html 以合理的价格购买,免费的 GPL 版本可以从 ftp.trolltech.com/qtopia/sdk 下载。这些安装到 /opt/Qtopia。然后从命令提示符执行

# ln -s /opt/Qtopia/sharp /opt/Qtopia/arm

如果不存在 /opt/Qtopia/arm 目录。

KDevelop 项目

启动 KDevelop,然后从“项目”菜单中选择“新建项目”。打开 C++ 目录图标,在“嵌入式”目录下。单击名为“Qtopia 应用程序”的文件以启动新的 Qtopia 项目。我可以将其命名为任何名称,例如 hippopotamus,但我将我的项目命名为 skizzy。有关创建此项目的示例对话框,请参见图 1。

Getting Started with Qtopia

图 1. 创建 skizzy 项目

一旦您拥有一个项目,您就可以开始编辑它以满足您的需求。您需要确保在为 Qtopia 编辑 .ui(用户界面)文件时使用 Qt 2 的 Designer,因为从更高版本的 Qt 生成的 .ui 文件不兼容。我通过设置自定义外部工具,然后从 Designer 2 中打开我的 .ui 文件来完成此操作。

注意:不要通过单击 .ui 文件打开,因为 Designer 3 将在 KDevelop 中打开,您可能会损坏您的 .ui 文件。因此,您必须从命令行运行 KDevelop,在导出一些变量之后

export PATH=/opt/Qtopia/bin:$PATH
export LD_LIBRARY_PATH=/opt/Qtopia/lib:$LD_LIBRARY_PATH

您还需要设置 Qt 虚拟帧缓冲区工具,名称为 QVFb,指向 /opt/Qtopia/bin/qvfb,应用程序将在桌面上运行。Qtopia 直接显示到帧缓冲区,因此它不需要 X-11 显示服务器的开销。

设置开发环境

我们需要为我们的 KDevelop 项目设置一些环境变量。运行 KDevelop,然后单击“项目”→“项目选项”→“运行选项”。添加这些变量

Name: QTDIR Value: /opt/Qtopia
Name: QPEDIR Value: /opt/Qtopia
Name PATH Value: /opt/Qtopia/bin:$PATH
Name LD_LIBRARY_PATH Value: /opt/Qtopia/lib:$LD_LIBRARY_PATH

类似地,为桌面开发添加到“Make 选项”

Name: QTDIR Value: /opt/Qtopia
Name: QPEDIR Value: /opt/Qtopia
Name PATH Value: /opt/Qtopia/bin:/opt/Qtopia/tmake/bin:$PATH
Name LD_LIBRARY_PATH Value: /opt/Qtopia/lib:$LD_LIBRARY_PATH
Name TMAKEPATH Value:/opt/Qtopia/tmake/lib/qws/linux-generic-g++

添加-lqtopia到 skizzy.pro 中的 LIBS 行,因为 Qtopia 1.7 添加了一个新库。

此时,您需要手动生成 Makefile,因为 KDevelop 没有正确使用 tmake

# export TMAKEPATH=/opt/Qtopia/tmake/lib/qws/linux-generic-g++
# tmake -o Makefile skizzy.pro

然后您可以从 KDevelop 中构建项目 (F8)。这个小故障将在使用 qmake 生成 Makefile 的较新版本的 Qtopia 中得到解决。

进入正题

让我们为 skizzy 添加一些功能。

启动 Designer 2 应用程序并打开 skizzybase.ui,然后删除 QLabel。在第一个选项卡上添加一个 QComboBox,在第二个选项卡上添加一个 QListBox,在第三个选项卡上添加一个 QMultiLineEdit,例如(图 2)。

Getting Started with Qtopia

图 2. 如果您使用 QLayouts,它允许您的应用程序根据显示分辨率或屏幕旋转调整大小。

保存 .ui 文件。

使用 KDevelop 打开文件 skizzy.cpp。您将看到我们的应用程序派生自 skizzyBase

skizzy::skizzy( QWidget* parent,  const char* name, WFlags fl )
    : skizzyBase( parent, name, fl )

我想将 main.cpp 更改为一种更好的构造应用程序的方法,该方法是在创建 KDevelop 的 Qtopia 模板之后添加的。

我们更改常用的 main() 函数

int main( int argc, char ** argv )
{
    QPEApplication a( argc, argv );
    skizzy mw;
    a.showMainWidget( &mw );
    return a.exec();
}

为 Qtopia 的应用程序宏

QTOPIA_ADD_APPLICATION("skizzy",skizzy);
QTOPIA_MAIN

这允许我们创建一个快速启动应用程序,通过使用内存中已有的通用应用程序构造函数来帮助加快启动时间。

我的应用程序 skizzy 尚无任何功能,因此包含

#include <qpe/fontdatabase.h>

添加一个私有成员

FontDatabase fdb;

和一些函数作为私有槽

private slots:
void fillCombo();
void comboSelected(const QString &);
void showFont( QListBoxItem *);

我们需要在 skizzy.cpp 中添加几行代码,用于我们将要使用的内容

#include <qstringlist.h>
#include <qcombobox.h>
#include <qtabwidget.h>
#include <qlistbox.h>
#include <qmultilineedit.h>
#include <qfont.h>
#include <qfontinfo.h>

#include <qpe/fontdatabase.h>

然后添加实现,我们将把我们的窗口小部件信号连接到这些实现

/*
This function uses Qtopia's FontDatabase to
fill the combobox with a list of font names.*/
void skizzy::fillCombo()
{
	QStringList families = fontdb.families();
  	for ( QStringList::Iterator f = families.begin(); f != families.end();++f ) {
      	QString family = *f;
		ComboBox1->insertItem( family);
	}
}

/*
This gets called when the combobox is selected, and
fills the listbox on the second tab with the name,
style and point size for the family of fonts
selected, and raises it. */
void skizzy::comboSelected(const QString &selectedFont)
{
ListBox1->clear();

 QStringList styles = fdb.styles( selectedFont );
  	for ( QStringList::Iterator s = styles.begin(); s != styles.end();++s ) {
              QString style = *s;
              QValueList<int> smoothies = fdb.smoothSizes( selectedFont, style );
               for ( QValueList<int>::Iterator points = smoothies.begin(); points != smoothies.end(); ++points ) {
                   QString pointSize = selectedFont + " "+ style +" "+QString::number( *points ) + " ";
                  ListBox1 ->insertItem( pointSize);
               }
           }
    TabWidget2->showPage(tab2);
}

/*
This shows example text of the selected font in
the QMultiLineWidget on the 3rd tab, and raises it.*/
void skizzy::showFont( QListBoxItem *item)
{
	QStringList fontItemString = QStringList::split(' ',item->text());
	QString family, style, point;

    family = fontItemString[0];
    style = fontItemString[1];
    point = fontItemString[2];
    bool ok;
    int i_size = point.toInt(&ok,10);

    if (!ok) {
        style += " "+fontItemString[2];
        point = fontItemString[3];
        i_size = point.toInt(&ok,10);
    }

    QFont selectedFont( family);
    selectedFont.setPointSize(i_size);

     if(style.find("Italic",0,TRUE) != -1) {
          selectedFont.setItalic(TRUE);
     }

     if(style.find("Bold",0,TRUE) != -1) {
        selectedFont.setWeight(QFont::Bold);
     }

     if(style.find("Light",0,TRUE) != -1) {
        selectedFont.setWeight(QFont::Light);
    }

    MultiLineEdit1->setFont( selectedFont);
    MultiLineEdit1->setText( tr( "The Quick Brown Fox Jumps Over The Lazy Dog" ) );
    MultiLineEdit1->setWordWrap( QMultiLineEdit::WidgetWidth);

	TabWidget2->showPage(tab3);
}

Qt 和 Qtopia 使用信号在窗口小部件之间发送消息。每个窗口小部件都有一些它发出的信号,我们可以使用 connect 宏来链接功能。

连接 ComboBox1 的 activated 信号和 ListBox1 的 clicked 信号到我们的槽,像这样

skizzy::skizzy( QWidget* parent,  const char* name, WFlags fl )
    : skizzyBase( parent, name, fl )
{
    connect(bye, SIGNAL(clicked()), this, SLOT(goodBye()));
    connect(ComboBox1, SIGNAL(activated(const QString &)), this, SLOT(comboSelected(const QString &)));
    connect(ListBox1, SIGNAL( clicked ( QListBoxItem * )), this, SLOT(showFont( QListBoxItem*)));
    fillCombo();
}

请注意槽函数的参数类型与信号的类型完全相同。

在 KDevelop 中,按 F8,或在菜单中选择“构建”→“构建项目”。

现在应该使用本机编译器进行编译。要运行它,启动 QVFb,然后简单地选择“构建”→“执行主程序”。skizzy 应用程序应该会在 QVFb 中显示出来。

Getting Started with Qtopia

图 3. 具有真实(即使不是有用)功能的 Skizzy。

交叉编译

因此,既然我们有了一个运行合理的程序,我们需要为 Archos 设备交叉编译它。我们必须更改项目设置以找到正确的库。

我们现在准备好进行交叉编译,因此通过从菜单中选择“构建”→“清理项目”来清理项目。

您需要使用“项目选项”→“Make 选项”对话框更改 Make 选项

Name: QTDIR Value: /opt/Qtopia/arm
Name: QPEDIR Value: /opt/Qtopia/arm
Name PATH Value: /usr/local/arm/bin:/opt/Qtopia/tmake/bin:$PATH
Name TMAKEPATH Value:/opt/Qtopia/tmake/lib/qws/linux-arm-g++

删除 Makefile,并从命令行运行以下命令以创建使用 arm-linux 编译器进行编译的 Makefile

# export TMAKEPATH=/opt/Qtopia/tmake/lib/qws/linux-arm-g++
# tmake -o Makefile skizzy.pro

按 F8 构建项目。您现在可以获取生成的二进制文件,使用 USB 将其传输到 Archos 设备,并从那里运行它!

如果您想创建一个可安装的软件包,Qtopia 使用来自 handhelds.org 的 Itsy Package Management (ipkg),以使用 Software Packages 应用程序安装内容。有关 ipkg 和 Qtopia 开发的更多信息,请访问 Trolltech 的 Qtopia.net 网站。

Lorn Potter 在 Trolltech 担任 Qtopia 社区经理。他是一位美国人,与他的澳大利亚妻子和儿子住在阳光明媚的澳大利亚布里斯班。他是一位自学成才的开源程序员,也是 Opie(开放掌上集成环境)项目的核心开发人员。他还曾担任音乐家、音响工程师和滑雪爱好者。

加载 Disqus 评论