From 937d851ec1763f5a313a5ed31a697636fb9643b2 Mon Sep 17 00:00:00 2001
From: Daniel Vedder <daniel.vedder@idiv.de>
Date: Tue, 24 Oct 2023 17:04:46 +0200
Subject: [PATCH] Reorganised code, splitting it up over more files

---
 src/GUI.jl              | 102 +---------------------
 src/PersefoneDesktop.jl |   3 +-
 src/config.jl           |  35 --------
 src/logic.jl            |  82 +++++++++++++++++
 src/mockup.ui           | 189 ----------------------------------------
 src/splash.qml          |  34 --------
 src/variables.jl        |  70 +++++++++++++++
 7 files changed, 155 insertions(+), 360 deletions(-)
 create mode 100644 src/logic.jl
 delete mode 100644 src/mockup.ui
 delete mode 100644 src/splash.qml
 create mode 100644 src/variables.jl

diff --git a/src/GUI.jl b/src/GUI.jl
index f216a8f..6035690 100644
--- a/src/GUI.jl
+++ b/src/GUI.jl
@@ -3,42 +3,6 @@
 ### This file links the QML UI designs to the underlying model.
 ###
 
-global model = nothing
-global landcovermap = nothing
-global runsimulation = nothing
-global timer = nothing
-
-const configfile = Observable("guiparams.toml")
-const launching = Observable(false)
-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")
-const mapimage = Observable(Figure().scene)
-
-global guiproperties =
-    ["running" => running,
-     "ticks" => ticks,
-     "date" => date,
-     "delay" => delay,
-     "progress" => progress,
-     "runbuttontext" => runbuttontext,
-     "runbuttontip" => runbuttontip]
-
-function newsimulation()
-    global model, landcovermap
-    launching[] = true # trigger the launch screen
-    running[] = false
-    progress[] = 0.0
-    model = initialise(configfile[])
-    landcovermap = rotr90(load(@param(world.landcovermap)))
-    date[] = model.date
-    launching[] = false
-    println("Model initialised.")
-end
 
 function loadsimulation(filename)
     global model
@@ -73,59 +37,6 @@ end
 
 #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
-
-#XXX This could also be done with Channels rather than ResumableFunctions,
-# which would save us one dependency (Channels are inbuilt). However, the
-# ResumableFunctions version seemed to work more smoothly.
-@resumable function createrunfunction()
-    i = round(delay[]*10)
-    while running[]
-        i -= 1
-        if i <= 0
-            nextstep()
-            i = round(delay[]*10)
-        end
-        # to reduce interface lag, always only sleep for 0.1s
-        sleep(0.1)
-        @yield 1
-    end
-end
-
 function togglerunning()
     global runsimulation, timer
     if running[]
@@ -137,7 +48,6 @@ function togglerunning()
         running[] = true
         runbuttontext[] = "||"
         runbuttontip[] = "Pause"
-        runsimulation = createrunfunction()
         QML.start(timer)
     end
 end
@@ -195,16 +105,6 @@ 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
-    #launching[] && #FIXME
-    running[] && runsimulation()
-end
-
 on(launching) do l
     if l
         @emit showSplash()
@@ -246,7 +146,7 @@ function launch()
     qmlfile = joinpath(dirname(@__FILE__), "main.qml")
     loadqml(qmlfile,
             timer = timer,
-            vars = JuliaPropertyMap(guiproperties...,configproperties...),
+            vars = JuliaPropertyMap(guiproperties...),
             render_map_callback = @safe_cfunction(render_map, Cvoid, (Any,)),
             render_plot_callback = @safe_cfunction(render_plot, Cvoid, (Any,)))
     launching[] = true
diff --git a/src/PersefoneDesktop.jl b/src/PersefoneDesktop.jl
index f8c5899..75d8489 100644
--- a/src/PersefoneDesktop.jl
+++ b/src/PersefoneDesktop.jl
@@ -26,6 +26,8 @@ using
     DataFramesMeta,
     ResumableFunctions
 
+include("variables.jl")
+include("logic.jl")
 include("config.jl")
 include("GUI.jl")
 
@@ -33,7 +35,6 @@ precompile(launch, ())
 precompile(render_map, (Any,)) #what's the input type?
 
 export
-    configwindow,
     launch
 
 end
diff --git a/src/config.jl b/src/config.jl
index 89c5b21..33c17a7 100644
--- a/src/config.jl
+++ b/src/config.jl
@@ -3,41 +3,6 @@
 ### This file manages the backend of the setup window.
 ###
 
