Creating user's slots

Kakadu edited this page Jan 1, 2016 · 3 revisions

This page is from the ancient times when I was createing bindings to the Qt Widgets. In the most new version of lablqt when we deal with QML this text is irrelevant

To create user object with some slots you should compile Qt's moc-like tool (aka mocml). For quick start using user slots you should do three steps

  1. Create input file for mocml

    Write only slots signatures here. (File format is not yet documented in wiki). You can write something like this.

    let foo: int -> int -> unit
    let goo: qWidget -> qWidget
    
  2. Write slots bodies in UserSlots.ml. Somethis like this:

    let boo x y =
      Printf.printf "%d + %d = %d\n" x y (x+y);
      flush stdout
    let goo w = match w#parentWidget with 
      | None -> assert false
      | Some x -> x
    
  3. Add to Makefile a target for creating stubs for this slots. For example that:

    ./mocml input
    moc UserSlots.h > moc_UserSlots.cpp
    $(GCC) -c moc_UserSlots.cpp
    $(GCC) -c UserSlots.cpp
    $(OCAMLC) $(INCLUDES) -c UserSlots.ml
    $(OCAMLC) $(INCLUDES) -c UserSlots_stubs.ml
    

No you can create UserSlots object and create some connections to your slots.

let user_obj = UserSlots_stubs.create_UserSlots () 
let () = Stub_helpers.connect someobject someobject#some_signal user_obj user_obj#slot_boo

Below you can get more information how it works internally.


Mocml receives input file as argument and generates class with slots according to its content. It will create three files (UserSlots.cpp, UserSlots.h and UserSlots_stubs.ml) describing your object.


The content of cpp file is simple.

#include "UserSlots.h"
extern "C" {
#include "headers.h"
value createUserSlots(value x) {
  CAMLparam1(x);
  CAMLreturn((value)(new UserSlots()));
}
}

Function createUserSlots returns to OCaml new object of UserSlots class.


Let's examine contents of header file.

#include <Qt/QtOpenGL>
extern "C" {
#include "headers.h"
}
#include <QObject>
class UserSlots : public QObject {
Q_OBJECT
public slots:

Moc have created header file for class inherited from QObject and have added some public slots:

  void foo(int x0,int x1) {
    value *closure = caml_named_value("userSlots_foo_int_int_unit");
    value *args = new value[2];
    args[0] = Val_int (x0);
    args[1] = Val_int (x1);
    if (closure==NULL)
      printf("closure not found. crash.\n");
    caml_callbackN(*closure, 2, args);
  }

This slot receives two int arguments and send them to callback function from OCaml.

  QWidget* goo(QWidget* x0) {
    value *closure = caml_named_value("userSlots_goo_qWidget_qWidget");
    value *args = new value[1];
    args[0] = (x0!=0)? Some_val((QWidget*)x0) : Val_none;
    if (closure==NULL)
      printf("closure not found. crash.\n");
    value _ans = caml_callbackN(closure, 1, args);
    return  (QWidget*)_ans;
  }
};

This slot sends object of QWidget class to OCaml, receives new QWidget object from OCaml and returns it.


And the last file UserSlots_stubs store stubs for functions from C++.

open Stub_helpers
open Classes

These two modules are the part of lablqt library. First stores some helper functions for manipulating objects, second contains from Qt's classes

let userSlots_foo_int_int_unit x0 x1 =
  UserSlots.foo x0 x1
let () = Callback.register "userSlots_foo_int_int_unit" userSlots_foo_int_int_unit
let userSlots_goo_qWidget_qWidget x0 =
  UserSlots.goo (new qWidget x0)
let () = Callback.register "userSlots_goo_qWidget_qWidget" userSlots_goo_qWidget_qWidget

You can see wrapper functions which helps with registering callbacks.

class userSlots me = object 
  method handler : [`qobject] obj = me
  method slot_foo = object (self: (<arg0:int; arg1:int; ..>, int->int->unit) #ssslot)
    method name = "foo(int,int)"
    method call = UserSlots.foo
  end
  method slot_goo = object (self: (<arg0:qWidget; ..>, qWidget->qWidget) #ssslot)
    method name = "goo(QWidget*)"
    method call = UserSlots.goo
  end
end

This is OCaml class which stores your slots. You can connect it's slots when connectiong some signals. See wikiarticle about type-safe connection from signals to slots.

external create_UserSlots': unit -> [`qobject] obj = "createUserSlots"
let create_UserSlots () = create_UserSlots' () |> new userSlots

This is simple stub functions for creating UserSlots object.


I want summarize information about source files in list below:

  • input is declarative description of slots and signals. It is created by programmer
  • UserSlots.ml contains slots declarations as like as OCaml values. Also created by programmer
  • UserSlots.h is header file for UserSlots object (generated from input using mocml)
  • UserSlots.cpp C++ source file with function for creating UserSlots object (generated from input using mocml)
  • UserSlots_stubs.ml source file which contains helper function for calling OCaml values declared in UserSlots.ml form C++ (generated from input using mocml)