Properties¶
validated, editor-visible node values.
▶ Run in browserTags: basics property inspector
A Property is a descriptor declared as a class attribute. It gives a node a
typed value that the editor inspector shows and the scene file saves, and you
read and write it as a plain attribute: bar.length = 250.0.
class Bar(Node2D):
length = Property(200.0, range=(0.0, 400.0), hint="Bar length in pixels")
range=(lo, hi)clamps every assignment to the nearest bound instead of raising, sobar.length = 460.0stores400.0. Passclamp=Falseto keep the range as an inspector hint only.on_change="method"names a method that runs after the stored value actually changes. Herehuerecomputes the bar’s colour.A Property is also a constructor keyword:
Bar(thickness=30.0).A node that draws its own Properties redraws when one changes, so
Barneeds no per-frame redraw flag.
Each frame the scene swings both bar lengths from -60 to 460. The range clamps them at 0 and 400, so the bars sit still at either end of the white outline instead of overflowing it, and the readout shows the stored value.
Source¶
1# /// simvx
2# tags = ["basics", "property", "inspector"]
3# web = { width = 800, height = 600, responsive = true }
4# ///
5"""Properties: validated, editor-visible node values.
6
7A `Property` is a descriptor declared as a class attribute. It gives a node a
8typed value that the editor inspector shows and the scene file saves, and you
9read and write it as a plain attribute: `bar.length = 250.0`.
10
11 class Bar(Node2D):
12 length = Property(200.0, range=(0.0, 400.0), hint="Bar length in pixels")
13
14- `range=(lo, hi)` clamps every assignment to the nearest bound instead of
15 raising, so `bar.length = 460.0` stores `400.0`. Pass `clamp=False` to keep
16 the range as an inspector hint only.
17- `on_change="method"` names a method that runs after the stored value
18 actually changes. Here `hue` recomputes the bar's colour.
19- A Property is also a constructor keyword: `Bar(thickness=30.0)`.
20- A node that draws its own Properties redraws when one changes, so `Bar`
21 needs no per-frame redraw flag.
22
23Each frame the scene swings both bar lengths from -60 to 460. The range
24clamps them at 0 and 400, so the bars sit still at either end of the white
25outline instead of overflowing it, and the readout shows the stored value.
26"""
27
28import colorsys
29import math
30
31from simvx.core import Anchor, CanvasLayer, Label, Node2D, Property
32from simvx.graphics import App
33
34
35class Bar(Node2D):
36 """A horizontal bar drawn entirely from its Properties."""
37
38 MAX_LENGTH = 400.0
39
40 length = Property(200.0, range=(0.0, MAX_LENGTH), hint="Bar length in pixels")
41 thickness = Property(40.0, range=(10.0, 80.0), hint="Bar thickness in pixels")
42 hue = Property(0.0, range=(0.0, 1.0), on_change="update_colour", hint="Bar hue, 0 to 1")
43
44 def on_ready(self):
45 self.update_colour() # on_change fires on a change, never for the default
46
47 def update_colour(self):
48 self.colour = (*colorsys.hsv_to_rgb(self.hue, 1.0, 1.0), 1.0)
49
50 def on_draw(self, renderer):
51 renderer.rect((0, 0), (self.length, self.thickness), fill=self.colour)
52 renderer.rect((0, 0), (self.MAX_LENGTH, self.thickness), stroke=(1, 1, 1, 0.25))
53
54
55class PropertiesDemo(Node2D):
56 def on_ready(self):
57 self.time = 0.0
58 self.bar_a = self.add_child(Bar())
59 self.bar_b = self.add_child(Bar(thickness=30.0))
60 hud = self.add_child(CanvasLayer())
61 self.readout = hud.add_child(Label(anchor=Anchor.TOP_LEFT, inset=16, font_size=18))
62
63 def on_update(self, dt):
64 self.time += dt
65 t = self.time
66 # 200 +/- 260 overshoots both ends of the 0..400 range; the Property clamps it.
67 self.bar_a.length = 200.0 + 260.0 * math.sin(t)
68 self.bar_a.hue = (t * 0.15) % 1.0
69 self.bar_b.length = 200.0 + 260.0 * math.sin(t * 0.7 + 1.0)
70 self.bar_b.hue = (t * 0.25 + 0.5) % 1.0
71
72 width, height = self.viewport_size
73 left = (width - Bar.MAX_LENGTH) / 2
74 self.bar_a.position = (left, height * 0.4)
75 self.bar_b.position = (left, height * 0.6)
76 self.readout.text = (
77 f"bar_a.length = {self.bar_a.length:5.1f} (range 0..400)\nbar_b.length = {self.bar_b.length:5.1f}"
78 )
79
80
81if __name__ == "__main__":
82 App(title="Properties", width=800, height=600).run(PropertiesDemo())