|
楼主 |
发表于 2004-3-13 16:23:28
|
显示全部楼层
第三章剩下的部分:
窗口组件
gtkmm 应用程序由一系列窗口组成, 这些窗口包含了像按钮, 文本框这样的一些组件. 在其它一些系统上, 窗口组件被称作"控件". 对于你的应用程序中的每个组件, 对应地, 在你的代码里有相应的 C++ 对象. 所以当你想控制组件行为的时候, 只要调用这个组件对象的相应方法即可.
组件被排列在褚如 frame, notebook 这样的窗口容器中. 在架构上, 属于组件的组件这样一个层次. 有一些像 Gtk::VBox 这样的容器组件是不可见的 - 它们只被用来放置其它组件. 下面这个例子用来把两个 Gtk::Button 控件放到一个 Gtk::VBox 容器中.
m_box.pack_start(m_Button1);
m_box.pack_start(m_Button2);
接下来我们演示怎样再把这个包含两个按钮的 Gtk::VBox 加入一个 Gtk::Frame 中. 它含一个可以的边框和标题.
这本书中的大部分章节处理特定的组件. 关于把组件添加到容器组件中, 要得到更多的信息, 请看容器组件这一章.
尽管你可以用 C++ 代码来控制窗口和组件的外观和布局, 但你会发现, 用 Glade 来设计你的界面, 或在运行时使用 libglademm 动态加载界面是一件更轻松的事. 请参考 Glade 和 libglademm 这一章.
信号
和其它 GUI 工具集一样, gtkmm 也是事件驱动的. 当一个事件发生时, 比如说一个按钮被按下, 被按下的这个组件将发出一个合适的信号. 每种组件有一组不同的信号集可以发送. 为了使这个按钮按下的动作产生合适的效果, 我们可以建立一个信号号柄(signal handler)来捕捉这个信号.
gtkmm 使用 libsigc++ 来实现信号操作. 下面这个例子演示怎样把 Gtk::Button 的单击(clicked)信号连接到 on_button_clicked 这个信号处理句柄上.
m_button1.signal_clicked().connect( sigc::mem_fun(*this, &HelloWorld:n_button_clicked) );
要得到更多关于信号的信息, 请参考附录.
要想知道实现你自己的信号处理方式, 而不仅仅是连接到现成的 gtkmm 信号. 请参考附录.
Glib::ustring
你一定很奇怪 gtkmm 没有把 std::string 纳入自己的接口中. 实际上, Glib::ustring 和它非常相似, 甚至不用看下面的部分, 你就可以把它当成 std::string 来用. 但如果你是想使用编程语言而不是英语的话, 最好还是看一下吧.
std::string 使用 8 位字符编码, 但是对于像阿拉伯语, 汉语和日语这样的语言来说, 8位编码是不够的. 尽管这些语言的编码已经被 Unicode Constortium 组织详细说明了, 但是 C 和 C++ 还没有提供标准的 Unicode 支持. GTK+ 和 GNOME 采用 UTF-8 编码来实现 Unicode, 这也是 Glib::ustring 所包装的东西. 它提供和 std::string 几乎相同的接口, 以及和 std::string 的自动类型转换功能.
UTF-8 编码的优点之一是, 除非必要, 你可以不使用它, 所以你不必经常把自己的代码变来变去. std::string 还将继续使用 7 位的 ASCII 编码. 但是一旦你想本地化你的程序, 比如说, 汉化, 那你就会开始遇到各种奇怪的问题, 可能还会崩溃. 然后你可能只得用 Glib::usting 取而代之.
要注意的是 UTF-8 和其它一些 8 位的编码如 ISO-8859-1 并不兼容. 比如, 德语中的元音变音并不在 ASCII 码的编码范围内, 你还需要 UTF-8 编码中额外一位. 如果你的代码中包含 8 位的字符串, 你需要把它们转变成 UTF-8 编码 (比如, 巴伐利亚的问候语 "Grü? Gott" 可能会显示成 "Gr\xC3\xBC\xC3\x9F Gott").
你应该尽量避免 C 类型的指针算法, 以及像 strlen() 这样的函数. 在 UTF-8 编码中, 每个字符可能占 1 到 6 个字节, 所以你并不能假定下一个字节一定是另一个字符. 所有的这些细节 Glib::ustring 都已经帮你考虑到了, 所以你可以从字符的层面上考虑使用 Glib::ustring::substr() 这样的函数, 而不用再去考虑字节这样的细节了.
不像 Windows 的 UCS-2 Unicode 解决方案, 这并不需要编译器提供特别的选项来处理这样字符串, 也不会导致含 Unicode 的可执行文件或库与含 ASCII 的不兼容.
参考
要得到更多关于 UTF-8 字符的信息, 请参考国际化这一节.
中间型别
gtkmm 的部分 API 使用了中间型别, 比如使用 Glib::StringArrayHandle, 而不是像 std::vector 或 std::list 这样的 C++ 标准容器. 你不用声明这些型别 -- 你也可以使用自己喜欢的 C++ 标准容器. gtkmm 会为你做好自动型别转换的工作. 下面是一些常用的中间型别:
Glib::StringArrayHandle 或 Glib::ArrayHandle<Glib::ustring>: 使用 std::vector<Glib::ustring>, std::list<Glib::ustring>, const char*[], 等等.
Glib:istHandle<Gtk::Widget*>: 使用 std::vector<Gtk::Widget*>, std::list<Gtk::Widget*>, 等等.
Glib::SListHandle<Gtk::Widget*>: 使用 std::vector<Gtk::Widget*>, std::list<Gtk::Widget*>, 等等.
用 gtkmm 写 Hello World
到目前为止, 我们已经可以自己的所学来写一个真正的程序了. 根据计算机科学的传统, 你们现在以 gtkmm 的方式来介绍 Hello World:
源代码:
文件: helloworld.h
[php]
#ifndef GTKMM_EXAMPLE_HELLOWORLD_H
#define GTKMM_EXAMPLE_HELLOWORLD_H
#include <gtkmm/button.h>
#include <gtkmm/window.h>
class HelloWorld : public Gtk::Window
{
public:
HelloWorld();
virtual ~HelloWorld();
protected:
// 消息句柄:
virtual void on_button_clicked();
// 成员组件:
Gtk::Button m_button;
};
#endif // GTKMM_EXAMPLE_HELLOWORLD_H
[/php]
File: helloworld.cc
[php]
#include "helloworld.h"
#include <iostream>
HelloWorld::HelloWorld()
: m_button("Hello World") // 创建一个含 "Hello World" 标签的按钮.
{
// 为窗口设置边框宽度.
set_border_width(10);
// 当按钮收到 "clicked" 消息时, 会调用 hello() 方法.
// hello() 方法将在下面定义.
m_button.signal_clicked().connect(sigc::mem_fun(*this, &HelloWorld:n_button_clicked));
// 这个语句将按钮包装进窗口(一个容器)
add(m_button);
// 最后一步是显示刚刚创建的组件...
m_button.show();
}
HelloWorld::~HelloWorld()
{
}
void HelloWorld:n_button_clicked()
{
std::cout << "Hello World" << std::endl;
}
[/php]
文件: main.cc
[php]
#include <gtkmm/main.h>
#include "helloworld.h"
int main (int argc, char *argv[])
{
Gtk::Main kit(argc, argv);
HelloWorld helloworld;
Gtk::Main::run(helloworld); //显示窗口, 关闭时返回.
return 0;
}
[/php]
尝试着编译并运行这个程序. 你会看到这样的结果:
图片 3.1. Hello World
[图片]
真让人激动, 嗯? 让我们回过头来看一下代码. 先看 HelloWorld 类:
[php]
class HelloWorld : public Gtk::Window
{
public:
HelloWorld();
virtual ~HelloWorld();
protected:
// 消息句柄:
virtual void on_button_clicked();
// 成员组件:
Gtk::Button m_button;
}
[/php]
这个类实现了 "Hello World" 窗口. 它由 Gtk::Window 派生, 并且仅有一个 Gtk::Button 成员. 我们已经使用构造函数为该窗口完成了所有的初始化工作, 包括挂接消息. 下面是除去注释的代码:
[php]
HelloWorld::HelloWorld()
:
m_button ("Hello World")
{
set_border_width(10);
m_button.signal_clicked().connect(sigc::mem_fun(*this, &HelloWorld:n_button_clicked));
add(m_button);.
m_button.show();
}
[/php]
注意, 我们使用初始化列表的方式给 m_button 对象添加了一个 "Hello World" 的标签.
接下来调用 Windows 类的 set_border_width() 方法, 设置窗口的边框和它所包含的组件之间的空白距离.
再把 m_button 的 clicked 消息挂接到消息号柄上. 结果会向 stdout(标准输入) 打印出友好的问候语.
下一步, 我们使用 Window 类的 add() 方法把 m_button 加到 Window 类中. (add() 方法由 Gtk::container 类继承而来, 我们将在容器这一章中具体介绍.) add() 方法仅仅将组件添加到窗口中, 它并不负责显示这些组件. 所以 gtkmm 组件在创建以后都是不可见的 - 要显示它们, 你必须调用 show() 方法, 通常我们会在下个一语句就调用它.
现在让我们来看看没有注释过的 main() 函数:
[php]
int main(int argc, char** argv)
{
Gtk::Main kit(argc, argv);
HelloWorld helloworld;
Gtk::Main::run(helloworld);
return 0;
}
[/php]
我们首先初始化一个叫 kit 的对象, 它是 Gtk::Main 类型的. 每一个程序都有这样一个东西. 我们将命令行参数传递给它. 它提取自己需要的参数, 将剩下的部分留给我们, 就像前面已经描述过的那样.
接着我们创建了 HelloWorld 类的实例, 它的构造函数没有参数, 但它也是不可见的. 当我们调用 Gtk::Main::run(), 并给它提供一个 helloworld 窗口作为参数时, 它将窗口显示出来并进入消息循环. 在消息循环中, gtkmm 是空闲的, 它等待用户的操作动作并作出相应的反应. 当用户关闭窗口时, run() 函数将返回, main() 函数的最后一行将被执行. 程序也就结束了. |
|