[orx-file-watcher, orx-property-watchers] Add events and documentation
This commit is contained in:
@@ -3,9 +3,7 @@ package org.openrndr.extra.filewatcher
|
|||||||
import com.sun.nio.file.SensitivityWatchEventModifier
|
import com.sun.nio.file.SensitivityWatchEventModifier
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import org.openrndr.Program
|
|
||||||
import org.openrndr.events.Event
|
import org.openrndr.events.Event
|
||||||
import org.openrndr.launch
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@@ -13,7 +11,6 @@ import java.nio.file.StandardWatchEventKinds
|
|||||||
import java.nio.file.WatchKey
|
import java.nio.file.WatchKey
|
||||||
import java.util.WeakHashMap
|
import java.util.WeakHashMap
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
@@ -52,15 +49,19 @@ private val watchThread by lazy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property file
|
||||||
|
* @property fileChangedEvent
|
||||||
|
* @param requestStopEvent
|
||||||
|
*/
|
||||||
class FileWatcher(
|
class FileWatcher(
|
||||||
val file: File,
|
private val file: File,
|
||||||
private val fileChangedEvent: Event<File>,
|
private val fileChangedEvent: Event<File>,
|
||||||
requestStopEvent: Event<Unit>? = null
|
requestStopEvent: Event<Unit>? = null
|
||||||
) {
|
) {
|
||||||
val path = file.absoluteFile.toPath()
|
private val path = file.absoluteFile.toPath()
|
||||||
val parent = path.parent
|
private val parent = path.parent
|
||||||
val key = pathKeys.getOrPut(parent) {
|
private val key = pathKeys.getOrPut(parent) {
|
||||||
parent.register(
|
parent.register(
|
||||||
watchService, arrayOf(StandardWatchEventKinds.ENTRY_MODIFY),
|
watchService, arrayOf(StandardWatchEventKinds.ENTRY_MODIFY),
|
||||||
SensitivityWatchEventModifier.HIGH
|
SensitivityWatchEventModifier.HIGH
|
||||||
@@ -78,6 +79,7 @@ class FileWatcher(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
fun stop() {
|
fun stop() {
|
||||||
synchronized(watching) {
|
synchronized(watching) {
|
||||||
logger.info { "stopping, watcher stop requested" }
|
logger.info { "stopping, watcher stop requested" }
|
||||||
@@ -90,34 +92,36 @@ class FileWatcher(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watch a file for changes
|
||||||
fun <T> watchFile(
|
* @param file the file to watch
|
||||||
|
* @param valueChangedEvent the event that is triggered when the value (after transforming) has changed
|
||||||
|
* @param requestStopEvent an event that can be triggered to request the watcher to stop
|
||||||
|
* @param transducer a function that transforms a [File] into a value of type [R]
|
||||||
|
*/
|
||||||
|
fun <R> watchFile(
|
||||||
file: File,
|
file: File,
|
||||||
contentsChangedEvent: Event<T>? = null,
|
valueChangedEvent: Event<R>? = null,
|
||||||
requestStopEvent: Event<Unit>? = null,
|
requestStopEvent: Event<Unit>? = null,
|
||||||
transducer: (File) -> T
|
transducer: (File) -> R
|
||||||
): () -> T {
|
): () -> R {
|
||||||
var result = transducer(file)
|
var result = transducer(file)
|
||||||
val fileChangedEvent = Event<File>()
|
val fileChangedEvent = Event<File>()
|
||||||
val watcher = FileWatcher(file, fileChangedEvent, requestStopEvent)
|
|
||||||
|
@Suppress("UNUSED_VARIABLE") val watcher = FileWatcher(file, fileChangedEvent, requestStopEvent)
|
||||||
|
|
||||||
fileChangedEvent.listen {
|
fileChangedEvent.listen {
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
try {
|
try {
|
||||||
result = transducer(file)
|
result = transducer(file)
|
||||||
contentsChangedEvent?.trigger(result)
|
valueChangedEvent?.trigger(result)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
logger.error(e) {
|
logger.error(e) {
|
||||||
"""exception while transducing file"""
|
"""exception while transforming file ${file.absolutePath}"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//@JvmName("programWatchFile")
|
|
||||||
//fun <T> Program.watchFile(file: File, onChange: Event<T>? = null, transducer: (File) -> T): () -> T =
|
|
||||||
// watchFile(this, file, onChange, transducer = transducer)
|
|
||||||
@@ -1,11 +1,21 @@
|
|||||||
|
package org.openrndr.extra.filewatcher
|
||||||
|
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
import org.openrndr.Program
|
import org.openrndr.Program
|
||||||
import org.openrndr.events.Event
|
import org.openrndr.events.Event
|
||||||
import org.openrndr.extra.filewatcher.watchFile
|
|
||||||
import org.openrndr.launch
|
import org.openrndr.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property delegator that watches a file. Changes are propagated right before the [Program] updates its extensions
|
||||||
|
* @param program the program to synchronise updates with
|
||||||
|
* @param file the file to watch
|
||||||
|
* @param valueChangedEvent the event that is triggered when the value (after transformation) has changed
|
||||||
|
* @param requestStopEvent an event that can be triggered to request the watcher to stop
|
||||||
|
* @since 0.4.3
|
||||||
|
* @see watchingFile
|
||||||
|
*/
|
||||||
class FileWatcherDelegate<T>(
|
class FileWatcherDelegate<T>(
|
||||||
program: Program,
|
program: Program,
|
||||||
file: File,
|
file: File,
|
||||||
@@ -13,10 +23,11 @@ class FileWatcherDelegate<T>(
|
|||||||
requestStopEvent: Event<Unit>? = null,
|
requestStopEvent: Event<Unit>? = null,
|
||||||
transducer: (File) -> T
|
transducer: (File) -> T
|
||||||
) {
|
) {
|
||||||
val watchValue = watchFile(file, valueChangedEvent, requestStopEvent, transducer)
|
private val watchValue = watchFile(file, valueChangedEvent, requestStopEvent, transducer)
|
||||||
var value = watchValue()
|
private var value = watchValue()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
// make sure that `value` is updated at the beginning of a draw cycle and not mid-cycle.
|
||||||
program.launch {
|
program.launch {
|
||||||
while (true) {
|
while (true) {
|
||||||
value = watchValue()
|
value = watchValue()
|
||||||
@@ -25,11 +36,23 @@ class FileWatcherDelegate<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return transformed value
|
||||||
|
*/
|
||||||
operator fun getValue(any: Any?, property: KProperty<*>): T {
|
operator fun getValue(any: Any?, property: KProperty<*>): T {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate value to a file watcher
|
||||||
|
* @param file the file to watch
|
||||||
|
* @param valueChangedEvent the event that is triggered when the value (after transformation) has changed
|
||||||
|
* @param requestStopEvent an event that can be triggered to request the watcher to stop
|
||||||
|
* @param transducer a function that transforms a [File] into a value of type [R]
|
||||||
|
* @since 0.4.3
|
||||||
|
* @see FileWatcherDelegate
|
||||||
|
*/
|
||||||
fun <R> Program.watchingFile(
|
fun <R> Program.watchingFile(
|
||||||
file: File,
|
file: File,
|
||||||
valueChangedEvent: Event<R>? = null,
|
valueChangedEvent: Event<R>? = null,
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import kotlin.reflect.KProperty0
|
|||||||
/**
|
/**
|
||||||
* Property watcher delegate
|
* Property watcher delegate
|
||||||
* @see watchingProperty
|
* @see watchingProperty
|
||||||
|
* @since 0.4.3
|
||||||
*/
|
*/
|
||||||
class PropertyWatcherDelegate<V, R>(
|
class PropertyWatcherDelegate<V, R>(
|
||||||
private val property: KProperty0<V>,
|
private val property: KProperty0<V>,
|
||||||
private val valueChangedEvent: Event<V>,
|
private val valueChangedEvent: Event<R>? = null,
|
||||||
private val cleaner: ((R) -> Unit)? = null,
|
private val cleaner: ((R) -> Unit)? = null,
|
||||||
val function: (V) -> R
|
val function: (V) -> R
|
||||||
) {
|
) {
|
||||||
@@ -24,19 +25,21 @@ class PropertyWatcherDelegate<V, R>(
|
|||||||
cleaner?.invoke(it)
|
cleaner?.invoke(it)
|
||||||
}
|
}
|
||||||
value = function(ref)
|
value = function(ref)
|
||||||
|
valueChangedEvent?.trigger(value ?: error("no value"))
|
||||||
}
|
}
|
||||||
return value ?: error("no value?")
|
return value ?: error("no value?")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property watcher delegate
|
* Property watcher delegate
|
||||||
* @see watchingProperties
|
* @see watchingProperties
|
||||||
|
* @since 0.4.3
|
||||||
*/
|
*/
|
||||||
class PropertyWatcherDelegate2<V0, V1, R>(
|
class PropertyWatcherDelegate2<V0, V1, R>(
|
||||||
private val toWatch0: KProperty0<V0>,
|
private val toWatch0: KProperty0<V0>,
|
||||||
private val toWatch1: KProperty0<V1>,
|
private val toWatch1: KProperty0<V1>,
|
||||||
|
private val valueChangedEvent: Event<R>? = null,
|
||||||
private val cleaner: ((R) -> Unit)? = null,
|
private val cleaner: ((R) -> Unit)? = null,
|
||||||
private val function: (V0, V1) -> R
|
private val function: (V0, V1) -> R
|
||||||
) {
|
) {
|
||||||
@@ -54,6 +57,7 @@ class PropertyWatcherDelegate2<V0, V1, R>(
|
|||||||
cleaner?.invoke(it)
|
cleaner?.invoke(it)
|
||||||
}
|
}
|
||||||
value = function(ref0, ref1)
|
value = function(ref0, ref1)
|
||||||
|
valueChangedEvent?.trigger(value ?: error("no value"))
|
||||||
}
|
}
|
||||||
return value ?: error("no value?")
|
return value ?: error("no value?")
|
||||||
}
|
}
|
||||||
@@ -62,11 +66,13 @@ class PropertyWatcherDelegate2<V0, V1, R>(
|
|||||||
/**
|
/**
|
||||||
* Property watcher delegate
|
* Property watcher delegate
|
||||||
* @see watchingProperties
|
* @see watchingProperties
|
||||||
|
* @since 0.4.3
|
||||||
*/
|
*/
|
||||||
class PropertyWatcherDelegate3<V0, V1, V2, R>(
|
class PropertyWatcherDelegate3<V0, V1, V2, R>(
|
||||||
private val toWatch0: KProperty0<V0>,
|
private val toWatch0: KProperty0<V0>,
|
||||||
private val toWatch1: KProperty0<V1>,
|
private val toWatch1: KProperty0<V1>,
|
||||||
private val toWatch2: KProperty0<V2>,
|
private val toWatch2: KProperty0<V2>,
|
||||||
|
private val valueChangedEvent: Event<R>? = null,
|
||||||
private val cleaner: ((R) -> Unit)? = null,
|
private val cleaner: ((R) -> Unit)? = null,
|
||||||
private val function: (V0, V1, V2) -> R
|
private val function: (V0, V1, V2) -> R
|
||||||
) {
|
) {
|
||||||
@@ -85,6 +91,7 @@ class PropertyWatcherDelegate3<V0, V1, V2, R>(
|
|||||||
cleaner?.invoke(it)
|
cleaner?.invoke(it)
|
||||||
}
|
}
|
||||||
value = function(ref0, ref1, ref2)
|
value = function(ref0, ref1, ref2)
|
||||||
|
valueChangedEvent?.trigger(value ?: error("no value"))
|
||||||
}
|
}
|
||||||
return value ?: error("no value?")
|
return value ?: error("no value?")
|
||||||
}
|
}
|
||||||
@@ -94,30 +101,33 @@ class PropertyWatcherDelegate3<V0, V1, V2, R>(
|
|||||||
/**
|
/**
|
||||||
* Delegate property value to a function for which the value of a single property is watched
|
* Delegate property value to a function for which the value of a single property is watched
|
||||||
* @param property the property for which to watch for value changes
|
* @param property the property for which to watch for value changes
|
||||||
|
* @param valueChangedEvent an optional event that is triggered on value change
|
||||||
|
* @param cleaner an optional cleaner function that is invoked to clean up the old value
|
||||||
* @param function a function that maps the property value to a new value
|
* @param function a function that maps the property value to a new value
|
||||||
|
* @since 0.4.3
|
||||||
*/
|
*/
|
||||||
fun <V, R> watchingProperty(
|
fun <V, R> watchingProperty(
|
||||||
property: KProperty0<V>,
|
property: KProperty0<V>,
|
||||||
|
valueChangedEvent: Event<R>? = null,
|
||||||
cleaner: ((R) -> Unit)? = null,
|
cleaner: ((R) -> Unit)? = null,
|
||||||
function: (value: V) -> R
|
function: (value: V) -> R
|
||||||
): PropertyWatcherDelegate<V, R> {
|
): PropertyWatcherDelegate<V, R> = PropertyWatcherDelegate(property, valueChangedEvent, cleaner, function)
|
||||||
return PropertyWatcherDelegate(property, Event("value-changed-${property.name}"), cleaner, function)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate property value to a function for which the values of 2 properties are watched
|
* Delegate property value to a function for which the values of 2 properties are watched
|
||||||
* @param property0 the first property for which to watch for value changes
|
* @param property0 the first property for which to watch for value changes
|
||||||
* @param property1 the second property which to watch for value changes
|
* @param property1 the second property which to watch for value changes
|
||||||
* @param function a function that maps the two property values to a new value
|
* @param function a function that maps the two property values to a new value
|
||||||
|
* @since 0.4.3
|
||||||
*/
|
*/
|
||||||
fun <V0, V1, R> watchingProperties(
|
fun <V0, V1, R> watchingProperties(
|
||||||
property0: KProperty0<V0>,
|
property0: KProperty0<V0>,
|
||||||
property1: KProperty0<V1>,
|
property1: KProperty0<V1>,
|
||||||
|
valueChangedEvent: Event<R>? = null,
|
||||||
cleaner: ((R) -> Unit)?,
|
cleaner: ((R) -> Unit)?,
|
||||||
function: (value0: V0, value1: V1) -> R
|
function: (value0: V0, value1: V1) -> R
|
||||||
): PropertyWatcherDelegate2<V0, V1, R> {
|
): PropertyWatcherDelegate2<V0, V1, R> =
|
||||||
return PropertyWatcherDelegate2(property0, property1, cleaner, function)
|
PropertyWatcherDelegate2(property0, property1, valueChangedEvent, cleaner, function)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate property value to a function for which the values of 3 properties are watched
|
* Delegate property value to a function for which the values of 3 properties are watched
|
||||||
@@ -125,13 +135,14 @@ fun <V0, V1, R> watchingProperties(
|
|||||||
* @param property1 the second property which to watch for value changes
|
* @param property1 the second property which to watch for value changes
|
||||||
* @param property2 the third property which to watch for value changes
|
* @param property2 the third property which to watch for value changes
|
||||||
* @param function a function that maps the three property values to a new value
|
* @param function a function that maps the three property values to a new value
|
||||||
|
* @since 0.4.3
|
||||||
*/
|
*/
|
||||||
fun <V0, V1, V2, R> watchingProperties(
|
fun <V0, V1, V2, R> watchingProperties(
|
||||||
property0: KProperty0<V0>,
|
property0: KProperty0<V0>,
|
||||||
property1: KProperty0<V1>,
|
property1: KProperty0<V1>,
|
||||||
property2: KProperty0<V2>,
|
property2: KProperty0<V2>,
|
||||||
|
valueChangedEvent: Event<R>? = null,
|
||||||
cleaner: ((R) -> Unit)? = null,
|
cleaner: ((R) -> Unit)? = null,
|
||||||
function: (value0: V0, value1: V1, value2: V2) -> R
|
function: (value0: V0, value1: V1, value2: V2) -> R
|
||||||
): PropertyWatcherDelegate3<V0, V1, V2, R> {
|
): PropertyWatcherDelegate3<V0, V1, V2, R> =
|
||||||
return PropertyWatcherDelegate3(property0, property1, property2, cleaner, function)
|
PropertyWatcherDelegate3(property0, property1, property2, valueChangedEvent, cleaner, function)
|
||||||
}
|
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
package org.openrndr.extra.propertywatchers
|
package org.openrndr.extra.propertywatchers
|
||||||
|
|
||||||
import org.openrndr.Program
|
|
||||||
import org.openrndr.draw.ColorBuffer
|
import org.openrndr.draw.ColorBuffer
|
||||||
import org.openrndr.draw.loadImage
|
import org.openrndr.draw.loadImage
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.reflect.KProperty0
|
import kotlin.reflect.KProperty0
|
||||||
|
|
||||||
fun Program.watchingImagePath(pathProperty: KProperty0<String>, imageTransform: (ColorBuffer) -> ColorBuffer = { it }) =
|
/**
|
||||||
|
* Delegate property value by watching a path property
|
||||||
|
* @param pathProperty the property holding a path to watch
|
||||||
|
* @param imageTransform an optional image transform function
|
||||||
|
* @since 0.4.3
|
||||||
|
*/
|
||||||
|
fun watchingImagePath(pathProperty: KProperty0<String>, imageTransform: (ColorBuffer) -> ColorBuffer = { it }) =
|
||||||
watchingProperty(pathProperty, cleaner = { it.destroy() }) {
|
watchingProperty(pathProperty, cleaner = { it.destroy() }) {
|
||||||
val file = File(it)
|
val file = File(it)
|
||||||
require(file.exists()) { "$it does not exist" }
|
require(file.exists()) { "$it does not exist" }
|
||||||
@@ -17,4 +22,4 @@ fun Program.watchingImagePath(pathProperty: KProperty0<String>, imageTransform:
|
|||||||
image.destroy()
|
image.destroy()
|
||||||
}
|
}
|
||||||
transformedImage
|
transformedImage
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user