Gui: add .toObject() .fromObject() (#126)
Split .loadParameters() .saveParameters() in two This allows the user to read / write the gui state and serialize it externally.
This commit is contained in:
72
orx-gui/src/demo/kotlin/DemoPresets01.kt
Normal file
72
orx-gui/src/demo/kotlin/DemoPresets01.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import org.openrndr.application
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.color.mix
|
||||||
|
import org.openrndr.extensions.SingleScreenshot
|
||||||
|
import org.openrndr.extra.gui.GUI
|
||||||
|
import org.openrndr.extra.parameters.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows how to store and retrieve in-memory gui presets.
|
||||||
|
* Keyboard controls:
|
||||||
|
* [Left Shift] + [0]..[9] => store current gui values to a preset
|
||||||
|
* [0]..[9] => recall a preset
|
||||||
|
*/
|
||||||
|
fun main() = application {
|
||||||
|
program {
|
||||||
|
// -- this block is for automation purposes only
|
||||||
|
if (System.getProperty("takeScreenshot") == "true") {
|
||||||
|
extend(SingleScreenshot()) {
|
||||||
|
this.outputFile = System.getProperty("screenshotPath")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val gui = GUI()
|
||||||
|
gui.compartmentsCollapsedByDefault = false
|
||||||
|
|
||||||
|
val presets = MutableList(10) {
|
||||||
|
gui.toObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
val settings = @Description("Settings") object {
|
||||||
|
@IntParameter("a", 1, 10)
|
||||||
|
var a = 7
|
||||||
|
|
||||||
|
@IntParameter("b", 1, 10)
|
||||||
|
var b = 3
|
||||||
|
|
||||||
|
@ColorParameter("foreground")
|
||||||
|
var foreground = ColorRGBa.fromHex("654062")
|
||||||
|
|
||||||
|
@ColorParameter("background")
|
||||||
|
var background = ColorRGBa.fromHex("ff9c71")
|
||||||
|
}
|
||||||
|
gui.add(settings)
|
||||||
|
extend(gui)
|
||||||
|
extend {
|
||||||
|
drawer.clear(settings.background)
|
||||||
|
drawer.stroke = settings.background
|
||||||
|
drawer.fill = settings.foreground
|
||||||
|
// Draw a pattern based on modulo
|
||||||
|
for(i in 0 until 100) {
|
||||||
|
if(i % settings.a == 0 || i % settings.b == 0) {
|
||||||
|
val x = (i % 10) * 64.0
|
||||||
|
val y = (i / 10) * 48.0
|
||||||
|
drawer.rectangle(x, y, 64.0, 48.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyboard.keyDown.listen {
|
||||||
|
when (it.name) {
|
||||||
|
in "0" .. "9" -> {
|
||||||
|
if(keyboard.pressedKeys.contains("left-shift")) {
|
||||||
|
// 1. Get the current gui state, store it in a list
|
||||||
|
presets[it.name.toInt()] = gui.toObject()
|
||||||
|
} else {
|
||||||
|
// 2. Set the gui state
|
||||||
|
gui.fromObject(presets[it.name.toInt()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,7 @@ package org.openrndr.extra.gui
|
|||||||
|
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import org.openrndr.Extension
|
import org.openrndr.*
|
||||||
import org.openrndr.KEY_F11
|
|
||||||
import org.openrndr.KEY_LEFT_SHIFT
|
|
||||||
import org.openrndr.KeyModifier
|
|
||||||
import org.openrndr.Program
|
|
||||||
import org.openrndr.color.ColorRGBa
|
import org.openrndr.color.ColorRGBa
|
||||||
import org.openrndr.dialogs.getDefaultPathForContext
|
import org.openrndr.dialogs.getDefaultPathForContext
|
||||||
import org.openrndr.dialogs.openFileDialog
|
import org.openrndr.dialogs.openFileDialog
|
||||||
@@ -22,8 +18,6 @@ import org.openrndr.panel.ControlManager
|
|||||||
import org.openrndr.panel.controlManager
|
import org.openrndr.panel.controlManager
|
||||||
import org.openrndr.panel.elements.*
|
import org.openrndr.panel.elements.*
|
||||||
import org.openrndr.panel.style.*
|
import org.openrndr.panel.style.*
|
||||||
import org.openrndr.panel.styleSheet
|
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
@@ -631,49 +625,62 @@ class GUI : Extension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ParameterValue(var doubleValue: Double? = null,
|
class ParameterValue(var doubleValue: Double? = null,
|
||||||
var intValue: Int? = null,
|
var intValue: Int? = null,
|
||||||
var booleanValue: Boolean? = null,
|
var booleanValue: Boolean? = null,
|
||||||
var colorValue: ColorRGBa? = null,
|
var colorValue: ColorRGBa? = null,
|
||||||
var vector2Value: Vector2? = null,
|
var vector2Value: Vector2? = null,
|
||||||
var vector3Value: Vector3? = null,
|
var vector3Value: Vector3? = null,
|
||||||
var vector4Value: Vector4? = null,
|
var vector4Value: Vector4? = null,
|
||||||
var doubleListValue: MutableList<Double>? = null,
|
var doubleListValue: MutableList<Double>? = null,
|
||||||
var textValue: String? = null,
|
var textValue: String? = null,
|
||||||
var optionValue: String? = null
|
var optionValue: String? = null
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
fun saveParameters(file: File) {
|
* Can be called by the user to obtain an object to be serialized
|
||||||
|
* externally. This allows the user to combine custom data with gui
|
||||||
|
* state and save it all to one file. Complements `.fromObject()`.
|
||||||
|
*/
|
||||||
|
fun toObject(): Map<String, Map<String, ParameterValue>> {
|
||||||
fun <T> KMutableProperty1<out Any, Any?>?.qget(obj: Any): T {
|
fun <T> KMutableProperty1<out Any, Any?>?.qget(obj: Any): T {
|
||||||
return (this as KMutableProperty1<Any, T>).get(obj)
|
return (this as KMutableProperty1<Any, T>).get(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
val toSave =
|
return trackedObjects.entries.associate { (lo, b) ->
|
||||||
trackedObjects.entries.associate { (lo, b) ->
|
Pair(lo.label, b.parameterControls.keys.associate { k ->
|
||||||
Pair(lo.label, b.parameterControls.keys.associate { k ->
|
Pair(k.property?.name ?: k.function?.name
|
||||||
Pair(k.property?.name ?: k.function?.name ?: error("no name"), when (k.parameterType) {
|
?: error("no name"), when (k.parameterType) {
|
||||||
/* 3) setup serializers */
|
/* 3) setup serializers */
|
||||||
ParameterType.Double -> ParameterValue(doubleValue = k.property.qget(lo.obj) as Double)
|
ParameterType.Double -> ParameterValue(doubleValue = k.property.qget(lo.obj) as Double)
|
||||||
ParameterType.Int -> ParameterValue(intValue = k.property.qget(lo.obj) as Int)
|
ParameterType.Int -> ParameterValue(intValue = k.property.qget(lo.obj) as Int)
|
||||||
ParameterType.Action -> ParameterValue()
|
ParameterType.Action -> ParameterValue()
|
||||||
ParameterType.Color -> ParameterValue(colorValue = k.property.qget(lo.obj) as ColorRGBa)
|
ParameterType.Color -> ParameterValue(colorValue = k.property.qget(lo.obj) as ColorRGBa)
|
||||||
ParameterType.Text -> ParameterValue(textValue = k.property.qget(lo.obj) as String)
|
ParameterType.Text -> ParameterValue(textValue = k.property.qget(lo.obj) as String)
|
||||||
ParameterType.Boolean -> ParameterValue(booleanValue = k.property.qget(lo.obj) as Boolean)
|
ParameterType.Boolean -> ParameterValue(booleanValue = k.property.qget(lo.obj) as Boolean)
|
||||||
ParameterType.XY -> ParameterValue(vector2Value = k.property.qget(lo.obj) as Vector2)
|
ParameterType.XY -> ParameterValue(vector2Value = k.property.qget(lo.obj) as Vector2)
|
||||||
ParameterType.DoubleList -> ParameterValue(doubleListValue = k.property.qget(lo.obj) as MutableList<Double>)
|
ParameterType.DoubleList -> ParameterValue(doubleListValue = k.property.qget(lo.obj) as MutableList<Double>)
|
||||||
ParameterType.Vector2 -> ParameterValue(vector2Value = k.property.qget(lo.obj) as Vector2)
|
ParameterType.Vector2 -> ParameterValue(vector2Value = k.property.qget(lo.obj) as Vector2)
|
||||||
ParameterType.Vector3 -> ParameterValue(vector3Value = k.property.qget(lo.obj) as Vector3)
|
ParameterType.Vector3 -> ParameterValue(vector3Value = k.property.qget(lo.obj) as Vector3)
|
||||||
ParameterType.Vector4 -> ParameterValue(vector4Value = k.property.qget(lo.obj) as Vector4)
|
ParameterType.Vector4 -> ParameterValue(vector4Value = k.property.qget(lo.obj) as Vector4)
|
||||||
ParameterType.Option -> ParameterValue(optionValue = (k.property.qget(lo.obj) as Enum<*>).name)
|
ParameterType.Option -> ParameterValue(optionValue = (k.property.qget(lo.obj) as Enum<*>).name)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
file.writeText(Gson().toJson(toSave))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadParameters(file: File) {
|
fun saveParameters(file: File) {
|
||||||
|
file.writeText(Gson().toJson(toObject()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be called by the user to update the gui using an object
|
||||||
|
* deserialized externally. Allows the user to load a larger json object,
|
||||||
|
* deserialize it, and use part of it to update the GUI.
|
||||||
|
* Complements `.toObject()`.
|
||||||
|
*/
|
||||||
|
fun fromObject(labeledValues: Map<String, Map<String, ParameterValue>>) {
|
||||||
fun <T> KMutableProperty1<out Any, Any?>?.qset(obj: Any, value: T) =
|
fun <T> KMutableProperty1<out Any, Any?>?.qset(obj: Any, value: T) =
|
||||||
(this as KMutableProperty1<Any, T>).set(obj, value)
|
(this as KMutableProperty1<Any, T>).set(obj, value)
|
||||||
|
|
||||||
@@ -686,10 +693,6 @@ class GUI : Extension {
|
|||||||
(this as KMutableProperty1<Any, Enum<*>>).set(obj, enumValue)
|
(this as KMutableProperty1<Any, Enum<*>>).set(obj, enumValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
val json = file.readText()
|
|
||||||
val typeToken = object : TypeToken<Map<String, Map<String, ParameterValue>>>() {}
|
|
||||||
val labeledValues: Map<String, Map<String, ParameterValue>> = Gson().fromJson(json, typeToken.type)
|
|
||||||
|
|
||||||
labeledValues.forEach { (label, ps) ->
|
labeledValues.forEach { (label, ps) ->
|
||||||
trackedObjects.keys.find { it.label == label }?.let { lo ->
|
trackedObjects.keys.find { it.label == label }?.let { lo ->
|
||||||
val binding = trackedObjects[lo]!!
|
val binding = trackedObjects[lo]!!
|
||||||
@@ -741,6 +744,14 @@ class GUI : Extension {
|
|||||||
updateControls()
|
updateControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadParameters(file: File) {
|
||||||
|
val json = file.readText()
|
||||||
|
val typeToken = object : TypeToken<Map<String, Map<String, ParameterValue>>>() {}
|
||||||
|
val labeledValues: Map<String, Map<String, ParameterValue>> = Gson().fromJson(json, typeToken.type)
|
||||||
|
|
||||||
|
fromObject(labeledValues)
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateControl(labeledObject: LabeledObject, parameter: Parameter, control: Element) {
|
private fun updateControl(labeledObject: LabeledObject, parameter: Parameter, control: Element) {
|
||||||
when (parameter.parameterType) {
|
when (parameter.parameterType) {
|
||||||
/* 5) Update control from property value */
|
/* 5) Update control from property value */
|
||||||
|
|||||||
Reference in New Issue
Block a user