-const userconfigfile = Observable("userconfig.toml")
-
-const outdir = Observable("persefone_output")
-const csvoutput = Observable(false)
-const loglevel = Observable("warn")
-const processors = Observable(1)
-const seed = Observable(2)
-const startdateday = Observable(1)
-const startdatemonth = Observable(1)
-const startdateyear = Observable(2022)
-const enddateday = Observable(31)
-const enddatemonth = Observable(12)
-const enddateyear = Observable(2022)
-const region = Observable("Jena") #XXX alternatives not yet implemented
-#const farmmodel = Observable("FieldManager") #XXX not yet implemented
-#const targetspecies = Observable() #TODO not sure how to configure a list?
-#const insectmodel = Observable() #TODO not sure how to configure a list?
-const cropmodel = Observable("ALMaSS") #XXX alternatives not yet implemented
-
-global configproperties = 
-    ["userconfigfile" => userconfigfile,
-     "outdir" => outdir,
-     "csvoutput" => csvoutput,
-     "loglevel" => loglevel,
-     "processors" => processors,
-     "seed" => seed,
-     "startdateday" => startdateday,
-     "startdatemonth" => startdatemonth,
-     "startdateyear" => startdateyear,
-     "enddateday" => enddateday,
-     "enddatemonth" => enddatemonth,
-     "enddateyear" => enddateyear,
-     "region" => region,
-     "cropmodel" => cropmodel]
-
 function writeconfig()
     println("Wrote config file.")
     
