### PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems ### ### This file links the QML UI designs to the underlying model. ### ## For references, see: ## - https://github.com/barche/QML.jl ## - https://github.com/barche/QmlJuliaExamples ## - https://doc.qt.io/qt-6/qtquick-index.html #TODO define standard config file for Persefone Desktop global model = nothing global landcovermap = nothing global runsimulation = nothing global timer = nothing const running = Observable(false) const ticks = Observable(0) const date = Observable(today()) const progress = Observable(0.0) const delay = Observable(0.5) const runbuttontext = Observable(">>") const runbuttontip = Observable("Run") function newsimulation() global model, landcovermap running[] = false progress[] = 0.0 model = initialise() #TODO configure landcovermap = load(@param(world.landcovermap)) date[] = model.date println("Model initialised.") end #TODO loadsimulation() #TODO saveoutput() function nextstep() global model if date[] >= @param(core.enddate) running[] = false runbuttontext[] = ">>" runbuttontip[] = "Run" return elseif date[] < model.date # If we've "scrolled back" in time, step forward again without # having to resimulate (see `previousstep()`) date[] += Day(1) else # If we're already displaying the newest model update, we need to # keep simulating stepsimulation!(model) date[] = model.date end progress[] = (date[]-@param(core.startdate)) / (@param(core.enddate)-@param(core.startdate)) @emit updateMakie() println("Updated model: $(date[]-Day(1))") end function previousstep() # Since we store all model output in a dataframe, we can simply # "scroll back" in time and visualise the state of the model at # a previous date global model date[] <= @param(core.startdate) && return date[] -= Day(1) progress[] = (date[]-@param(core.startdate)) / (@param(core.enddate)-@param(core.startdate)) @emit updateMakie() println("Backtracked model: $(date[]-Day(1))") end @resumable function createrunfunction() while running[] nextstep() #XXX to reduce interface lag, always only sleep for 0.1s? sleep(delay[]) @yield 1 end end function togglerunning() global runsimulation, timer if running[] running[] = false QML.stop(timer) runbuttontext[] = ">>" runbuttontip[] = "Run" else running[] = true QML.start(timer) runbuttontext[] = "||" runbuttontip[] = "Pause" runsimulation = createrunfunction() end end function render_map(screen) global model, landcovermap println("Updating map") figure = visualisemap(model, date[], landcovermap) display(screen, figure.scene) end function render_plot(screen) global model println("Updating plot") figure = populationtrends(model) display(screen, figure.scene) end on(delay) do d println("Delay is now $(round(d, digits=1)) seconds.") #XXX DEBUG end on(running) do r ticks = 0 #prevent overflows r ? println("Simulation started.") : println("Simulation stopped.") #XXX DEBUG end on(ticks) do t running[] && runsimulation() end function splashscreen() qmlfile = joinpath(dirname(@__FILE__), "splash.qml") quick_view = init_qquickview() set_source(quick_view, QUrlFromLocalFile(qmlfile)) QML.show(quick_view) #XXX exec_async() is currently still broken, but should be fixed soon # (https://github.com/JuliaGraphics/QML.jl/issues/174) @async exec() end """ launch() The main function that creates the application. """ function launch() global model, timer #splashscreen() newsimulation() @qmlfunction newsimulation @qmlfunction nextstep @qmlfunction previousstep @qmlfunction togglerunning timer = QTimer() qmlfile = joinpath(dirname(@__FILE__), "main.qml") loadqml(qmlfile, timer = timer, vars = JuliaPropertyMap("running" => running, "ticks" => ticks, "date" => date, "delay" => delay, "progress" => progress, "runbuttontext" => runbuttontext, "runbuttontip" => runbuttontip), render_map_callback = @safe_cfunction(render_map, Cvoid, (Any,)), render_plot_callback = @safe_cfunction(render_plot, Cvoid, (Any,))) @emit updateMakie() println("Launched Persefone Desktop.") exec() end