繁體中文翻譯:Jim Huang (黃敬群)
KDE 2.0 在 2000 年九月份正式釋出,相較於其前身 KDE 1.X 是很大的突破。桌面系統幾乎重新撰寫過,並受益於許多威力強大的新技術:元件 (components)、RPC/IPC、網路通透性 (network transparency)、背景音效伺服器 (sound daemon)、...
或許有些人會認為以上這些新技術會讓開發應用程式變得困難,但,實際上恰好相反。這些新技術相當新潮並易於使用,這也是我在本文所要說明的。
本文所有的程式範例都在 KDE 2.1 下撰寫並測試過,主要是從以下不同的地方取得的:cvs 上的範例程式、kde book、mails (譯注:應該是指 mailing-list)、文件檔、教學說明檔 (tutorials),以及應用程式本身。你可以在 KDE 開發者站台 developer.kde.org 上取得以上這些資料。
值得注意的是,這些新技術都當穩定,在歷經三份穩定的釋出版 (stable releases,指的是 KDE 2.0、2.1,以及 2.2) 時,都幾乎沒有更動過。這些也是 KDE 底層最重要的技術,所以,你可以視他們如堅石一般穩固。
使用者介面元件 (GUI components) 是 KDE 中相當具有威力的技術。基本上,每個元件都允許應用程式提供自己專屬的特徵與介面,並且適用的範圍不僅包含應用程式,連同嵌入 (embedded frame) 到其他桌面環境中的應用程式也可。每個元件都可單獨發展,也可以指定若干元件賦予某些任務,一些經典的例子是:一個郵件閱讀程式需要一個文字編輯器 (Text editor) 元件以便能夠發信;一個整合開發環境 (Integreted Development Environment) 也需要一個文字編輯器 (Text editor) 來編輯程式碼 (當然不見得要同一份程式碼);試算表程式 (spreadsheet) 則需要一個報表產生器 (Chart builder) 來分析資料;而一個普通的應用程式或許也需要個 html browser 等等。
元件增加了程式碼重用性 (code reuse) 與模組性 (modularity),也因此更應該儘量被使用。為了讓元件技術更能切合實際,KDE 小組讓這些技術能夠相當簡單的撰寫與使用。
在 Gnome 陣營中,其元件技術是由 Bonobo 這個建構於 CORBA 分散式運算標準的項目所提供的。過去,KDE 也曾使用過 CORBA,但最後卻放棄而改用另一項技術:KPart。這個選擇曾經廣受批評,雖然幾乎沒有多少人知道這個背景與其因果。事實上,這是個正確的抉擇,而且這也是為什麼我要撰寫本文來說明的原因了。
簡而言之,一個好的使用者介面元件技術應該具有以下特徵:
如果採用 CORBA 的話,則相當難符合以上需求,而非變得容易。CORBA 是個相當優秀的技術,但是並不適合使用者介面元件採用。在以下的部分,我將說明 KPart 是如何在這領域拔得頭籌 (譯注:即取代 CORBA 成為 KDE 底層元件技術)。
當 KDE 核心開發人員發現使用 CORBA 逐漸成為他們的夢魘時,他們開始撰寫一個輕量級 (lightweight) 而有效的元件技術來取代:KPart。
KPart 以共享程式庫 (shared libraries) 為基礎,這讓元件直接以 C++ 物件的形式存在,也因此沒有必要用 IDL 語法去包裝 (wrap) 他們的特徵,所有項目都可以不假外力的取得。所以,撰寫一個元件就如同撰寫一個 C++ 物件一般,只需要負責在應用程式裡頭所需要注意的項目。共享程式庫也相當容易去觸發 (activate) 或卸載 (unload)。你甚至不必考量 fork 的產生,因為程式碼就在應用程式裡頭運行 (譯注:這句話有點抽象,主要是說明一般程式設計師只要去注意撰寫元件時的 Parts,而不需留意整體的運作)。 95% 與元件相關的工作都由 KPart API 所處理,也因此,撰寫與使用元件就變成相當容易的工作了,待會我就會援以範例說明。
KPart 有兩個部分:components (譯注:此處 component 不譯作「元件」,以區隔一般通稱的「元件技術」與 KPart 中的元件) 與 plugins。Components 提供可以讓你顯示的widget (譯注:Widget 特指 Window System 中用來顯示的單位元件,如對話框、捲軸等),且可以用來延伸應用程式的選單項目與增加元件專屬的動作 (action)。而 plugin 則沒有 widget,通常以一個選單的項目 (menu entry) 的方式提供一項新特徵,並且,在應用程式裡的選單,會定義在一個 XML 檔中 (譯注:即 XML-GUI)。
使用一個 Part 相當的簡單,這邊有個範例就是讓你能夠嵌入一個 html browser 到你的應用程式中。這段程式碼片段是使用由 kapptemplate 所產生的程式範本 (application template) (application template 產生器是由 Kurt Granroth 所撰寫)。
KTrader::OfferList offers = KTrader::self()->query("text/html",
"'KParts/ReadOnlyPart' in ServiceTypes");
KLibFactory *factory = 0;
// 理論上,我們只需留意到一開始,但我們可以嘗試如果因為某種理由
// 而沒辦法載入的狀況
KTrader::OfferList::Iterator it(offers.begin());
for( ; it != offers.end(); ++it) {
KService::Ptr ptr = (*it);
// 現在我們知道這段可以處理 HTML 顯示,且為一個 Part。
// 既然是個 Part,必定有個程式庫,咱們現在嘗試來載入
factory = KLibLoader::self()->factory( ptr->library() );
if (factory) {
m_html = static_cast<KParts::ReadOnlyPart *>(factory->create(this,
ptr->name(), "KParts::ReadOnlyPart"));
break;
}
}
|
所有可以使用的元件都被索引化 (indexed) 並儲存於資料庫中。你可以透過 KTrader 來作查詢。KTrade 允許你去指定你所要求元件的特徵:元件名稱、可以處理的 mimetype ,或是其他參數。而在這邊,我們要求一個可以處理 text/html 唯讀顯示 (譯注:就是一般我們稱「瀏覽網頁」的動作) 的元件,則 KTrader 會傳回一份有效元件的列表 (KService::Ptr),並依據 preference 作排序。KService::Ptr 具有一份關連性的程式庫,可讓你透過 KLibLoader 來載入系統,而這個程式庫則設計有可以建立 widget 的 factory (譯注:factory 是 Design Patterns 專有名詞,在此不作翻譯。關於 factory 的定義,可以見物件導向設計的專書《Design Patterns》,簡言之,factory 是指如同我們所見的工廠可以源源不絕的產生新的產品一般的行為,我們可以透過特定的 factory 來產生對應的物件),當然,widget 正是你所要求的元件。
乍看下似乎頗複雜,不過請放心,不會變得更困難了:尋找特定的服務、載入程式庫、取得程式庫的 factory,並且藉此產生我們所要的 widget。就是這樣!在這個範例來說,預設的 html 瀏覽器將會因此被觸發 (可能是 khtml,也可能透過 kmozilla 來觸發 gecko 也說不定)。如果要求不同的元件,只要更改原本的 text/html 成其他 mimetype 即可。
透過相同的途徑,你可以使用一個文字編輯器。以下的程式片段變得更緊密,但是基本上不需要更動。這個程式片段是節取自 KTextEditor interface documentation 中。
KTrader::OfferList offers = KTrader::self()->query( "KTextEditor/Document" ); ASSERT( offers.count() >= 1 ); KService::Ptr service = *offers.begin(); KLibFactory *factory = KLibLoader::self()->factory( service->library() ); ASSERT( factory ); m_part = static_cast<KTextEditor::Document *>( factory->create( this, 0, "KTextEditor::Document" ) ); ASSERT( m_part ); QWidget * view = m_part->createView( my_parent_widget, 0 ); |
如你所見,八行的程式碼中,包含三個 assert 敘述,你可以發現在應用程式中使用 Part 是多麼簡單的一回事,我沒說錯吧。
現在,想像我們已經撰寫一個相當棒的應用程式,並且想要在其他應用程式中,以 KParts 元件的形式被使用。這邊有份 Kurt Granroth 所撰寫的 KPart tutorial,有提及這部分所必要的程式碼片段,所舉 KPart 的例子,就是 aKtion。
基本上,你必須提供一個能夠在程式庫載入後能夠作用的 factory。這個 factory 角色只有提供一個 KPart 物件,而 KPart 會安裝新的選單項目並且回傳一份應該納入你的應用程式的 widget。
| aktion_part.h | aktion_part.cpp |
|---|---|
#ifndef __aktion_part_h__
#define __aktion_part_h__
#include "kparts/browserextension.h"
#include "klibloader.h"
class KAboutData;
class KInstance;
class AktionBrowserExtension;
class QLabel;
class AktionFactory : public KLibFactory
{
Q_OBJECT
public:
AktionFactory() {}
virtual ~AktionFactory();
virtual QObject* create(QObject* parent = 0,
const char* name = 0,
const char* classname = "QObject",
const QStringList &args = QStringList());
static KInstance *instance();
static KAboutData *aboutData();
private:
static KInstance *s_instance;
};
class AktionPart: public KParts::ReadOnlyPart
{
Q_OBJECT
public:
AktionPart(QWidget *parent, const char *name);
virtual ~AktionPart() { closeURL(); }
bool closeURL() { return true; }
protected:
virtual bool openFile()
{ widget->setText(m_file); return true; }
QLabel *widget;
};
#endif
|
#include "aktion_part.h"
#include "kinstance.h"
#include "klocale.h"
#include "kaboutdata.h"
#include "qlabel.h"
extern "C"
{
void *init_libaktion()
{
return new AktionFactory;
}
};
/**
* 我們需要一份 factory function 的 static instance
*/
KInstance *AktionFactory::s_instance = 0L;
AktionFactory::~AktionFactory()
{
if (s_instance) {
delete s_instance->aboutData();
delete s_instance;
}
s_instance = 0;
}
QObject *AktionFactory::create(QObject *parent,
const char *name, const char*,
const QStringList& )
{
QObject *obj = new AktionPart((QWidget*)parent, name);
emit objectCreated(obj);
return obj;
}
KInstance *AktionFactory::instance()
{
if ( !s_instance ) s_instance = new KInstance( aboutData() );
return s_instance;
}
KAboutdata *AktionFactory::aboutData()
{
KAboutData *about = new KAboutData("aktion",
I18N_NOOP("aKtion"), "1.99");
return about;
}
AktionPart::AktionPart(QWidget *parent, const char *name)
: KParts::ReadOnlyPart(parent, name)
{
setInstance(AktionFactory::instance());
// 建立一個可以插入我們 widget 的 canvas
QWidget *canvas = new QWidget(parent);
canvas->setFocusPolicy(QWidget::ClickFocus);
setWidget(canvas);
// 顯示一個黑白的簡單 widget 作為範例
widget = new QLabel(canvas);
widget->setText("aKtion!");
widget->setAutoResize(true);
widget->show();
}
|
以上八十行的程式碼,有 90% 是一般性的並且對其他元件來說,都是可重用的 (reusable),而只有最後 15 是 aktion part 所專屬的。當然,如果你使用 kapptemplate 或 kdevelop 這類的開發工具,甚至就不需要花時間撰寫以上這八十行了,因為都會自動建構 :-)
Konqueror 就是我們所知的 KDE 2 Web Browser。但 Konqueror 並不只是一個網頁瀏覽器,事實上,Konqueror 正是一個處理 Kio slaves 資料的前端 shell (譯注:原文是 "it is just a Shell that requests data using Kio slaves (another KDE technology)",我為了口語化,就稍微修改這個敘述),並且能嵌入使用 KPart 展現的資料。這是使用 KDE 元件的最有趣的例子了。
這邊有份不完整的列表,是列出提供 KPart 元件的應用程式,當然,都可以被嵌入到 Konqueror 之中:
記得,這份列表意味著你可以在你的應用程式中,用八行的程式碼 (譯注:請回頭見 [範例二]) 嵌入以上任何元件,而隨著 KDE 2.2 的來臨,這份列表會更豐富!
KDE 也因其 Office 套件而聞名:提供了文字處理器 (kword)、試算表 (kspread), 簡報展示軟體 (kpresenter)、向量繪圖工具 (killustrator),以及報表繪製工具 (kchart) 等等。
而,較少人注意的是,KOffice 的性質是「每個應用程式就是以一個元件的形式存在」代表一份 KOffice 文件的類別 (譯注:"class" 的翻譯,以下不多作說明) 是KoDocument,而 KoDocument 衍生於 (譯注:"inherits from" 的翻譯,以下亦不多作說明)KParts::ReadWritePart,因此,每份 KOffice 文件可以被嵌入並以一個 KPart 元件的形式顯示。這裡就是 KPart 應用到 KOffice 的優勢:可以很簡單的撰寫與使用元件、快速與輕量級的載入 (譯注:就是 KDE KPart 元件技術 vs. CORBA 的比較) 等等。KOffice 元件技術增加更多能夠被觸發的子工具 (activation subtilities),因為他們可以被重複應用,論功能性來說,他們就是 KParts。
KOffice embedding 在 Konqueror 中被使用作預覽在瀏覽器中的 KOffice 文件。但,主要的使用是讓 KWord 能夠即時嵌入一份試算表、公式項目,以及一份圖片到一個 KWord 文件中。當你在文字欄位編輯時,KWord 元件就會被觸發;而當你在試算表項目編輯時,KSpread 跟著被觸發 (請見下方快照)。
當你啟動 KWord 或 KSpread 時,你會看到一個建立新文件或開啟舊檔的對話框,如果你注意對話框裡頭的視窗 (請見快照),你將會發現工具列 (toolbar) 幾乎是空的,而且就跟 KWord 和 KSpread 是一致的。這個視窗是一個通用性的 KOffice 文件介面 (譯注,原文是 "shell",為了口語方便,我稍微修改敘述),並且包含與 KWord 或 KSpread 無關的項目 (譯注:換言之,就是這兩者通用的)。這會視 KWord 或 KSpread 元件被觸發與否而變成對應的應用程式。KSpread 與 Kword 唯一的區隔是,當他們能夠處理的 mimetypes (藉由 Open Dialog 的方式觸發)。
| KWord 開啟一份文件 | KSpread 開啟一份文件 |
|---|---|
|
|
現在,建立一份 KWord 文件,並在裡頭嵌入一份 KSpread 文件。當你在 KWord 文件上編輯時,所有的選單與工具列都是 KWord 的,而 KSpread 欄位還是靜態的區域 (請見快照)。但是,這時如果你按一下 KSpread 欄位,你將發現 KWord 選單與工具列就不見了,而是 KSpread 選單與工具列取而代之。以上所發生的,就是元件的解除觸發(deactivation) 與觸發的過程:KWord 元件被解除觸發狀態,其選單與工具列就自 Sehll 視窗被移除。在這過程的同時,會產生一個空的 Koffice 視窗 (跟在 opening dialog 後方的是同一個),下一刻,KSpread 元件就被觸發了,KSpread 選單與工具列就被新增上去了,原本的試算表就轉身一變成為可編輯的狀態。既然 KSpread 的特徵都以元件的形式被提供,也就是說,你可以直接透過 KSpread 來編輯試算表,如同你在快照所見一般。
| KWord 文件包含一份 KSpread 欄位 | |
|---|---|
| KWord 被觸發 | KSpread 被觸發 |
![]() |
![]() |
| 同一份 KSpread 文件在 KSpread 中 | |
|---|---|
![]() |
|
There is a program that highlights the true component nature of KOffice. (譯注:以上這句我抓不到感覺,所以保留原文) 這稱為 KOffice 工作區 (Koffice workspace),你可以藉由輸入 "koshell" 來啟動。並且,你可注意到主視窗個我討論過的通用性視窗。你可以按擊左側 KOffice 元件的圖示,來觸發這些元件,之後,這個通用性的 KOffice Shell 就會轉變成 KWord、KSpread,或是其他你要求的應用程式。
| 空白 KOffice 工作區 | |
|---|---|
![]() |
|
不同於 Gnome 的辦公室整合套件,如 OpenOffice、GNU Spread,或是 Abiword,這些在 Gnome 之前就已經發展存在的專案 (譯注:意思就是說,Gnome 雖然以 CORBA 作為元件技術標準,但是其上殺手級應用程式卻非採用 Gnome 標準的元件技術),KOffice 是從無到有、跟隨著 KDE 的發展。對 KDE Office 套件來說,快速發展是可能的,因為KDE 底層的架構與發展的 framework 相當具有威力,並且相當易於使用。
舉例來說,我們所見的 KOffice 工作區只用不到 600 行程式碼就完成了,這相當於一個普通程式設計師三天份的工作量。
philippe@werewindle /usr/src/kde-cvs/koffice/koshell $ ls
AUTHORS Makefile.am dummy.cc koshell_shell.cc
CVS/ Makefile.in koshell.desktop koshell_shell.h
Makefile TODO koshell_main.cc
philippe@werewindle /usr/src/kde-cvs/koffice/koshell $ wc -l *.h *.cc
121 koshell_shell.h
1 dummy.cc
49 koshell_main.cc
434 koshell_shell.cc
605 total
|
如果你想開撰寫新的 KOffice 應用程式,最重要的任務就是去建立一個衍生於KoDocument 的新類別。KOffice 所提供的範例大約是 430 行的程式碼。
philippe@werewindle /usr/src/kde-cvs/koffice/example $ ls
CVS/ README example_factory.cc example_view.cc
Makefile configure.in.in example_factory.h example_view.h
Makefile.am example.desktop example_part.cc main.cc
Makefile.in example.rc example_part.h x-example.desktop
philippe@werewindle /usr/src/kde-cvs/koffice/example $ wc -l *.h *.cc
47 example_factory.h
42 example_part.h
48 example_view.h
106 example_factory.cc
70 example_part.cc
67 example_view.cc
49 main.cc
429 total
|
如果要把以上的範例變成一個真實、全功能的 KOffice document 處理程式,為達成這個目標,你必須重新轉寫三個函式:ExamplePart::loadXML()、ExamplePart::saveXML(),以及ExamplePart::paintContent(),以上三者分別具有從一個 XML 檔中載入你的文件資料、把文件資料儲存於一份 XML 檔中,最後是依據文件內容繪製與呈現應有的效果。
如你所體會的,KDE 擁有一個相當具有威力的架構,可盡可能廣泛發揮元件技術。KPart 實際上在 KDE 各處都高度使用著,而 Gnome 的元件技術 Bonobo 則還有點距離,而且據我所知,Gnome 2.0 也還不會全盤使用 Bonobo (譯注:原文是 "won't be bonoboized"),Gnome 仍然還是緊跟著 KDE 的發展步調。
在我下一篇文章中,我將會討論 KDE 中的 IPC/RPC 機制 —— DCOP。
Copyright (c) 2001 Philippe Fremy (pfremy at freehackers dot org)
Translation to Traditional Chinese Copyright (c) 2001 Jim Huang (jserv at kaffe dot org)
Last modification : $Date: 2005/01/04 09:12:39 $