Title: | A Factory of Self-Describing Properties |
---|---|
Description: | Supports the definition of sets of properties on objects. Observers can listen to changes on individual properties or the set as a whole. The properties are meant to be fully self-describing. In support of this, there is a framework for defining enumerated types, as well as other bounded types, as S4 classes. |
Authors: | Tengfei Yin [aut], Michael Lawrence [aut, cre] |
Maintainer: | Michael Lawrence <[email protected]> |
License: | GPL (>= 2) |
Version: | 0.6.8 |
Built: | 2024-10-27 03:48:28 UTC |
Source: | https://github.com/cran/objectProperties |
list
Coercion from PropertySet
to list
.
x |
A |
This coersion only return a list of properties instances. filtering out singal function and other fields which are not properties.
A list of properties instance.
Tengfei Yin
filt.gen <- setRefClass("Filter", properties(list(cutoff = "NonnegativeInteger", weight = "PositiveInteger")), contains = "PropertySet") obj <- filt.gen$new(cutoff = NonnegativeInteger(0), weight = PositiveInteger(1)) obj$properties() as.list(obj)
filt.gen <- setRefClass("Filter", properties(list(cutoff = "NonnegativeInteger", weight = "PositiveInteger")), contains = "PropertySet") obj <- filt.gen$new(cutoff = NonnegativeInteger(0), weight = PositiveInteger(1)) obj$properties() as.list(obj)
This set of classes define different numerical object with restriction on it.
PositiveInteger(object)
PositiveInteger(object)
object |
object to be coerced |
These special classes could be registered as signaling fields by calling
signalingFields
or signalingField
, or using setProperties
,
so they could be used for GUI design, and changing of the fields automatically
validate the current value
The construction of these objects has validation with them, please see the example.
Construct a PositiveInteger
object
Construct a NonpositiveInteger
object
Construct a NegativeInteger
object
Construct a NonnegativeInteger
object
An object of the corresponding class
Tengfei Yin, Michael Lawrence
## Constructors require(objectProperties) obj <- PositiveInteger(1) obj <- NonnegativeInteger(0) obj <- NegativeInteger(-1) obj <- NonpositiveInteger(0) ## setting as properties filt.gen <- setRefClass("Filter", properties(list(cutoff = "NonnegativeInteger", weight = "PositiveInteger")), contains = "PropertySet") ## new property instance obj <- filt.gen$new(cutoff = 0, weight = 1) obj$properties() as.list(obj) ## get the value obj$cutoff ## set the value obj$cutoff <- 30 ## the class doesn't change ## if you pass a value which out of boundary, it will throw an error message obj$cutoff class(obj$cutoff)
## Constructors require(objectProperties) obj <- PositiveInteger(1) obj <- NonnegativeInteger(0) obj <- NegativeInteger(-1) obj <- NonpositiveInteger(0) ## setting as properties filt.gen <- setRefClass("Filter", properties(list(cutoff = "NonnegativeInteger", weight = "PositiveInteger")), contains = "PropertySet") ## new property instance obj <- filt.gen$new(cutoff = 0, weight = 1) obj$properties() as.list(obj) ## get the value obj$cutoff ## set the value obj$cutoff <- 30 ## the class doesn't change ## if you pass a value which out of boundary, it will throw an error message obj$cutoff class(obj$cutoff)
R functions often have parameters with enumerated values. These are
typically passed as a character vector and resolved using
match.arg(). The Enum
structure is very similar to that of a factor,
except the data is character, not integer and with appropriate validation.
setSingleEnum(prefix, levels, contains=character(), where=topenv(parent.frame())) ## S4 method for signature 'Enum' levels(x)
setSingleEnum(prefix, levels, contains=character(), where=topenv(parent.frame())) ## S4 method for signature 'Enum' levels(x)
prefix |
Prefix for new subclass of |
levels |
An vector of characters which define the levels for this class. |
contains |
What class does this class extended besides |
where |
the environment in which to store or remove the definition. Defaults to the top-level environment of the calling function. |
x |
A |
The SingleEnum
object is different from simple factor.
It validates the value to see if it's in the defined levels during construction.
and only the value within defined levels is allowed to be set as current chosen value
when it is created as property. It is particularly useful for GUI design,
such as creating a drop list or ratio buttons for exclusive choice,
you can only choose one item within certain choices at one time.
setSingleEnum
will create a S4 subclass for SingleEnum
,
and return the class name.
The MultipleEnum
has the same design with SingleEnum
, except
it support multiple choices. So for GUI level, it could be used for creating
check boxes. setMultipleEnum
will create a S4 subclass for MultipleEnum
,
and return the class name.
The Enum
class is a Class union for SingleEnum
and MultipleEnum
Color
class is a special character, this properties could be used
for creating a widgets which showing a color picker pallete and a text
input field, a simple character object will be only treated as simple
text in the UI. Color
class could be constructed by constructor
Color
.
ColorEnum
class is a VIRTUAL class, which including a set of
SingleEnum
subclass, when creating widget based on this property,
it should be treated as a special color droplist, instead of showing
a droplist of levels of text, it shows a drop list of colors, the levels
are treated as color in this class.
setColorEnum
is a convenient class generator function for single enum
of ColorEnum
and it return a class name.
GlyphEnum
class is a VIRTUAL class, which including a set of
SingleEnum
subclass, when creating widget based on this property,
it should be treated as a special glyph droplist, instead of showing
a droplist of levels of text, it shows a drop list of different glyphs, the levels
are treated as glyphs in this class. Different engine genenerate icons for different
glyphs, such as different point size, line type, etc.
setGlyphEnum
is a convenient class generator function for single enum
of GlyphEnum
and it return a class name.
setSingleEnum
return a class name for SingleEnum
subclass.setMultipleEnum
return a class name for MultipleEnum
subclass.
setColorEnum
return a class name for ColorEnum
subclass which is also
a SingleEnum
. setGlyphEnum
return a class name for GlyphEnum
subclass which is also a SingleEnum
. All those function return a
generator function in R(>= 2.15)
Tengfei Yin, Michael Lawrence
## ---------------------------------------------------------------------- ## setSingleEnum ## ---------------------------------------------------------------------- ShapeEnum.gen <- setSingleEnum("Shape", levels = c("circle", "line", "rectangle")) obj <- new("ShapeSingleEnum", "circle") obj obj <- "triangle" # doesn't check, because it's not signal field. obj # it's not SingleEnum object anymore, be careful. class(obj) # just character ## only set it as properties, allow you to assign the value and ## validate it. par.gen <- setRefClass("Graph", properties(fields = list(shape = "ShapeSingleEnum"), prototype = list(shape = new("ShapeSingleEnum", "circle")))) pars <- par.gen$new() pars$shape pars$shape <- "line" pars$shape class(pars$shape)# still a SingleEnum ## ---------------------------------------------------------------------- ## setMultipleEnum ## ---------------------------------------------------------------------- ShapeEnum.gen <- setMultipleEnum("Shape", levels = c("circle", "line", "rectangle")) par.gen <- setRefClass("Graph", properties(list(shape = "ShapeMultipleEnum"))) ## we can initialize in this way too pars <- par.gen$new(shape = new("ShapeMultipleEnum", c("circle", "line"))) pars$shape pars$shape <- c("line", "rectangle") pars$shape class(pars$shape)# still a MultipleEnum ## Color Single Enum bgColorSingleEnum.gen <- setColorEnum("bgColor", levels = c("black", "white", "gray")) obj <- new("bgColorSingleEnum", "white") ## Glyph Single Enum PointSizeSingleEnum.gen <- setGlyphEnum("PointSize", levels = c("1", "2", "5", "10"), contains = "GlyphEnum") obj <- new("PointSizeSingleEnum", "1") obj ## ---------------------------------------------------------------------- ## change levels ## ---------------------------------------------------------------------- geomSingleEnum <- setSingleEnum("geom", c("rect", "triangle")) obj <- geomSingleEnum("rect") ## change levels levels(obj) levels(obj) <- c("rect", "circle") ## changed levels must include current value try(levels(obj) <- c("triangle", "circle")) ## ---------------------------------------------------------------------- ## change levels ## ---------------------------------------------------------------------- obj <- factor("a", levels = letters) SingleEnum(obj) MultipleEnum(obj)
## ---------------------------------------------------------------------- ## setSingleEnum ## ---------------------------------------------------------------------- ShapeEnum.gen <- setSingleEnum("Shape", levels = c("circle", "line", "rectangle")) obj <- new("ShapeSingleEnum", "circle") obj obj <- "triangle" # doesn't check, because it's not signal field. obj # it's not SingleEnum object anymore, be careful. class(obj) # just character ## only set it as properties, allow you to assign the value and ## validate it. par.gen <- setRefClass("Graph", properties(fields = list(shape = "ShapeSingleEnum"), prototype = list(shape = new("ShapeSingleEnum", "circle")))) pars <- par.gen$new() pars$shape pars$shape <- "line" pars$shape class(pars$shape)# still a SingleEnum ## ---------------------------------------------------------------------- ## setMultipleEnum ## ---------------------------------------------------------------------- ShapeEnum.gen <- setMultipleEnum("Shape", levels = c("circle", "line", "rectangle")) par.gen <- setRefClass("Graph", properties(list(shape = "ShapeMultipleEnum"))) ## we can initialize in this way too pars <- par.gen$new(shape = new("ShapeMultipleEnum", c("circle", "line"))) pars$shape pars$shape <- c("line", "rectangle") pars$shape class(pars$shape)# still a MultipleEnum ## Color Single Enum bgColorSingleEnum.gen <- setColorEnum("bgColor", levels = c("black", "white", "gray")) obj <- new("bgColorSingleEnum", "white") ## Glyph Single Enum PointSizeSingleEnum.gen <- setGlyphEnum("PointSize", levels = c("1", "2", "5", "10"), contains = "GlyphEnum") obj <- new("PointSizeSingleEnum", "1") obj ## ---------------------------------------------------------------------- ## change levels ## ---------------------------------------------------------------------- geomSingleEnum <- setSingleEnum("geom", c("rect", "triangle")) obj <- geomSingleEnum("rect") ## change levels levels(obj) levels(obj) <- c("rect", "circle") ## changed levels must include current value try(levels(obj) <- c("triangle", "circle")) ## ---------------------------------------------------------------------- ## change levels ## ---------------------------------------------------------------------- obj <- factor("a", levels = letters) SingleEnum(obj) MultipleEnum(obj)
Convenience function for defining a set of reference class fields that signals when set.
properties(fields=list(), prototype=list())
properties(fields=list(), prototype=list())
fields |
list of names of the field and associated fields class |
prototype |
A list of values declaring a default value for a field. |
When constructing signaling fields in this way, each field has the ability to register its own signal and at the same time, there is one top level signal which could be emitted no matter which field changes. Please see the example to learn to register global signal and individual signal.
A list that is easily concatenated into the field list
Michael Lawrence, Tengfei Yin
## we could pass prototype as in S4 GPars.gen <- setRefClass("GraphicProperties", fields = properties(fields = list(size = "numeric", color = "character"), prototype = list(size =1, color = "red"))) obj <- GPars.gen$new() ## since it's not PropertySet, no global signal ## let's register individual signal obj$sizeChanged$connect(function(){ print("size changed") }) ## emit signal obj$size <- 3 ## no signal obj$color <- "black"
## we could pass prototype as in S4 GPars.gen <- setRefClass("GraphicProperties", fields = properties(fields = list(size = "numeric", color = "character"), prototype = list(size =1, color = "red"))) obj <- GPars.gen$new() ## since it's not PropertySet, no global signal ## let's register individual signal obj$sizeChanged$connect(function(){ print("size changed") }) ## emit signal obj$size <- 3 ## no signal obj$color <- "black"
The PropertySet
class is a collection of properties and is
useful as a data model, e.g., for storing the parameters of some
operation.
setPropertySet
is a simple wrapper around setRefClass
for
creating subclasses of PropertySet
. It
ensures that all fields of the subclass are defined via
properties
.
setPropertySet(Class, fields=list(), prototype=list(), contains="PropertySet", ..., where=topenv(parent.frame()))
setPropertySet(Class, fields=list(), prototype=list(), contains="PropertySet", ..., where=topenv(parent.frame()))
Class |
class name |
fields |
list of fields |
prototype |
list of default values, as in
|
contains |
superclasses, one of which must extend PropertySet |
... |
additional arguments to |
where |
the environment in which to define the class |
PropertySet-class
: PropertySet
object has following methods, where x
is
a PropertySet
object:
x$properties()
Return the classes of the properties as a
named character vector. Compare to the fields
method on
a reference class generator
.
as.list(x)
Returns a named list of the property values.
When any property in the set changes, the changed(name)
signal is emitted, where name
is the name of the property
that changed.
setPropertySet
: the class generator object
Michael Lawrence, Tengfei Yin
filt.gen <- setRefClass("Filter", properties(fields = list(cutoff = "numeric", weight = "numeric"), prototype = list(cutoff = 0, weight = 1)), contains = "PropertySet") obj <- filt.gen$new() obj obj$properties() as.list(obj) obj$changed$connect(function(name) print(name)) obj$cutoffChanged$connect(function() print(paste("change to", obj$cutoff))) obj$cutoff <- 0 obj$cutoff <- 2 obj$weight <- 3 ## use setPropertySet, the same thing as above filt.gen <- setPropertySet("Filter", fields = list(cutoff = "numeric", weight = "numeric"), prototype = list(cutoff = 0, weight = 1)) obj <- filt.gen$new() obj obj$properties() as.list(obj) obj$changed$connect(function(name) print(name)) obj$cutoffChanged$connect(function() print(paste("change to", obj$cutoff))) obj$cutoff <- 0 obj$cutoff <- 2 obj$weight <- 3
filt.gen <- setRefClass("Filter", properties(fields = list(cutoff = "numeric", weight = "numeric"), prototype = list(cutoff = 0, weight = 1)), contains = "PropertySet") obj <- filt.gen$new() obj obj$properties() as.list(obj) obj$changed$connect(function(name) print(name)) obj$cutoffChanged$connect(function() print(paste("change to", obj$cutoff))) obj$cutoff <- 0 obj$cutoff <- 2 obj$weight <- 3 ## use setPropertySet, the same thing as above filt.gen <- setPropertySet("Filter", fields = list(cutoff = "numeric", weight = "numeric"), prototype = list(cutoff = 0, weight = 1)) obj <- filt.gen$new() obj obj$properties() as.list(obj) obj$changed$connect(function(name) print(name)) obj$cutoffChanged$connect(function() print(paste("change to", obj$cutoff))) obj$cutoff <- 0 obj$cutoff <- 2 obj$weight <- 3
This class creator is used to define a special property for numeric range, which could be used for UI design and could be setted as signaling field, so it will support validation on the input.
setIntegerWithRange(prefix = "Integer", min, max, where=topenv(parent.frame()))
setIntegerWithRange(prefix = "Integer", min, max, where=topenv(parent.frame()))
prefix |
Prefix for new class name. Default is "Integer" |
min |
Minimal value for this range object. |
max |
Maximal value for this range object. |
where |
the environment in which to store or remove the definition. Defaults to the top-level environment of the calling function. |
The purpose of creating such a class genenrator is to define a special
range properties which could be set as singaling field, such as Properties
object. Then validation will be turned on automatically to make sure the current
value is within the defined range. This is particular useful when you try to
design a slider widget of such a property, let's say, a alpha blending slider.
A S4 class name in R(< 2.15) and a generator function in R(>= 2.15)
Tengfei Yin
num1to100.gen <- setIntegerWithRange(min = 1L, max = 100L) par.gen <- setRefClass("Graph", properties(list(size = "IntegerWithMin1Max100"))) pars <- par.gen$new(size = new("IntegerWithMin1Max100", 5.5)) ## Covert to integer pars$size #current value is 5 try(pars$size <- 300) # out of range error pars$size <- 4.4 # covert to integer pars$size
num1to100.gen <- setIntegerWithRange(min = 1L, max = 100L) par.gen <- setRefClass("Graph", properties(list(size = "IntegerWithMin1Max100"))) pars <- par.gen$new(size = new("IntegerWithMin1Max100", 5.5)) ## Covert to integer pars$size #current value is 5 try(pars$size <- 300) # out of range error pars$size <- 4.4 # covert to integer pars$size
This class creator is used to define a special property for numeric range, which could be used for UI design and could be setted as signaling field, so it will support validation on the input.
setNumericWithRange(prefix = "Numeric", min, max, where=topenv(parent.frame()))
setNumericWithRange(prefix = "Numeric", min, max, where=topenv(parent.frame()))
prefix |
Prefix for new class name.Default is "Numeric" |
min |
Minimal value for this range object. |
max |
Maximal value for this range object. |
where |
the environment in which to store or remove the definition. Defaults to the top-level environment of the calling function. |
The purpose of creating such a class genenrator is to define a special
range properties which could be set as singaling field, such as Properties
object. Then validation will be turned on automatically to make sure the current
value is within the defined range. This is particular useful when you try to
design a slider widget of such a property, let's say, a alpha blending slider.
A S4 class name in R(< 2.15) and a generator function in R(>= 2.15)
Tengfei Yin
num1to100.gen <- setNumericWithRange(min = 1, max = 100) par.gen <- setRefClass("Graph", properties(list(size = "NumericWithMin1Max100"))) pars <- par.gen$new(size = new("NumericWithMin1Max100", 5)) pars$size #current value is 5 try(pars$size <- 300) # out of range error pars$size <- 10 #works ## Positive Integer par.gen <- setRefClass("PI", properties(list(size = "PositiveInteger"), list(size = PositiveInteger(2)))) obj <- par.gen$new() ## error try(obj$size <- -1) obj$size <- 3
num1to100.gen <- setNumericWithRange(min = 1, max = 100) par.gen <- setRefClass("Graph", properties(list(size = "NumericWithMin1Max100"))) pars <- par.gen$new(size = new("NumericWithMin1Max100", 5)) pars$size #current value is 5 try(pars$size <- 300) # out of range error pars$size <- 10 #works ## Positive Integer par.gen <- setRefClass("PI", properties(list(size = "PositiveInteger"), list(size = PositiveInteger(2)))) obj <- par.gen$new() ## error try(obj$size <- -1) obj$size <- 3