最近,我正在试用 GTK+ 和它的 Ruby 程序包(binding)。我决定写一个教程介绍一下这个功能。在本文中,我们将会用 gtk3 gem —— 即 GTK+ 的 Ruby 程序包——来创建一个简单的 ToDo 程序。这个程序有点像我们在 Ruby on Rails 上创建的这个程序。
注:本教程的代码可以在 GitHub 上下载。
引用工具箱页面上的一句话,GTK+ 就是:
GTK+,即 GIMP Toolkit(工具箱)的缩写,是一个可以用来创建用户界面的多平台工具箱。它提供了一整套小部件,适用于各种场合,小到一个简单的工具程序,大到一个完整的应用套件。
另外,关于 GTK+ 的起源:
GTK+ 最初是为 GIMP(GNU Image Manipulation Program 的缩写,即 GNU 图像处理程序)开发的,也是在其环境下应用的。它被称为“ GIMP 工具箱”,就是要大家记住这个项目的起源。如今,它更多地以一个简短的名字—— GTK+ 示人,并且被应用更多的场景中,包括 GNU 项目的 GNOME 桌面系统在内。
GTK +版本
确保你已经安装了 GTK +。
我开发教程应用程序的操作系统是 Ubuntu 16.04 ,默认安装了 GTK +(版本:3.18)。
你可以使用以下命令检查设备:
dpkg -l libgtk-3-0
Ruby
你应该在你的系统上安装 ruby 。 我使用 RVM 来管理我的系统上安装的多个 ruby 版本。 如果你也想这样做,你可以在主页上找到安装该工具的说明,以及安装 ruby 版本(a.k.a. rubies)的相关文档页面。
本教程使用 Ruby 2.4.2。 你可以用如下命令检查版本号:ruby --version 或通过 RVM 的 rvm list 。
我们将建立一个应用程序:
它将有一个用户界面(桌面应用程序)
它将允许用户为每个项目设置杂项属性(如优先级)
它将允许用户创建和编辑 ToDo 项目
所有的项目将作为文件保存在用户的主目录中一个名为 .gtk-todo-tutorial 的文件夹中
它将允许用户存档 ToDo 事项
归档的项目应该放在自己的 archived 文件夹中
gtk-todo-tutorial # root directory |-- application |-- ui # everything related to the ui of the application |-- models # our models |-- lib # the directory to host any utilities we might need |-- resources # directory to host the resources of our application gtk-todo # the executable that will start our application
开始创建项目吧!
初始化应用程序
创建一个目录,我们将在其中保存应用程序所需的所有文件。如上面的部分所示,我命名 gtk-todo-tutorial 为我的主目录文件夹。
在那里创建一个名为 gtk-todo 的文件(这是正确的,没有扩展名),并添加以下内容:
#!/usr/bin/env ruby require 'gtk3' app = Gtk::Application.new 'com.iridakos.gtk-todo', :flags_none app.signal_connect :activate do |application| window = Gtk::ApplicationWindow.new(application) window.set_title 'Hello GTK+Ruby!' window.present end puts app.run
这是用来启动应用程序的脚本文件。
注意第一行中的 shebang (#!)。这就是我们如何定义在 UNIX / Linux 操作系统下使用哪个解释器来执行脚本。 这样,我们不必使用 ruby gtk-todo ,而只需要使用脚本名称 gtk-todo 。
现在不要去尝试运行,因为我们没有改变文件的模式。 为此,请在导航到应用程序根目录后,在终端中键入以下命令:
chmod +x ./gtk-todo # make the script executable
现在从控制台执行:
./gtk-todo # execute the script
Ta daaaaaa
注意事项:
我们上面定义的应用程序对象和所有的 GTK + 小部件通常会发出触发事件的信号。一旦应用程序开始运行,例如,它发出一个信号来触发 activate 事件。我们所要做的就是定义这个信号发射时我们想要发生的事情。我们通过使用 signal_connect 实例方法并将其传递给将在给定事件上执行的代码块来完成此操作。我们将在整个教程中做很多工作。
当我们初始化 Gtk :: Application 对象时,我们传递了两个参数:
com.iridakos.gtk-todo:这是我们的应用程序的 ID ,一般来说它应该是一个反向的 DNS 样式标识符。有关其使用情况和最佳做法的更多信息,请点击此处。
:flags_none:这是一个定义应用程序行为的标识。在我们的例子中,我们使用了默认行为。在这里检查所有的标志和他们定义的应用程序的类型。您可以使用 Gio :: ApplicationFlags.constants 中定义的 Ruby 等效标志。例如,不是使用:flags_none,而是使用 Gio :: ApplicationFlags :: FLAGS_NONE
假设我们之前创建的应用程序对象(Gtk :: Application)在 activate 信号触发者在我们想要连接更多信号时有很多工作要做。我们最终会创建一个巨大的 gtk-todo 脚本文件,这将难以读取/维护。(甚至)花费时间重构。
如应用程序结构部分所述,创建一个名为 application 的文件夹及其子文件夹 ui、models 和 lib 。
在 ui 文件夹中,我们将放置与用户界面相关的所有文件。
在 models 文件夹中,我们将放置与模型相关的所有文件。
在 lib 文件夹中,我们将放置所有不属于上述文件夹的其他文件。
我们将为我们的应用程序定义一个新的 Gtk :: Application 类的子类。 在 application / ui / todo 下创建一个名为 application.rb 的文件,其内容如下。
module ToDo class Application < Gtk::Application def initialize super 'com.iridakos.gtk-todo', Gio::ApplicationFlags::FLAGS_NONE signal_connect :activate do |application| window = Gtk::ApplicationWindow.new(application) window.set_title 'Hello GTK+Ruby!' window.present end end end end
并相应地更改 gtk-todo 脚本:
#!/usr/bin/env ruby require 'gtk3' app = ToDo::Application.new puts app.run
更简洁,不是吗? 是的,但它不起作用。 你应该得到像这样的东西:
./gtk-todo:5:in `<main>': uninitialized constant ToDo (NameError)
现在的问题是我们没有要求在 application 文件夹中放置任何 ruby 文件。 如下更改脚本文件并再次执行。
#!/usr/bin/env ruby require 'gtk3' # Require all ruby files in the application folder recursively application_root_path = File.expand_path(__dir__) Dir[File.join(application_root_path, '**', '*.rb')].each { |file| require file } app = ToDo::Application.new puts app.run
这下就没问题了。
在本教程开始我们说过会使用 Glade 设计应用程序的用户界面。 Glade 实际上产生的 XML 文件与相应的元素和属性,反映了我们设计的用户界面。我们需要使用这些文件,这样我们的应用程序就可以得到我们设计的 UI 。
这些文件是与 GResource API 提供了一种包装在一起的一个二进制文件,然后从内部访问他们已经加载的资源,读取文件系统的位置等等,相对于手动来处理有明显的优势。点此了解更多 API 相关。
描述资源文件
首先,我们需要创建一个描述应用程序资源的文件。 创建一个名为 gresources.xml 的文件,并将其直接放置在 resources 文件夹下。
<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/com/iridakos/gtk-todo"> <file preprocess="xml-stripblanks">ui/application_window.ui</file> </gresource> </gresources>
在这个“描述文件”中,实际上说:我们有一个位于 ui 目录下的资源(相对于这个 xml 文件),名字是 main_window.ui 。 在加载此资源之前,请删除空白。 谢谢。 当然,由于我们还没有通过 Glade 创建资源,所以现在还不能工作。 不要担心,事情一件一件来做。
注意:xml-stripblanks 指令将使用 xmllint 命令删除空白。 在 Ubuntu 中,你必须安装 libxml2-utils 软件包来获取它。
建立二进制资源文件
为了生成二进制资源文件,我们将使用另一个名为 glib-compile-resources 的 GLib 库实用程序。 检查是否已经安装了 dpkg -l libglib2.0-bin 。 你应该看到如下内容:
ii libglib2.0-bin 2.48.2-0ubuntu amd64 Programs for the GLib library
如果没有,就要先安装软件包(在 Ubuntu 中使用命令 :sudo apt install libglib2.0-bin )。
让我们建立文件。 我们将在脚本中添加代码,以便每次执行时都会生成资源。 更改 gtk-todo 脚本,如下所示。
#!/usr/bin/env ruby require 'gtk3' require 'fileutils' # Require all ruby files in the application folder recursively application_root_path = File.expand_path(__dir__) Dir[File.join(application_root_path, '**', '*.rb')].each { |file| require file } # Define the source & target files of the glib-compile-resources command resource_xml = File.join(application_root_path, 'resources', 'gresources.xml') resource_bin = File.join(application_root_path, 'gresource.bin') # Build the binary system("glib-compile-resources", "--target", resource_bin, "--sourcedir", File.dirname(resource_xml), resource_xml) at_exit do # Before existing, please remove the binary we produced, thanks. FileUtils.rm_f(resource_bin) end app = ToDo::Application.new puts app.run
并执行它。 这将会在控制台输出如下内容,这很好,我们稍后会修复它:
/.../gtk-todo-tutorial/resources/gresources.xml: Failed to locate 'ui/main_window.ui' in any source directory.
我们做了什么:
为 fileutils 库添加了一个 require 语句,以便我们可以在 at_exit 调用中使用它
定义了 glib-compile-resources 命令的源文件和目标文件
执行了 glib-compile-resources 命令
设置了一个钩子,以便在退出脚本之前(应用程序退出之前)删除二进制文件,方便下次再次构建
评论删除后,数据将无法恢复
评论(7)
引用来自“开心303”的评论
看起来挺好,真正开发产品的时候才发现,好多控件无法满足需求,问题很多。引用来自“Honghe”的评论
能举些例子吗?引用来自“开心303”的评论
看起来挺好,真正开发产品的时候才发现,好多控件无法满足需求,问题很多。