如何使用lablgtk2编写实现MVC的新小部件?

时间:2015-01-25 21:02:02

标签: gtk ocaml lablgtk

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

此模块定义了GTree.modelGTree.view类,每个类都有可以连接的信号,GTree.model可以附加到一个或多个GTree.view'第

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

  1. 定义新的小部件
  2. 定义新信号
  3. 触发这些新信号
  4. 定义新模型
  5. 我可以通过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端发出的。这两个参数似乎与信号参数的准备有关,具体化为'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(对象)'失败

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

1 个答案:

答案 0 :(得分: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信号添加到任意对象;只需继承ml_signals代替widget_signalsadd_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的{​​{1}}方法完成的。
  4. 由于我们不再使用['a] GUtil.signal,实际上没有问题。