我正在为 lablgtk2(Gtk+ 的 OCaml 绑定)编写一系列新的小部件。GTree
其中一些小部件可以编辑或呈现相当复杂的信息,因此我对使用模型视图控制器或主题观察器感兴趣,类似于在模块中可以找到的。
这个模块定义了一个GTree.model
和一个GTree.view
类,每个都有可以连接的信号,并且一个GTree.model
可以附加到一个或多个GTree.view
's。
在纯 OCaml 中模仿这种组织并不是那么简单,因为库中可用的代码是 C 库的绑定。我需要完成以下步骤:
- 定义新的小部件
- 定义新信号
- 触发这些新信号
- 定义新模型
我可以通过 1 和 2,但我不确定如何执行 3 和 4。如何正确执行这些操作?
定义新的小部件
新小部件的定义本身没有问题。新的小部件通常是 Gnome 画布的专用版本或组合。在前一种情况下,我们的新小部件可以作为 GObj.widget 从 Gnome 画布继承,而在后一种情况下,我们可以使用容器提供的 GObj.widget 来保存组合。这通常看起来像
class view () =
let vbox = GPack.vbox () in
…
object(self)
inherit GObj.widget vbox#as_widget
…
end
定义新信号
绑定为定义新信号的代码提供了大量示例,因此我们可以为我们的小部件定义新信号,如下面的片段所示,考虑到没有参数的信号的简单情况:
open GtkSignal
module Event =
struct
let plop : ([>`widget], unit -> unit) t = {
name = "plop_event";
classe = `widget;
marshaller = marshal_unit;
}
let fizz : ([>`widget], unit -> unit) t = {
name = "fizz_event";
classe = `widget;
marshaller = marshal_unit;
}
end
class pill_signals obj =
object (self)
inherit ['a] GObj.gobject_signals (obj :> Gtk.widget Gobject.obj)
method plop = self#connect Event.plop
method fizz = self#connect Event.fizz
end
通过这些定义,我们的view
小部件可以通过定义适当的connect
方法来公开这些信号:
method connect =
new pill_signals obj
触发新信号
该函数似乎GtkSignal.emit
用于向对象发出信号,触发注册的回调。这起到以下签名的作用:
val emit :
'a Gobject.obj ->
sgn:('a, 'b) GtkSignal.t ->
emitter:(cont:('c Gobject.data_set array -> 'd) -> 'b) ->
conv:(Gobject.g_value -> 'd) -> 'b
前两个参数是不言自明的,但还不清楚剩下的两个参数是什么。不幸的是,lablgtk 源代码中没有使用示例,因为信号是从代码的 C 端发出的。这两个参数似乎与信号参数的准备有关,具体化为 a'c Gobject.data_set array
以及使用标记为 的参数检索产生的值~conv
。尽管如此, -~cont
参数在发射器中的作用仍然需要清除。
定义新模型
模型定义中的棘手部分是它应该继承自GObj.object
以便能够发送接收信号。不幸的是,没有允许直接定义最小 Gtk+ 对象的函数。我朝这个方向走的最远的是
module Model =
struct
let create () =
GtkObject.make ~classe:"GObject" []
end
let model () =
new model (Model.create ())
调用该函数model
来实例化相应的对象会产生以下消息:
Gtk-CRITICAL **: IA__gtk_object_sink: 断言 'GTK_IS_OBJECT (object)' 失败
显然,这里有些可疑之处,很可能是参数列表(上面代码片段中的空列表)太小了。