Safe Haskell | None |
---|---|
Language | Haskell2010 |
Graphics.QML.DataModel.Tutorial
Contents
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() }