hsqml-datamodel-0.2.0.2: HsQML (Qt5) data model.

Safe HaskellNone
LanguageHaskell2010

Graphics.QML.DataModel.Tutorial

Contents

Synopsis

Haskell side

Any data type with a single constructor and marshallable fields can be used in a QML data model. For example:

  data Row = Row {foo :: Int, bar :: String, baz :: Double}
    deriving Generic

To use the data type in a model, first you need to declare the necessary instances. All the necessary classes have default implementations using Generic. To further automate the process, there's a Template Haskell macro that declares them all:

 dataModelInstances Row

If you don't want to use TH, you can declare them manually:

 instance QtTable      Row
 instance CountFields  Row
 instance SetupColumns Row

Finally, in your main function, register the data model as a QML type, set up the delegate and its callbacks, and provide a way for QML to access it.

setTVarCallbacks 
  :: QtTable a 
  => DataModel a 
  -> TVar [a]
  -> IO ()
setTVarCallbacks model tv = do
  setRowCountCallback model $      length $ readTVarIO tv
  setDataCallback     model $ i -> (!!i) $ readTVarIO tv

main = do
    registerHaskellModel   

    storage <- newTVarIO [Row 1 "a" 3.0, Row 2 "b" 4.2]
    model <- setupDataModel
    setTVarCallbacks model storage

    skey <- newSignalKey

    toplevel'class <- newClass 
       [ defPropertyConst' "haskellModelDelegate" $ _ -> return (delegate model)
       , defPropertyConst' "self" return 
       , defSignal "updateTable" skey 
       ]
    toplevel'obj <- newObject toplevel'class ()

    runEngineLoop defaultEngineConfig 
      { initialDocument = "path/toyour/main.qml"
      , contextObject = Just $ anyObjRef toplevel'obj
      }

QML side

Using the model in QML is simple as that:

  HaskellModel {
    id: haskellModel;
    delegate: haskellModelDelegate;
  }

  TableView {
    id: hsView;
    model: haskellModel;

    TableViewColumn {
      title: "A";
      role: "baz";
    }

    TableViewColumn {
      title: "B";
      role: "foo";
    }
  }

Record fields become roles, and you can use them in any order any with any names you like. Views other than TableView can be used too, of course.

You will also need to reset the model every time you make changes from the Haskell side. This can be done by connecting the modelReset method to a signal fired from Haskell:

  Connections {
    target: self;
    onUpdateTable: hsView.model.modelReset()  
  }