В cl-gir есть пример, копипастю его сюда:
(defvar *gtk* (gir:ffi "Gtk"))
(defun main ()
(gir:invoke (*gtk* 'init) nil)
(let ((window (gir:invoke (*gtk* "Window" 'new)
(gir:nget *gtk* "WindowType" :toplevel)))
(button (gir:invoke (*gtk* "Button" 'new-with-label) "Hello, world!")))
(gir::g-signal-connect-data (gir::object-this window)
"destroy"
(cffi:foreign-symbol-pointer "gtk_main_quit")
(cffi:null-pointer)
(cffi:null-pointer)
0)
(gir:connect button :clicked
(lambda (button)
(setf (gir:property button 'label) "OK")))
(gir:invoke (window 'add) button)
(gir:invoke (window 'show-all))
(gir:invoke (*gtk* 'main))))
Я вижу тут две привязки к сигналам destroy и clicked. Нельзя ли
destroy также связать через gir:connect? Можно ли получать доступ к
функциям gtk_main_quit через *gtk* и вызвать её?
У меня (gir:nget *gtk* 'main-quit) достаёт вроде что-то, но
(gir:invoke (*gtk* 'main-quit)) как-то непонятно работает.
> Нельзя ли destroy также связать через gir:connect? Можно ли получать доступ к функциям gtk_main_quit через *gtk* и вызвать её?
Можно. Здесь пример такой, чтобы продемонстрировать разные варианты привязки: низкий уровень через g-signal-connect-data и высокий через connect
Должно работать и так:(defvar *gtk* (gir:ffi "Gtk"))(defun main ()
(gir:invoke (*gtk* 'init) nil)
(let ((window (gir:invoke (*gtk* "Window" 'new)
(gir:nget *gtk* "WindowType" :toplevel)))
(button (gir:invoke (*gtk* "Button" 'new-with-label) "Hello, world!")))
(gir:connect window :destroy
(lambda (window)
(gir:invoke (*gtk* 'main-quit))))
(gir:connect button :clicked
(lambda (button)
(setf (gir:property button 'label) "OK")))
(gir:invoke (window 'add) button)
(gir:invoke (window 'show-all))
(gir:invoke (*gtk* 'main)))) Вроде так раньше пробовал - не работало. Сейчас работает. Странно.
Не работало, потому что я пытался закрыть окошко по нажатию кнопки вот так:
(gir:connect button :clicked
(lambda (button)
(declare (ignore button))
(gir:invoke (*gtk* 'main-quit))))
А вот так работает:
(gir:connect button :clicked
(lambda (button)
(declare (ignore button))
(gir:invoke (window 'destroy))))
Пытаюсь банально нарисовать окошко с белым холстом, чтобы на нём
вволю рисовать через cairo (из-за любви к inkscape) и чтобы рисование
и обработка событий управлялось из sbcl. По соображениям
перспективности выбрал cl-gobject-introspection. Опыт в таких делах -
реализация того же самого через rpc к matplotlib. В уровне пониже
опыта никакого.
Прототип упёрся пока на таком коде:
(defun main ()
(gir:invoke (*gtk* 'init) nil)
(let ((window (gir:invoke (*gtk* "Window" 'new)
(gir:nget *gtk* "WindowType" :toplevel)))
(drawing-area (gir:invoke (*gtk* "DrawingArea" 'new))))
(setf (gir:property window 'title) "Hello world!")
(gir:connect window :destroy
(lambda (win)
(declare (ignore win))
(gir:invoke (*gtk* 'main-quit))))
(gir:connect drawing-area :draw
(lambda (drawing-area cairo)))
(gir:invoke (window 'add) drawing-area)
(gir:invoke (window 'show-all))
(gir:invoke (*gtk* 'main))))
Вроде cairo уже доступно, но ничего не удаётся сделать. Либо
вызываемые функции отсутствуют либо вообще ничего не понятно. С
перезапуском перекомпилированной функции обычно получаю ошибки
выделенной памяти - приходится перезапускать sbcl. Привычной
интерактивной разработки никакой.
Вижу не очень понятные функции в пакете gir. Пытался их щупать как-то
так:
(let ((x (gir:invoke (*gtk* "DrawingArea" 'new))))
(gir:object-info-get-properties (slot-value (slot-value x 'class)
'object-info)))
Потом SBCL улетает куда-то далеко - только kill -KILL спасает. Что-то
я не так делаю?
Есть идея взять пример на vala, переделать всё в виде динамичесой
библиотеки. Параллельно вкуривая в CFFI мало-помалу реализовать что
надо. Может быть cl-gobject-introspection пока ещё сыроват и такой
вариант лучше?
Оказывается надо ещё кэш с fasl-ми подчищать - тогда sbcl ругаться начинает нормально:
(let ((x (gir:invoke (*gtk* "DrawingArea" 'new))))
(gir:invoke (*gtk* 'init) nil)
(prog1
(gir:function-info-get-symbol
(first (gir:object-info-get-methods
(slot-value (slot-value x 'class)
'gir::info))))
(gir:invoke (*gtk* 'main-quit))))
В общем пошло дело. Непонятно только почему
(let ((x (gir:invoke (*gtk* "DrawingArea" 'new))))
(gir:invoke (x "get_allocation")))
возвращает nil когда с "get_allocated_width" и "get_allocated_height" всё нормально.
А что с cairo делать. Оказывается это просто указатель.
Методы подобные тем, что из README не работают:
(defun main ()
(gir:invoke (*gtk* 'init) nil)
(let ((window (gir:invoke (*gtk* "Window" 'new)
(gir:nget *gtk* "WindowType" :toplevel)))
(drawing-area (gir:invoke (*gtk* "DrawingArea" 'new))))
(setf (gir:property window 'title) "Hello world!")
(gir:connect window :destroy
(lambda (win)
(declare (ignore win))
(gir:invoke (*gtk* 'main-quit))))
(gir:connect drawing-area :draw
(lambda (drawing-area c)
(declare (ignorable drawing-area
c))
(let ((cairo (gir:invoke (*cairo* "Context" 'new)
c)))
(setf *q* cairo))))
(gir:invoke (window 'add) drawing-area)
(gir:invoke (window 'show-all))
(gir:invoke (*gtk* 'main))))
> А что с cairo делать. Оказывается это просто указатель
В смысле (gir:invoke (*cairo* "Context" 'new) c) возвращает просто указатель?
Это в прошлых примерах. Здесь c - это просто указатель. Чтобы работать в том же стиле надо его перевести
него в объект класса Cairo.Context. Я пользовался примером из README
4.1. Pointer to object
Это в прошлых примерах символ cairo получает значение указателя.В данном случае выпадает при (funcall (gir:nget *cairo* "Context") c) то что funcall нужно что-то
funcallable. А с new: "Bad FFI constructor name"
Попробовал (gir::make-struct (gir:nget *cairo* "Context") c). Вроде объект получается, но толку от него никакого:
нужные методы к нему отсутствуют.
Попробуй (gir::build-object-ptr (gir:nget *cairo* "Context") c).
(let ((cairo (gir::build-object-ptr (gir:nget *cairo* "Context")
c)))
(setf *q*
(gir:nget cairo "arc")))выдаёт:
The value
#S(GIR::STRUCT-CLASS
:INFO #
:SIGNALS (NIL)
:FIELDS-DICT NIL
:CONSTRUCTOR-CACHE #
:METHOD-CACHE #)
is not of type
GIR::OBJECT-CLASS.
> GIR::STRUCT-CLASS
Это всё-таки структура. Тогда (gir::build-struct-ptr (gir:nget *cairo* "Context") c).
gir:nget у структуры привычных методов "line_to", "arc" не находит.
Вообще вроде всё верно ничего особого в gir-файле cairo нет.
Только вот вроде в PyGTK и у vala вроде те же механизмы и у них всё работает.
Такая работа достигается через грубые хаки cairo?
vala не использует gir. Она просто компилируется в Си+GTK. PyGTK -- тоже.
Полный аналог:
https://github.com/Kalimehtar/gtk-cffiА у тебя точно есть cairo-1.0.typelib
И ты его точно подключил?
cairo-1.0.typelib есть.
Подключение в норме:
GIR-TEST> *cairo*
#S(GIR::NAMESPACE
:NAME "cairo"
:VERSION NIL
:INTERFACE-CACHE #)
GIR-TEST> (gir:nget *cairo* "Content")
#
GIR-TEST>
Да. Увидел. https://github.com/GNOME/gobject-introspection/blob/master/gir/cairo-1.0.gir.in вообще почти пустой.
PyGObject использует http://www.cairographics.org/pycairo/
Так там для питона биндинг вообще на Си написан.
В общем: (quicklisp:quickload :cl-cairo2)
Не понял. Функции из cl-cairo2 должны работать с полученным указателем
из сигнала draw?
> Функции из cl-cairo2 должны работать с полученным указателем
из сигнала draw?
Конечно. (make-instance 'cl-cairo2:context :pointer c) и вперёд!
Всё работает! Рисует! Спасибо большое!
Что-то не пойму: а как присоединиться к событию нажатия клавиш, движений и кликов мыши
при том что надо постоянно знать где курсор находится на drawing-area?
http://www.opennet.ru/docs/RUS/gtk_plus/x2447.html