使用 Mono 快速开发 GNOME

作者:Robert Love

Mono 是 .NET 开发平台的开源实现,这是一个强大且现在开放的开发平台。Mono 包含许多组件:通用语言基础结构 (CLI) 虚拟机、C# 编译器和一组类库。Mono 根据 ECMA 标准 334 和 335 分别实现了 C# 语言和运行时环境。

Mono(在西班牙语中是猴子的意思)提供了各种类库,包括 .NET Framework SDK 的开源实现。然而,在本文中,我们将讨论 Mono 最重要的资产之一:它对 GNOME 的支持,以 Gtk# 的形式。

Gtk# 是 Gtk+ 工具包和各种其他 GNOME 库的 .NET 语言绑定。Gtk# 不仅仅是一个简单的包装器,它为在 GNOME 环境中开发 GUI 软件提供了一个强大的平台。Gtk# 的语言绑定利用良好的面向对象原则和 C# 风格的设计,使 GNOME 开发变得简单自然,同时又灵活强大。

在本文中,我们将研究一个简单的 C# 应用程序的构建,从一个简单的“Hello, World”应用程序开始,并将其构建成一个基本的 Wikipedia 搜索工具。我们唯一的依赖项将是 Mono 和 Gtk#。大多数发行版都提供了软件包——请参阅在线资源。

Hello, World!

让我们从构建最基本的 Mono 应用程序开始,它将“Hello, World!”打印到控制台

using System;

class first {
    public static void Main (string[] args)
    {
        Console.WriteLine ("Hello, World!");
    }
}

启动您最喜欢的编辑器,输入此代码并将其另存为 first.cs。然后,我们可以使用以下命令将程序编译为可执行映像

$ mcs first.cs

最后,我们可以通过以下命令运行它

$ mono first.exe
Hello, World!

此应用程序实现了第一个类。每个应用程序都需要一个入口点,即类内部的初始函数,供 Mono 运行时跳转到并开始程序的执行。与 C 和 C++ 一样,这是 Main 函数。此函数的原型是

public static void Main (string[] args)

在我们程序的 Main 函数中,我们调用了一个函数 WriteLine,它位于 Console 类中。此函数类似于 printf(),将一行文本写入控制台。它也可以用于输出变量的值

int x = 5;
String s = "wolf";

Console.WriteLine ("x={0} s={1}", x, s);

这会给我们

x=5 s=wolf
彩色 Hello, World!

然而,我们不必将“Hello, World!”限制在控制台中;使用 Gtk#,我们可以构建一个简单的 GUI 对话框,将我们的消息展示给世界

using System;
using Gtk;

class Two {
    static void WindowDelete (object o, DeleteEventArgs args)
    {
        Application.Quit ();
    }

    static void InitGui ()
    {
        Window w = new Window ("My Window");

        HBox h = new HBox ();
        h.BorderWidth = 6;
        h.Spacing = 6;
        w.Add (h);

        VBox v = new VBox ();
        v.Spacing = 6;
        h.PackStart (v, false, false, 0);

        Label l = new Label ("Hello, World!");
        l.Xalign = 0;
        v.PackStart (l, true, true, 0);

        w.DeleteEvent += WindowDelete;
        w.ShowAll ();
    }

    public static void Main (string[] args)
    {
        Application.Init ();
        InitGui ();
        Application.Run ();
    }
}

和以前一样,通过您喜欢的编辑器输入代码。这次,将其另存为 two.cs。要编译此程序,我们需要告诉 Mono 编译器我们要使用 Gtk# 程序集

$ mcs two.cs -pkg:gtk-sharp

但是,运行它是一样的

$ mono two.exe

该应用程序创建一个名为“My Window”的小窗口,并将“Hello, World!”写入窗口(图 1)。窗口是 GtkWindow,标签是 GtkLabel。Gtk 通过盒子布局窗口。盒子是不可见的窗口小部件——仅为了布局目的而存在——其他窗口小部件被打包到其中。盒子内窗口小部件的排列方式决定了窗口内窗口小部件的物理布局。尽管可以在 Gtk 中使用表格排列窗口小部件,但大多数程序员更喜欢盒子,因为它们具有灵活性和强大功能——而且,一旦您掌握了它们,它们并不难使用。