diff --git a/src/logic.jl b/src/logic.jl
new file mode 100644
index 0000000..3ae7165
--- /dev/null
+++ b/src/logic.jl
@@ -0,0 +1,82 @@
+### PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems
+###
+### This file manages the model initialisation and run functions.
+###
+
+
+function newsimulation()
+    global model, landcovermap
+    launching[] = true # trigger the launch screen
+    running[] = false
+    progress[] = 0.0
+    model = initialise(configfile[])
+    landcovermap = rotr90(load(@param(world.landcovermap)))
+    date[] = model.date
+    launching[] = false
+    println("Model initialised.")
+end
+
+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
+
+#XXX This could also be done with Channels rather than ResumableFunctions,
+# which would save us one dependency (Channels are inbuilt). However, the
+# ResumableFunctions version seemed to work more smoothly.
+@resumable function createrunfunction()
+    i = round(delay[]*10)
+    while running[]
+        i -= 1
+        if i <= 0
+            nextstep()
+            i = round(delay[]*10)
+        end
+        # to reduce interface lag, always only sleep for 0.1s
+        sleep(0.1)
+        @yield 1
+    end
+end
+
+
+on(running) do r
+    ticks = 0 #prevent overflows
+    runsimulation = createrunfunction()
+    r ? println("Simulation started.") : println("Simulation stopped.") #XXX DEBUG
+end
+
+on(ticks) do t
+    #launching[] && #FIXME
+    running[] && runsimulation()
+end
diff --git a/src/mockup.ui b/src/mockup.ui
deleted file mode 100644
index 19c710a..0000000
--- a/src/mockup.ui
+++ /dev/null
@@ -1,189 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>1067</width>
-    <height>614</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Persefone Desktop</string>
-  </property>
-  <property name="windowIcon">
-   <iconset>
-    <normaloff>../eacf50ed/favicon.png</normaloff>../eacf50ed/favicon.png</iconset>
-  </property>
-  <property name="toolTipDuration">
-   <number>-1</number>
-  </property>
-  <widget class="QWidget" name="centralwidget">
-   <layout class="QVBoxLayout" name="verticalLayout">
-    <item>
-     <layout class="QHBoxLayout" name="horizontalLayout">
-      <item>
-       <widget class="QGraphicsView" name="graphicsView"/>
-      </item>
-      <item>
-       <layout class="QVBoxLayout" name="verticalLayout_4">
-        <property name="leftMargin">
-         <number>5</number>
-        </property>
-        <item>
-         <widget class="QCalendarWidget" name="calendarWidget"/>
-        </item>
-        <item>
-         <widget class="QGraphicsView" name="graphicsView_2"/>
-        </item>
-       </layout>
-      </item>
-     </layout>
-    </item>
-    <item>
-     <layout class="QHBoxLayout" name="horizontalLayout_2">
-      <item>
-       <widget class="QPushButton" name="pushButton_3">
-        <property name="toolTip">
-         <string>Pause</string>
-        </property>
-        <property name="text">
-         <string>||</string>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <widget class="QPushButton" name="pushButton">
-        <property name="toolTip">
-         <string>Step</string>
-        </property>
-        <property name="text">
-         <string>&gt;</string>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <widget class="QPushButton" name="pushButton_2">
-        <property name="toolTip">
-         <string>Run</string>
-        </property>
-        <property name="text">
-         <string>&gt;&gt;</string>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <widget class="QProgressBar" name="progressBar">
-        <property name="value">
-         <number>24</number>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <widget class="QSlider" name="horizontalSlider">
-        <property name="sizePolicy">
-         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-          <horstretch>0</horstretch>
-          <verstretch>0</verstretch>
-         </sizepolicy>
-        </property>
-        <property name="minimumSize">
-         <size>
-          <width>200</width>
-          <height>0</height>
-         </size>
-        </property>
-        <property name="toolTip">
-         <string>Delay between updates</string>
-        </property>
-        <property name="maximum">
-         <number>10</number>
-        </property>
-        <property name="value">
-         <number>5</number>
-        </property>
-        <property name="orientation">
-         <enum>Qt::Horizontal</enum>
-        </property>
-        <property name="tickPosition">
-         <enum>QSlider::TicksBelow</enum>
-        </property>
-       </widget>
-      </item>
-     </layout>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QMenuBar" name="menubar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>1067</width>
-     <height>25</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menu_Menu">
-    <property name="title">
-     <string>&amp;Menu</string>
-    </property>
-    <addaction name="separator"/>
-    <addaction name="action_New_run"/>
-    <addaction name="actionLoad_saved_state"/>
-    <addaction name="actionSave_current_state"/>
-    <addaction name="separator"/>
-    <addaction name="action_Quit"/>
-   </widget>
-   <widget class="QMenu" name="menu_Help">
-    <property name="title">
-     <string>&amp;Help</string>
-    </property>
-    <addaction name="actionHelp"/>
-    <addaction name="actionWebsite"/>
-    <addaction name="actionAbout"/>
-   </widget>
-   <addaction name="menu_Menu"/>
-   <addaction name="menu_Help"/>
-  </widget>
-  <widget class="QStatusBar" name="statusbar"/>
-  <action name="actionHelp">
-   <property name="text">
-    <string>Documentation</string>
-   </property>
-  </action>
-  <action name="actionAbout">
-   <property name="text">
-    <string>About</string>
-   </property>
-  </action>
-  <action name="action_New_run">
-   <property name="text">
-    <string>&amp;New run</string>
-   </property>
-  </action>
-  <action name="action_Quit">
-   <property name="text">
-    <string>&amp;Quit</string>
-   </property>
-  </action>
-  <action name="actionSave_current_state">
-   <property name="text">
-    <string>&amp;Save current state</string>
-   </property>
-  </action>
-  <action name="actionLoad_saved_state">
-   <property name="text">
-    <string>&amp;Load saved state</string>
-   </property>
-  </action>
-  <action name="actionWebsite">
-   <property name="text">
-    <string>Website</string>
-   </property>
-  </action>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/splash.qml b/src/splash.qml
deleted file mode 100644
index 4108ee5..0000000
--- a/src/splash.qml
+++ /dev/null
@@ -1,34 +0,0 @@
-/// PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems
-///
-/// This file creates the splash screen. Load it using a QQuickView.
-///
-
-import QtQuick
-import QtQuick.Controls
-import org.julialang
-
-Popup {
-	id: splashPopupExt
-	parent: Overlay.overlay
-
-	width: 600
-	height: 250
-
-	x: Math.round((parent.width - width) / 2)
-    y: Math.round((parent.height - height) / 2)
-
-	Rectangle {
-		anchors.fill: parent
-		
-		Image {
-			source: "persefonejl_logo_v3_splash.png"
-			fillMode: Image.Stretch
-		}
-		Timer {
-			// Show the splash screen while the model object is initialised
-			//XXX is 15s a good time here?
-			interval: 15000; running: true; repeat: false
-			onTriggered: parent.Window.window.close()
-		}
-	}
-}
diff --git a/src/variables.jl b/src/variables.jl
new file mode 100644
index 0000000..b2a6b85
--- /dev/null
+++ b/src/variables.jl
@@ -0,0 +1,70 @@
+### PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems
+###
+### This file contains all globals and Observables.
+###
+
+
+## GUI parameters
+
+global model = nothing
+global landcovermap = nothing
+global runsimulation = nothing
+global timer = nothing
+
+const configfile = Observable("guiparams.toml")
+const launching = Observable(false)
+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")
+const mapimage = Observable(Figure().scene)
+
+
+## configuration parameters
+
+const userconfigfile = Observable("userconfig.toml")
+const outdir = Observable("persefone_output")
+const csvoutput = Observable(false)
+const loglevel = Observable("warn")
+const processors = Observable(1)
+const seed = Observable(2)
+const startdateday = Observable(1)
+const startdatemonth = Observable(1)
+const startdateyear = Observable(2022)
+const enddateday = Observable(31)
+const enddatemonth = Observable(12)
+const enddateyear = Observable(2022)
+const region = Observable("Jena") #XXX alternatives not yet implemented
+#const farmmodel = Observable("FieldManager") #XXX not yet implemented
+#const targetspecies = Observable() #TODO not sure how to configure a list?
+#const insectmodel = Observable() #TODO not sure how to configure a list?
+const cropmodel = Observable("ALMaSS") #XXX alternatives not yet implemented
+
+
+## the property list passed to QML
+
+global guiproperties =
+    ["running" => running,
+     "ticks" => ticks,
+     "date" => date,
+     "delay" => delay,
+     "progress" => progress,
+     "runbuttontext" => runbuttontext,
+     "runbuttontip" => runbuttontip,
+     "userconfigfile" => userconfigfile,
+     "outdir" => outdir,
+     "csvoutput" => csvoutput,
+     "loglevel" => loglevel,
+     "processors" => processors,
+     "seed" => seed,
+     "startdateday" => startdateday,
+     "startdatemonth" => startdatemonth,
+     "startdateyear" => startdateyear,
+     "enddateday" => enddateday,
+     "enddatemonth" => enddatemonth,
+     "enddateyear" => enddateyear,
+     "region" => region,
+     "cropmodel" => cropmodel]
-- 
GitLab