如何使用 lablgtk2 编写实现 MVC 的新小部件?
How to write new widgets implementing MVC with lablgtk2?
我正在为 lablgtk2(Gtk+ 的 OCaml 绑定)编写一系列新的小部件。其中一些小部件可以编辑或呈现相当复杂的信息,因此我对使用模型-视图-控制器或主题-观察器很感兴趣,类似于 GTree
模块中的内容。
这个模块定义了一个GTree.model
和一个GTree.view
class,每个都有可以连接的信号,一个GTree.model
可以连接一个或多个GTree.view
的。
在纯 OCaml 中模仿这个组织并不是那么简单,因为库中可用的代码是 C 库的绑定。我需要完成以下步骤:
- 定义新的小部件
- 定义新信号
- 触发这些新信号
- 定义新模型
我可以完成 1 和 2,但我不确定如何完成 3 和 4。如何正确完成这些?
定义新的小部件
新widgets的定义本身没有问题。新的小部件通常是 Gnome canvas 的专用版本或复合版本。在前一种情况下,我们的新小部件可以作为 GObj.widget 从 Gnome canvas 继承,在后一种情况下,我们可以使用容器提供的 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 端发出的。这两个参数似乎与信号参数的准备有关,具体化为 '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: assertion 'GTK_IS_OBJECT (object)' failed
显然,这里有些可疑,很可能是参数列表(上面代码段中的空列表)太小了。
LablGTK 为 Gtk 信号机制提供了一个很好的接口,这使我们可以在不修改 GtkSignal
和编组函数的情况下使用它。此接口由 GUtil
提供,并有详细的文档记录。
如何使用 GUtil,如模块文档中所述
将 ML 信号添加到 LablGTK 对象:
{[
class mywidget_signals obj ~mysignal1 ~mysignal2 = object
inherit somewidget_signals obj
inherit add_ml_signals obj [mysignal1#disconnect; mysignal2#disconnect]
method mysignal1 = mysignal1#connect ~after
method mysignal2 = mysignal2#connect ~after
end
class mywidget obj = object (self)
inherit somewidget obj
val mysignal1 = new signal obj
val mysignal2 = new signal obj
method connect = new mywidget_signals obj ~mysignal1 ~mysignal2
method call1 = mysignal1#call
method call2 = mysignal2#call
end
]}
您还可以将ML信号添加到任意对象;只是继承 ml_signals
代替 widget_signals
和 add_ml_signals
.
{[
class mysignals ~mysignal1 ~mysignal2 = object
inherit ml_signals [mysignal1#disconnect; mysignal2#disconnect]
method mysignal1 = mysignal1#connect ~after
method mysignal2 = mysignal2#connect ~after
end
]}
现在很容易解决上面的第 1、2、3 和 4 点:
- 这很好
- 使用
GUtil
来定义新信号而不是 GtkSignal
- 触发新信号是通过
['a] GUtil.signal
的 call
方法完成的。
- 既然不用
GtkSignal
了,其实也没什么问题。
我正在为 lablgtk2(Gtk+ 的 OCaml 绑定)编写一系列新的小部件。其中一些小部件可以编辑或呈现相当复杂的信息,因此我对使用模型-视图-控制器或主题-观察器很感兴趣,类似于 GTree
模块中的内容。
这个模块定义了一个GTree.model
和一个GTree.view
class,每个都有可以连接的信号,一个GTree.model
可以连接一个或多个GTree.view
的。
在纯 OCaml 中模仿这个组织并不是那么简单,因为库中可用的代码是 C 库的绑定。我需要完成以下步骤:
- 定义新的小部件
- 定义新信号
- 触发这些新信号
- 定义新模型
我可以完成 1 和 2,但我不确定如何完成 3 和 4。如何正确完成这些?
定义新的小部件
新widgets的定义本身没有问题。新的小部件通常是 Gnome canvas 的专用版本或复合版本。在前一种情况下,我们的新小部件可以作为 GObj.widget 从 Gnome canvas 继承,在后一种情况下,我们可以使用容器提供的 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 端发出的。这两个参数似乎与信号参数的准备有关,具体化为 '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: assertion 'GTK_IS_OBJECT (object)' failed
显然,这里有些可疑,很可能是参数列表(上面代码段中的空列表)太小了。
LablGTK 为 Gtk 信号机制提供了一个很好的接口,这使我们可以在不修改 GtkSignal
和编组函数的情况下使用它。此接口由 GUtil
提供,并有详细的文档记录。
如何使用 GUtil,如模块文档中所述
将 ML 信号添加到 LablGTK 对象:
{[
class mywidget_signals obj ~mysignal1 ~mysignal2 = object
inherit somewidget_signals obj
inherit add_ml_signals obj [mysignal1#disconnect; mysignal2#disconnect]
method mysignal1 = mysignal1#connect ~after
method mysignal2 = mysignal2#connect ~after
end
class mywidget obj = object (self)
inherit somewidget obj
val mysignal1 = new signal obj
val mysignal2 = new signal obj
method connect = new mywidget_signals obj ~mysignal1 ~mysignal2
method call1 = mysignal1#call
method call2 = mysignal2#call
end
]}
您还可以将ML信号添加到任意对象;只是继承 ml_signals
代替 widget_signals
和 add_ml_signals
.
{[
class mysignals ~mysignal1 ~mysignal2 = object
inherit ml_signals [mysignal1#disconnect; mysignal2#disconnect]
method mysignal1 = mysignal1#connect ~after
method mysignal2 = mysignal2#connect ~after
end
]}
现在很容易解决上面的第 1、2、3 和 4 点:
- 这很好
- 使用
GUtil
来定义新信号而不是GtkSignal
- 触发新信号是通过
['a] GUtil.signal
的call
方法完成的。 - 既然不用
GtkSignal
了,其实也没什么问题。