【问题标题】:How to write new widgets implementing MVC with lablgtk2?如何使用lablgtk2编写实现MVC的新小部件?
【发布时间】:2016-03-31 00:40:38
【问题描述】:

我正在为lablgtk2(Gtk+ 的OCaml 绑定)编写一系列新的小部件。其中一些小部件可以编辑或呈现相当复杂的信息,因此我对使用 model-view-controler 或 subject-observer 感兴趣,类似于 GTree 模块中的内容。

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

在纯 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

定义新信号

绑定提供了大量定义新信号的代码示例,因此我们可以为我们的小部件定义新信号,如下面的 sn-p 所示,考虑到没有参数的信号的简单情况:

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-argument 在发射器中的作用仍然需要清除。

定义新模型

模型定义中的棘手部分是它应该从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)' 失败

很明显,这里有问题,很可能是参数列表(上面sn-p中的空列表)太小了。

【问题讨论】:

  • 只是一个愚蠢的问题...您没有忘记调用GtkMain.Main.init 或添加auto-init 来初始化gtk 库?

标签: gtk ocaml lablgtk


【解决方案1】:

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. 触发新信号是通过['a] GUtil.signalcall 方法完成的。
  4. 由于我们不再使用GtkSignal,所以实际上没有问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-14
    • 1970-01-01
    • 2012-10-24
    • 2021-07-11
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多