Something went wrong on our end
-
Marco Matthies authoredMarco Matthies authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
main.qml 11.36 KiB
import QtQuick 6.2
import QtQuick.Controls 6.2
import QtQuick.Dialogs 6.2
import QtQuick.Layouts 6.2
ApplicationWindow {
id: mainWindow
title: "Persefone.jl GUI"
width: 1024
height: 768
visibility: "Maximized"
visible: true
// these are the coordinates of the map image
property var mapCoords: ({ x: 0, y: 0, width: 0, height: 0 })
// calculate the visible coordinates of an Image
function calculateVisibleGeometry(img) {
var containerWidth = img.width;
var containerHeight = img.height;
var imgRatio = img.sourceSize.width / img.sourceSize.height;
var containerRatio = containerWidth / containerHeight;
var visibleWidth, visibleHeight, visibleX, visibleY;
if (imgRatio > containerRatio) {
visibleWidth = containerWidth;
visibleHeight = containerWidth / imgRatio;
visibleX = 0;
visibleY = (containerHeight - visibleHeight) / 2;
} else {
visibleHeight = containerHeight;
visibleWidth = containerHeight * imgRatio;
visibleX = (containerWidth - visibleWidth) / 2;
visibleY = 0;
}
return {x: visibleX, y: visibleY, width: visibleWidth, height: visibleHeight};
}
menuBar: MenuBar {
Menu {
title: "&Simulation"
// Action {
// text: "&New Simulation"
// onTriggered: { vars.launching = true } //TODO select config file
// }
// Action {
// text: "&Configure Simulation"
// onTriggered: { Julia.configwindow() }
// }
// Action {
// text: "&Load Saved State"
// onTriggered: { loadFileChooser.open() }
// }
// Action {
// text: "&Save Current State"
// onTriggered: { saveFileChooser.open() }
// }
MenuSeparator { }
Action {
text: "&Quit"
onTriggered: { mainWindow.close() }
}
}
//Menu {
//title: "&Data"
// Action {
// text: "Show &Population Graph"
// onTriggered: { populationGraph.visible = true }
// }
// Action {
// text: "Save &Simulation Output"
// onTriggered: { Julia.saveoutput() }
// }
//}
Menu {
title: "&Help"
Action {
text: "&Documentation"
onTriggered: { Qt.openUrlExternally("https://persefone-model.eu/documentation") }
}
Action {
text: "&Website"
onTriggered: { Qt.openUrlExternally("https://persefone-model.eu/") }
}
// Action {
// text: "&About"
// onTriggered: { aboutDialog.open() }
// }
}
}
ColumnLayout {
anchors.fill: parent
//anchors.horizontalCenter: parent.horizontalCenter
Image {
id: mapImage
source: mapImagePath
Layout.fillWidth: true
Layout.fillHeight: true
height: parent.height - 50 // Leave some space for the row with play/pause
//anchors.fill: parent
//fillMode: Image.PreserveAspectFit
//anchors.top: parent.top
//anchors.bottom: buttonRow.top
//anchors.horizontalCenter: parent.horizontalCenter
fillMode: Image.PreserveAspectFit
onStatusChanged: {
if (status == Image.Error)
console.log("Error loading image: ", source);
if (status == Image.Ready) {
mapCoords = calculateVisibleGeometry(mapImage);
//console.log("Image loaded successfully.");
}
}
Canvas {
id: canvas
//anchors.fill: parent
//height: parent.height
//width: parent.width
// anchors.horizontalCenter: parent.horizontalCenter
width: mapImage.width
height: mapImage.height
x: mapImage.x
y: mapImage.y
z: 1
onPaint: {
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Adjust based on the image's scaling and aspect ratio
// var imageScaleX = mapImage.width; // / mapImage.width;
// var imageScaleY = mapImage.height; // / mapImage.height;
// Define circles (example positions and radii)
var circles = [
{ x: 0.4, y: 0.5, radius: 5 },
{ x: 0.8, y: 0.3, radius: 10 },
{ x: 1.0, y: 0.5, radius: 20 }
];
for (var i = 0; i < circles.length; i++) {
var circle = circles[i];
var color = colorList[i % colorList.length]; // Use colors from colorList
ctx.fillStyle = Qt.rgba(color.r / 255, color.g / 255, color.b / 255, 1);
ctx.beginPath();
ctx.arc(mapCoords.x + circle.x * mapCoords.width,
mapCoords.y + circle.y * mapCoords.height,
circle.radius, 0, 2 * Math.PI);
ctx.fill();
}
}
Timer {
id: moveTimer
interval: 100 // TODO: what is the unit, maybe milliseconds?
repeat: true
onTriggered: {
// TODO: circle movement is random for now, should come
// from simulation
for (var i = 0; i < parent.circles.length; i++) {
parent.circles[i].x += 2 * Math.random() - 1;
parent.circles[i].y += 2 * Math.random() - 1;
}
parent.requestPaint();
}
}
}
}
RowLayout {
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true
height: 50
//anchors.bottom: parent.bottom
//anchors.horizontalCenter: parent.horizontalCenter
Button {
text: "Play"
onClicked: moveTimer.start()
}
Button {
text: "Stop"
onClicked: moveTimer.stop()
}
}
}
// Redraw when the window is resized
Component.onCompleted: canvas.requestPaint()
onWidthChanged: canvas.requestPaint()
onHeightChanged: canvas.requestPaint()
}
// visualise the model map and the locations of animals
// MakieViewport {
// id: mapviewport
// anchors.fill: parent
// renderFunction: render_map_callback
//
// // the main control bar, with pause/step/run buttons, the progress
// // bar and a speed slider
// footer: ToolBar {
// RowLayout {
// //TODO change button texts to icons
// // (https://doc.qt.io/qt-6/qtquickcontrols-icons.html)
// id: controlBar
// anchors.fill: parent
// Layout.alignment: Qt.AlignVCenter
// Layout.fillWidth: true
// // anchors.topMargin: 5 //FIXME
// // anchors.bottomMargin: 5
// Button {
// id: backButton
// text: "<"
// ToolTip.text: "Back"
// ToolTip.visible: hovered
// onClicked: { Julia.previousstep() }
// }
// Button {
// id: stepButton
// text: ">"
// ToolTip.text: "Step"
// ToolTip.visible: hovered
// onClicked: { Julia.nextstep() }
// }
// Button {
// id: runButton
// text: vars.runbuttontext
// ToolTip.text: vars.runbuttontip
// ToolTip.visible: hovered
// onClicked: { vars.running = !vars.running }
// }
// ProgressBar {
// id: progressBar
// value: vars.progress
// Layout.fillWidth: true
// ToolTip.text: "Simulation progress"
// ToolTip.visible: hovered
// }
// Slider {
// id: speedSlider
// from: 0.0
// to: 2.0
// value: vars.delay
// stepSize: 0.1
// snapMode: Slider.SnapAlways
// ToolTip.text: "Time delay between updates"
// ToolTip.visible: hovered
// onValueChanged: vars.delay = value
// }
// Text {
// id: dateText
// text: Julia.datestring()
// //width: //TODO
// }
// }
// }
// TODO: MessageDialog is available since Qt 6.7
// extra windows
// MessageDialog {
// id: aboutDialog
// text: "Persefone.jl GUI"
// informativeText: "A mechanistic model of agricultural landscapes \
// and ecosystems in Europe.\n\n\
// © 2023 Daniel Vedder, Lea Kolb, Guy Pe'er\n\
// Distributed under the MIT license."
// }
// FileDialog {
// id: loadFileChooser
// defaultSuffix: "dat"
// nameFilters: ["Save files (*.dat)"]
// onAccepted: { Julia.loadsimulation(selectedFile.toString()) }
// }
// FileDialog {
// id: saveFileChooser
// defaultSuffix: "dat"
// fileMode: FileDialog.SaveFile
// nameFilters: ["Save files (*.dat)"]
// onAccepted: { Julia.savesimulation(selectedFile.toString()) }
// }
// Window {
// id: populationGraph
// title: "Population Graph"
// width: 512
// height: 512
// visible: false
// MakieViewport {
// id: plotviewport
// anchors.fill: parent
// renderFunction: render_plot_callback
// }
// }
// Popup {
// id: splashPopup
// parent: Overlay.overlay
// closePolicy: Popup.NoAutoClose
// modal: true
// padding: 0
// width: 600
// height: 250
// x: Math.round((parent.width - width) / 2)
// y: Math.round((parent.height - height) / 2)
// Image {
// anchors.fill: parent
// source: "persefonejl_logo_v3_splash.png"
// }
// }
// set up connections and signals to update the simulation and the display
// Connections {
// target: timer
// function onTimeout() { vars.ticks += 1 }
// }
// JuliaSignals {
// signal updateMakie()
// onUpdateMakie: {
// dateText.text = Julia.datestring();
// mapviewport.update();
// plotviewport.update();
// }
// signal showSplash()
// onShowSplash: {
// splashPopup.open()
// }
// signal closeSplash()
// onCloseSplash: {
// splashPopup.close()
// }
// }