Rapid GNOME Development with Mono

图 1. 我的窗口

Gtk 提供了两种类型的盒子:垂直和水平。垂直盒子称为 vbox,定义了窗口小部件的垂直排列——vbox 将窗口小部件排列成列。水平盒子称为 hbox,通过将窗口小部件排列成行来定义窗口小部件的水平排列。窗口小部件被打包到盒子中,盒子被打包到其他盒子中,盒子被添加到窗口中。

新的 hbox 通过以下方式创建

HBox h = new HBox ();

新的 vbox 通过以下方式创建

VBox v = new VBox ();

表示盒子的新对象具有各种属性,可以设置这些属性来操作盒子的外观和感觉。在之前的示例中,我们在 hbox 上设置了两个属性

h.BorderWidth = 6;
h.Spacing = 6;

在这里,我们将 hbox 的边框和周围间距分别设置为 6 像素。这在 hbox 周围提供了 12 像素的净间距。巧合的是,出于美观和一致性的目的,GNOME HIG(人机界面指南)规定窗口小部件之间最小间距为 12 像素——因此本示例中的 6 像素和 6 像素是完美的。

要将盒子添加到窗口,请在相关窗口上调用 Add() 成员函数并将其提供给盒子

w.Add (h);

要将窗口小部件打包到盒子中,请在相关盒子上调用 PackStart() 成员函数

public void PackStart (Widget child,
            bool expand,
            bool fill,
            uint padding)

在我们的示例中,我们这样做

v.PackStart (l, true, true, 0);

此调用将我们的标签打包到我们的 vbox 中。如果 expand 参数为 true,则窗口小部件子项将展开以填充盒子内的所有可用空间。如果 fill 参数为 true,则窗口小部件将消耗其所有空间进行渲染;否则,如果为 false,则窗口小部件将使用多余的空间进行填充。padding 参数允许在窗口小部件周围、盒子内部添加额外的间距,超出已提供的任何填充。

启动我们的应用程序很简单。我们执行三个简单的步骤

Application.Init ();
InitGui ();
Application.Run ();

Application.Init() 初始化 Gtk# 和应用程序的 GUI。它应该是 Gtk# 应用程序调用的第一个函数之一。执行此函数后,应用程序设置其 GUI,创建和排列其窗口小部件,并绘制初始窗口和其他 UI 元素。在我们的程序中,我们在 InitGui() 函数中执行此操作。一旦一切完成并准备就绪,程序将调用 Application.Run(),比赛就开始了。我们的主窗口弹出是因为我们在 InitGui() 中通过以下方式将其公开

w.ShowAll ();

这会公开窗口和其中打包的所有窗口小部件。因此,一旦应用程序调用 Application.Run(),我们的 UI 元素就会出现。

在执行过程中,程序的响应由窗口小部件的行为处理。一些窗口小部件以预定的方式运行,不需要程序员手动编写任何代码。但是,大多数情况下,程序员希望亲自处理事件。这是通过编写事件处理程序来完成的,该处理程序使用非常有用的 C# 事件功能,Gtk# 会在响应窗口小部件交互时调用该处理程序。

在我们的最后一个示例中,我们有一个这样的事件处理程序。我们希望当用户单击主窗口上的关闭框时,我们的应用程序退出,因此我们需要处理关联的事件,该事件称为 DeleteEvent。这通过编写事件处理程序来完成

static void WindowDelete (object o, DeleteEventArgs args)
{
    Application.Quit ();
}

并将其添加为事件处理程序

w.DeleteEvent += WindowDelete;

函数 Application.Quit() 使 Gtk# 销毁 UI,关闭并终止应用程序。因此,当用户单击主窗口上的关闭框时,我们的应用程序会优雅地退出。

构建更好的示例

现在让我们转向构建一个更完整——我敢说是更有用的——应用程序:一个用于搜索 Wikipedia 的工具。可以肯定的是,我们不会构建任何花哨的东西,但我们可以实现一些有趣的东西。让我们看看构建一个简单的窗口,带有一个文本输入窗口小部件。我们将允许用户在窗口中键入搜索查询并单击按钮(图 2)。然后,应用程序启动用户的 Web 浏览器并在 Wikipedia 上查找搜索词。我们只用几行代码就构建了整个程序,包括 GUI 和启动 Web 浏览器的代码。

