Как написать новые виджеты, реализующие MVC с lablgtk2?
Я пишу семейство новых виджетов для lablgtk2, привязки OCaml для Gtk+. Некоторые из этих виджетов могут редактировать или представлять довольно сложную информацию, поэтому мне интересно использовать модель-представление-контроллер или субъект-наблюдатель, аналогично тому, что можно найти в GTree
модуль.
Этот модуль определяет GTree.model
и GTree.view
класс, каждый из которых имеет сигналы, которые могут быть подключены к, и GTree.model
может быть прикреплен к одному или нескольким GTree.view
"S.
Подражать этой организации в чистом OCaml не так тривиально, потому что код, доступный в библиотеке, является привязкой к C-библиотеке. Мне нужно пройти следующие шаги:
- Определение новых виджетов
- Определение новых сигналов
- Запуск этих новых сигналов
- Определение новых моделей
Я мог пройти через 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 выше:
- Это отлично
- использование
GUtil
определять новые сигналы вместоGtkSignal
- Запуск новых сигналов осуществляется
call
метод['a] GUtil.signal
, - Так как мы не используем
GtkSignal
больше нет проблем.