0

我正在为 lablgtk2(Gtk+ 的 OCaml 绑定)编写一系列新的小部件。GTree其中一些小部件可以编辑或呈现相当复杂的信息,因此我对使用模型视图控制器或主题观察器感兴趣,类似于在模块中可以找到的。

这个模块定义了一个GTree.model和一个GTree.view类,每个都有可以连接的信号,并且一个GTree.model可以附加到一个或多个GTree.view's。

在纯 OCaml 中模仿这种组织并不是那么简单,因为库中可用的代码是 C 库的绑定。我需要完成以下步骤:

  1. 定义新的小部件
  2. 定义新信号
  3. 触发这些新信号
  4. 定义新模型

我可以通过 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)' 失败

显然,这里有些可疑之处,很可能是参数列表(上面代码片段中的空列表)太小了。

4

1 回答 1

0

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 信号添加到任意对象;只是继承 fromml_signals代替widget_signalsand 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 点:

  1. 这可以
  2. 用于GUtil定义新信号而不是GtkSignal
  3. 触发新信号是通过 的call方法完成的['a] GUtil.signal
  4. 由于我们不再使用GtkSignal,实际上没有问题。
于 2015-01-29T09:52:42.437 回答