Rapid GNOME Development with Mono

图 2. 精美的 GUI

我们可以通过基于上一个示例构建此新应用程序,添加新的窗口小部件和必需的功能。令人惊讶的是,由于 Gtk#,额外的代码并不多!我们开始吧

using System;
using Gtk;

class Example {
    public static Entry search_entry;

    public static void ButtonClicked (object o, EventArgs args)
    {
        String s = "http://en.wikipedia.org/wiki/Special:Search?search=";
        s += search_entry.Text;
        s += "&go=Go";
        Gnome.Url.Show (s);
    }

    static void WindowDelete (object o, DeleteEventArgs args)
    {
        Application.Quit ();
    }

    static void InitGui ()
    {
        Window w = new Window ("Wikipedia Search");

        HBox h = new HBox ();
        h.BorderWidth = 6;
        h.Spacing = 6;
        w.Add (h);

        VBox v = new VBox ();
        v.Spacing = 6;
        h.PackStart (v, false, false, 0);

        Label l = new Label ("_Search:");
        l.Xalign = 0;
        v.PackStart (l, true, false, 0);

        v = new VBox ();
        v.Spacing = 6;
        h.PackStart (v, true, true, 0);

        search_entry = new Entry ();
        search_entry.ActivatesDefault = true;
        l.MnemonicWidget = search_entry;
        v.PackStart (search_entry, true, true, 0);

        v = new VBox ();
        v.Spacing = 6;
        h.PackStart (v, true, true, 0);

        Button b = new Button ("Search");
        b.CanDefault = true;
        w.Default = b;
        v.PackStart (b, true, true, 0);

        b.Clicked += ButtonClicked;
        w.DeleteEvent += WindowDelete;
        w.ShowAll ();
    }

    public static void Main (string[] args)
    {
        Application.Init ();
        InitGui ();
        Application.Run ();
    }
}

我们需要在编译器命令行中添加一个新的程序集 gnome-sharp。假设您将此程序命名为 three.cs,您将执行以下操作

$ mcs three.cs -pkg:gtk-sharp -pkg:gnome-sharp

现在通过以下方式运行它

$ mono three.exe

第三个也是最后一个程序包含一些新的窗口小部件——按钮、标签和文本条目——但遵循与第二个程序相同的基本结构。如果我们从最后一个盒子向外工作到第一个盒子,即使不看屏幕截图,我们也能很好地了解 UI 元素的布局。

另一个很大的区别是一个新的事件,Clicked。我们定义一个函数并将其添加为事件处理程序

b.Clicked += ButtonClicked;

我们将文本输入窗口小部件 search_entry 设置为 public,因此我们的新事件处理程序可以在用户按下按钮时通过 search_entry.Text 获取搜索条目的内容。

在 ButtonClicked 中,我们获取此文本,构建搜索文本所需的 Wikipedia URL,并使用 Gnome.Url.Show() 函数打开用户首选的 Web 浏览器(全局 GNOME 设置)并将其发送到我们的 URL。

结论

尽管用如此少的代码实现如此多的功能令人印象深刻,但真正的好处是这是一种类似 C 的语言,而不是脚本语言。C# 保留了 C 的大部分功能和性能,并添加了强大的面向对象编程结构。

坦率地说,我是一名 C 程序员。我大部分时间都在破解内核。更高级的语言不是我的菜。事实上,我不是 C++ 或 Java 的粉丝。然而,在花时间使用 C#——从事 Beagle 和其他 C# 项目之后——我印象深刻。C# 是一种优雅的语言,它清楚地表明了一个明显的方面:它设计得非常好。

在 Mono 中,我们拥有一个免费且开放的 C# 编译器、运行时和库系列,并由充满活力的开源社区提供支持。尽管与 GNOME 开发完美结合,但 Mono 对于许多工作来说都是一个明智的工具。

本文资源: /article/8750

Robert Love 是 Novell 的 Ximian Desktop 组的高级内核黑客,《Linux 内核开发》(SAMS 2005)的作者,该书现在已出第二版。他拥有佛罗里达大学的计算机科学和数学学位。Robert 住在马萨诸塞州剑桥市。

加载 Disqus 评论