Как написать новые виджеты, реализующие MVC с lablgtk2?

Я пишу семейство новых виджетов для lablgtk2, привязки OCaml для Gtk+. Некоторые из этих виджетов могут редактировать или представлять довольно сложную информацию, поэтому мне интересно использовать модель-представление-контроллер или субъект-наблюдатель, аналогично тому, что можно найти в GTree модуль.

Этот модуль определяет GTree.model и GTree.view класс, каждый из которых имеет сигналы, которые могут быть подключены к, и GTree.model может быть прикреплен к одному или нескольким GTree.view"S.

Подражать этой организации в чистом OCaml не так тривиально, потому что код, доступный в библиотеке, является привязкой к C-библиотеке. Мне нужно пройти следующие шаги:

  1. Определение новых виджетов
  2. Определение новых сигналов
  3. Запуск этих новых сигналов
  4. Определение новых моделей

Я мог пройти через 1 и 2, но я не уверен, как сделать 3 и 4. Как это сделать правильно?

Определение новых виджетов

Само определение новых виджетов не является проблематичным. Новый виджет обычно представляет собой специализированную версию холста Gnome или составной. В первом случае наш новый виджет может наследоваться от холста Gnome как GObj.widget, а во втором случае мы можем использовать 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 ответ

Решение

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 выше:

  1. Это отлично
  2. использование GUtil определять новые сигналы вместо GtkSignal
  3. Запуск новых сигналов осуществляется call метод ['a] GUtil.signal,
  4. Так как мы не используем GtkSignal больше нет проблем.
Другие вопросы по тегам