Import code and add info to readme
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
		
							parent
							
								
									b7ed8b6f36
								
							
						
					
					
						commit
						9153be2272
					
				
							
								
								
									
										43
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								README.md
									
									
									
									
									
								
							@ -1,2 +1,43 @@
 | 
			
		||||
# nanogui-experiments
 | 
			
		||||
# NanoGUI Experiments
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
## Quickstart
 | 
			
		||||
 | 
			
		||||
```con
 | 
			
		||||
python3 -m venv venv
 | 
			
		||||
source venv/bin/activate
 | 
			
		||||
(venv) pip install -r requirenments.txt
 | 
			
		||||
(venv) python3 nanogui_helloworld.py
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Knobs Example
 | 
			
		||||
 | 
			
		||||
```con
 | 
			
		||||
(venv) python3 nanogui_knob.py
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Key and Mouse Bindings
 | 
			
		||||
 | 
			
		||||
|                                           |                      |                         |
 | 
			
		||||
| ----------------------------------------- | -------------------- |------------------------ |
 | 
			
		||||
| Mouse click-and-drag knob                 | coarse in-/decrement | +/- one 100th / pixel   |
 | 
			
		||||
| Shift + mouse click-and-drag knob         | fine in-/decrement   | +/- 0.1 / pixel         |
 | 
			
		||||
| Mouse wheel scroll over knob              | coarse in-/decrement | one 100th / click * 2.0 |
 | 
			
		||||
| Shift + mouse wheel scroll over knob      | fine in-/decrement   | +/- 0.1 / click         |
 | 
			
		||||
| Mouse wheel scroll over value entry       | fine in-/decrement   | +/- 0.1                 |
 | 
			
		||||
| Ctrl + left click on knob                 | set to default value |                         |
 | 
			
		||||
| Up/down key while mouse over knob         | coarse in-/decrement | +/- one 100th           |
 | 
			
		||||
| Shift + up/down key while mouse over knob | fine in-/decrement   | +/- 0.1                 |
 | 
			
		||||
| Page up/down key while mouse over knob    | fine in-/decrement   | +/- one 10th            |
 | 
			
		||||
| Mouse click on value entry up/down arrow  | fine in-/decrement   | +/- 0.1                 |
 | 
			
		||||
| Return/Enter while editing value entry    | accept & set value   |                         |
 | 
			
		||||
| Escape key                                | quit                 |                         |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Notes
 | 
			
		||||
 | 
			
		||||
* No support for scaling or re-layouting on window resize yet.
 | 
			
		||||
* Only the knob value gauge color can currently be specified on instantation,
 | 
			
		||||
  the knob gradient and outline colors are currently hard-coded.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										247
									
								
								knob.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								knob.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,247 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import math
 | 
			
		||||
 | 
			
		||||
from nanogui import Color, Cursor, FloatBox, Vector2f, Vector2i, Widget, glfw
 | 
			
		||||
from nanogui.nanovg import BUTT, RGB, RGBA, RGBAf, LerpRGBA, NVGwinding, RGBAf
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Knob(Widget):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        parent,
 | 
			
		||||
        label,
 | 
			
		||||
        default=0.0,
 | 
			
		||||
        min_val=0.0,
 | 
			
		||||
        max_val=127.0,
 | 
			
		||||
        unit="%",
 | 
			
		||||
        increment=0.1,
 | 
			
		||||
        size=80,
 | 
			
		||||
        color=RGBA(192, 192, 192, 255),
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__(parent)
 | 
			
		||||
        self.label = label
 | 
			
		||||
 | 
			
		||||
        # sizes
 | 
			
		||||
        self._size = size
 | 
			
		||||
        # value gauge width as ratio of knob radius
 | 
			
		||||
        self.gauge_width = 0.125
 | 
			
		||||
        # value indicator line length as ratio of knob radius
 | 
			
		||||
        self.indicator_size = 0.35
 | 
			
		||||
 | 
			
		||||
        # value and range
 | 
			
		||||
        self.value = default
 | 
			
		||||
        self.min_val = min_val
 | 
			
		||||
        self.max_val = max_val
 | 
			
		||||
        self.default = default
 | 
			
		||||
        self.unit = unit
 | 
			
		||||
        self.increment = increment
 | 
			
		||||
 | 
			
		||||
        # behaviour
 | 
			
		||||
        self.fine_mode = False
 | 
			
		||||
        self.scroll_speed = 2.0
 | 
			
		||||
        self.scroll_increment = (self.max_val - self.min_val) / 100.0 * self.scroll_speed
 | 
			
		||||
        self.drag_increment = (self.max_val - self.min_val) / 100.0
 | 
			
		||||
        self.callback = None
 | 
			
		||||
        self.set_cursor(Cursor.Hand)
 | 
			
		||||
 | 
			
		||||
        # colors
 | 
			
		||||
        self.color = color
 | 
			
		||||
        self.knob_color_1 = RGB(86, 92, 95)
 | 
			
		||||
        self.knob_color_2 = RGB(39, 42, 43)
 | 
			
		||||
        self.outline_color_1 = RGBA(190, 190, 190, 0.0)
 | 
			
		||||
        self.outline_color_2 = RGB(23, 23, 23)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def color(self):
 | 
			
		||||
        return self._color
 | 
			
		||||
 | 
			
		||||
    @color.setter
 | 
			
		||||
    def color(self, col):
 | 
			
		||||
        if isinstance(col, Color):
 | 
			
		||||
            col = RGBAf(col.r, col.g, col.b, col.w)
 | 
			
		||||
 | 
			
		||||
        self._color = col
 | 
			
		||||
        self.gauge_color = LerpRGBA(RGB(40, 40, 40), col, 0.3)
 | 
			
		||||
 | 
			
		||||
    def preferred_size(self, ctx):
 | 
			
		||||
        return Vector2i(self._size, self._size)
 | 
			
		||||
 | 
			
		||||
    def _adjust_value(self, val, inc):
 | 
			
		||||
        if self.fine_mode:
 | 
			
		||||
            inc = self.increment
 | 
			
		||||
 | 
			
		||||
        new_val = self.value + val * inc
 | 
			
		||||
        new_val = max(self.min_val, min(self.max_val, new_val))
 | 
			
		||||
 | 
			
		||||
        if new_val != self.value:
 | 
			
		||||
            self.value = new_val
 | 
			
		||||
 | 
			
		||||
            if self.callback:
 | 
			
		||||
                self.callback(new_val)
 | 
			
		||||
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def mouse_enter_event(self, pos: Vector2i, enter: bool):
 | 
			
		||||
        if enter:
 | 
			
		||||
            self.request_focus()
 | 
			
		||||
 | 
			
		||||
        self.set_focused(enter)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def mouse_button_event(self, pos: Vector2i, button: int, down: bool, modifiers: int):
 | 
			
		||||
        if button == glfw.MOUSE_BUTTON_1 and modifiers == glfw.MOD_CONTROL and down:
 | 
			
		||||
            if self.value != self.default:
 | 
			
		||||
                self.value = self.default
 | 
			
		||||
 | 
			
		||||
                if self.callback:
 | 
			
		||||
                    self.callback(self.value)
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def mouse_drag_event(self, p: Vector2i, rel: Vector2i, button: int, modifiers: int):
 | 
			
		||||
        self._adjust_value(-rel[1], self.drag_increment)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def scroll_event(self, p: Vector2i, rel: Vector2f):
 | 
			
		||||
        self._adjust_value(rel[1], self.scroll_increment)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def keyboard_event(self, key, scancode, action, modifiers):
 | 
			
		||||
        if key in (glfw.KEY_LEFT_SHIFT, glfw.KEY_RIGHT_SHIFT):
 | 
			
		||||
            if action == glfw.PRESS:
 | 
			
		||||
                self.fine_mode = True
 | 
			
		||||
            elif action == glfw.RELEASE:
 | 
			
		||||
                self.fine_mode = False
 | 
			
		||||
            return True
 | 
			
		||||
        elif action in (glfw.PRESS, glfw.REPEAT):
 | 
			
		||||
            if key == glfw.KEY_UP:
 | 
			
		||||
                self._adjust_value(1, self.drag_increment)
 | 
			
		||||
                return True
 | 
			
		||||
            elif key == glfw.KEY_DOWN:
 | 
			
		||||
                self._adjust_value(-1, self.drag_increment)
 | 
			
		||||
                return True
 | 
			
		||||
            elif key == glfw.KEY_PAGE_UP:
 | 
			
		||||
                self._adjust_value(10, self.drag_increment)
 | 
			
		||||
                return True
 | 
			
		||||
            elif key == glfw.KEY_PAGE_DOWN:
 | 
			
		||||
                self._adjust_value(-10, self.drag_increment)
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def set_callback(self, callback):
 | 
			
		||||
        if callable(callback):
 | 
			
		||||
            self.callback = callback
 | 
			
		||||
        else:
 | 
			
		||||
            raise TypeError("Not a callable.")
 | 
			
		||||
 | 
			
		||||
    def set_value(self, value, trigger_callback=False):
 | 
			
		||||
        new_val = max(self.min_val, min(self.max_val, value))
 | 
			
		||||
        if new_val != self.value:
 | 
			
		||||
            self.value = new_val
 | 
			
		||||
 | 
			
		||||
            if trigger_callback and self.callback:
 | 
			
		||||
                self.callback(value)
 | 
			
		||||
 | 
			
		||||
    def create_value_entry(self, parent, editable=True, font_size=20, widget=FloatBox, **kwargs):
 | 
			
		||||
        entry = widget(parent, **kwargs)
 | 
			
		||||
        entry.set_size((int(self._size * 1.1), int(font_size * 1.2)))
 | 
			
		||||
        entry.set_font_size(font_size)
 | 
			
		||||
        entry.set_editable(editable)
 | 
			
		||||
        entry.set_spinnable(True)
 | 
			
		||||
        entry.set_value(self.value)
 | 
			
		||||
        # entry.number_format("%02.2f")  # method missing in Python bindings
 | 
			
		||||
        entry.set_min_value(self.min_val)
 | 
			
		||||
        entry.set_max_value(self.max_val)
 | 
			
		||||
        entry.set_value_increment(self.increment)
 | 
			
		||||
 | 
			
		||||
        if self.unit:
 | 
			
		||||
            entry.set_units(self.unit)
 | 
			
		||||
 | 
			
		||||
        self.set_callback(entry.set_value)
 | 
			
		||||
        entry.set_callback(self.set_value)
 | 
			
		||||
 | 
			
		||||
        return entry
 | 
			
		||||
 | 
			
		||||
    def draw(self, ctx):
 | 
			
		||||
        pos = self.position()
 | 
			
		||||
        size = self.size()
 | 
			
		||||
        height = float(size[1])
 | 
			
		||||
        radius = height / 2.0
 | 
			
		||||
 | 
			
		||||
        gauge_width = radius * self.gauge_width
 | 
			
		||||
        margin = gauge_width / 2.0
 | 
			
		||||
        percent_filled = (self.value - self.min_val) / (self.max_val - self.min_val)
 | 
			
		||||
        knob_diameter = (radius - gauge_width) * 2.0 - margin
 | 
			
		||||
        indicator_length = radius * self.indicator_size
 | 
			
		||||
 | 
			
		||||
        ctx.Save()
 | 
			
		||||
        ctx.Translate(pos[0], pos[1])
 | 
			
		||||
 | 
			
		||||
        # Gauge (background)
 | 
			
		||||
        ctx.BeginPath()
 | 
			
		||||
 | 
			
		||||
        ctx.StrokeWidth(gauge_width)
 | 
			
		||||
        ctx.StrokeColor(self.gauge_color)
 | 
			
		||||
        ctx.LineCap(BUTT.ROUND)
 | 
			
		||||
        ctx.Arc(radius, radius, radius - margin, 0.75 * math.pi, 0.25 * math.pi, NVGwinding.CW)
 | 
			
		||||
        ctx.Stroke()
 | 
			
		||||
 | 
			
		||||
        # Gauge (fill)
 | 
			
		||||
        ctx.BeginPath()
 | 
			
		||||
 | 
			
		||||
        ctx.StrokeWidth(gauge_width)
 | 
			
		||||
        ctx.StrokeColor(self.color)
 | 
			
		||||
        ctx.LineCap(BUTT.ROUND)
 | 
			
		||||
        ctx.Arc(
 | 
			
		||||
            radius,
 | 
			
		||||
            radius,
 | 
			
		||||
            radius - margin,
 | 
			
		||||
            0.75 * math.pi,
 | 
			
		||||
            (0.75 + 1.5 * percent_filled) * math.pi,
 | 
			
		||||
            NVGwinding.CW,
 | 
			
		||||
        )
 | 
			
		||||
        ctx.Stroke()
 | 
			
		||||
 | 
			
		||||
        # Knob
 | 
			
		||||
        ctx.BeginPath()
 | 
			
		||||
 | 
			
		||||
        ctx.StrokeWidth(2.0)
 | 
			
		||||
        outline_paint = ctx.LinearGradient(
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            height - 10,
 | 
			
		||||
            self.outline_color_1,
 | 
			
		||||
            self.outline_color_2,
 | 
			
		||||
        )
 | 
			
		||||
        ctx.StrokePaint(outline_paint)
 | 
			
		||||
 | 
			
		||||
        knob_paint = ctx.LinearGradient(
 | 
			
		||||
            radius, gauge_width, radius, knob_diameter, self.knob_color_1, self.knob_color_2
 | 
			
		||||
        )
 | 
			
		||||
        ctx.FillPaint(knob_paint)
 | 
			
		||||
 | 
			
		||||
        ctx.Circle(radius, radius, knob_diameter / 2.0)
 | 
			
		||||
        ctx.Fill()
 | 
			
		||||
        ctx.Stroke()
 | 
			
		||||
 | 
			
		||||
        # Indicator
 | 
			
		||||
        ctx.BeginPath()
 | 
			
		||||
 | 
			
		||||
        ctx.Translate(radius, radius)
 | 
			
		||||
        ctx.Rotate((2.0 + ((percent_filled - 0.5) * 1.5)) * math.pi)
 | 
			
		||||
 | 
			
		||||
        ctx.StrokeColor(self.color)
 | 
			
		||||
        ctx.StrokeWidth(gauge_width)
 | 
			
		||||
        ctx.LineCap(BUTT.ROUND)
 | 
			
		||||
        indicator_start = radius - margin - indicator_length
 | 
			
		||||
        ctx.MoveTo(0, -indicator_start)
 | 
			
		||||
        ctx.LineTo(0, -(indicator_start + indicator_length))
 | 
			
		||||
        ctx.Stroke()
 | 
			
		||||
 | 
			
		||||
        ctx.Restore()
 | 
			
		||||
        ctx.ClosePath()
 | 
			
		||||
							
								
								
									
										78
									
								
								nanogui_customwidget.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								nanogui_customwidget.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import nanogui
 | 
			
		||||
from nanogui import glfw
 | 
			
		||||
from nanogui import BoxLayout, Color, Label, Orientation, Screen, Widget, Window, Vector2i
 | 
			
		||||
from nanogui.nanovg import RGBAf, RGB
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CustomWidget(Widget):
 | 
			
		||||
    def __init__(self, parent, size=(100, 100), color=Color(192, 0, 0, 255)):
 | 
			
		||||
        super().__init__(parent)
 | 
			
		||||
        self._size = size
 | 
			
		||||
        self._color = RGBAf(color.r, color.g, color.b, color.w)
 | 
			
		||||
 | 
			
		||||
    def preferred_size(self, ctx):
 | 
			
		||||
        return Vector2i(self._size)
 | 
			
		||||
 | 
			
		||||
    def draw(self, ctx):
 | 
			
		||||
        pos = self.position()
 | 
			
		||||
        size = self.size()
 | 
			
		||||
 | 
			
		||||
        ctx.BeginPath()
 | 
			
		||||
        ctx.Rect(pos[0], pos[1], size[0], size[1])
 | 
			
		||||
        ctx.FillColor(RGB(128, 128, 0))
 | 
			
		||||
        ctx.Fill()
 | 
			
		||||
        ctx.BeginPath()
 | 
			
		||||
        ctx.StrokeWidth(5.0)
 | 
			
		||||
        ctx.StrokeColor(self._color)
 | 
			
		||||
        ctx.MoveTo(pos[0], pos[1])
 | 
			
		||||
        ctx.LineTo(pos[0] + size[0], pos[1] + size[1] / 3.0)
 | 
			
		||||
        ctx.Stroke()
 | 
			
		||||
        ctx.ClosePath()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CustomWidgetApp(Screen):
 | 
			
		||||
    def __init__(self, size=(400, 300)):
 | 
			
		||||
        super().__init__(size, "NanoGUI CustomWidget")
 | 
			
		||||
        self.win = Window(self, "Demo Window")
 | 
			
		||||
 | 
			
		||||
        self.win.set_layout(BoxLayout(Orientation.Vertical, margin=20, spacing=20))
 | 
			
		||||
        self.resize_event(size)
 | 
			
		||||
 | 
			
		||||
        Label(self.win, "NanoGUI CustomWidget Demo", "sans-bold")
 | 
			
		||||
 | 
			
		||||
        cw = CustomWidget(self.win, (200, 100))
 | 
			
		||||
 | 
			
		||||
        self.draw_all()
 | 
			
		||||
        self.set_visible(True)
 | 
			
		||||
        self.perform_layout()
 | 
			
		||||
 | 
			
		||||
    def resize_event(self, size):
 | 
			
		||||
        self.win.set_fixed_size(size)
 | 
			
		||||
        self.win.set_size(size)
 | 
			
		||||
        self.win.center()
 | 
			
		||||
        self.win.perform_layout(self.nvg_context())
 | 
			
		||||
        super().resize_event(size)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def keyboard_event(self, key, scancode, action, modifiers):
 | 
			
		||||
        if super().keyboard_event(key, scancode, action, modifiers):
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
 | 
			
		||||
            self.set_visible(False)
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    import gc
 | 
			
		||||
 | 
			
		||||
    nanogui.init()
 | 
			
		||||
    app = CustomWidgetApp()
 | 
			
		||||
    nanogui.mainloop(refresh=1 / 60.0 * 1000)
 | 
			
		||||
    del app
 | 
			
		||||
    gc.collect()
 | 
			
		||||
    nanogui.shutdown()
 | 
			
		||||
							
								
								
									
										54
									
								
								nanogui_helloworld.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								nanogui_helloworld.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import nanogui
 | 
			
		||||
from nanogui import glfw
 | 
			
		||||
from nanogui import BoxLayout, Button, Label, Orientation, Screen, Window
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HelloWorldApp(Screen):
 | 
			
		||||
    def __init__(self, size=(400, 300)):
 | 
			
		||||
        super().__init__(size, "NanoGUI Hello World")
 | 
			
		||||
        self.win = Window(self, "Demo Window")
 | 
			
		||||
 | 
			
		||||
        self.win.set_layout(BoxLayout(Orientation.Vertical, margin=20, spacing=20))
 | 
			
		||||
        self.resize_event(size)
 | 
			
		||||
 | 
			
		||||
        Label(self.win, "NanoGUI Demo", "sans-bold")
 | 
			
		||||
        b = Button(self.win, "Say hello!")
 | 
			
		||||
 | 
			
		||||
        def cb():
 | 
			
		||||
            print("Well, 'ello there!")
 | 
			
		||||
 | 
			
		||||
        b.set_callback(cb)
 | 
			
		||||
 | 
			
		||||
        self.set_visible(True)
 | 
			
		||||
        self.perform_layout()
 | 
			
		||||
 | 
			
		||||
    def resize_event(self, size):
 | 
			
		||||
        self.win.set_fixed_size(size)
 | 
			
		||||
        self.win.set_size(size)
 | 
			
		||||
        self.win.center()
 | 
			
		||||
        self.win.perform_layout(self.nvg_context())
 | 
			
		||||
        super().resize_event(size)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def keyboard_event(self, key, scancode, action, modifiers):
 | 
			
		||||
        if super().keyboard_event(key, scancode, action, modifiers):
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
 | 
			
		||||
            self.set_visible(False)
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    import gc
 | 
			
		||||
 | 
			
		||||
    nanogui.init()
 | 
			
		||||
    app = HelloWorldApp()
 | 
			
		||||
    nanogui.mainloop(refresh=1 / 60.0 * 1000)
 | 
			
		||||
    del app
 | 
			
		||||
    gc.collect()
 | 
			
		||||
    nanogui.shutdown()
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								nanogui_knobs.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								nanogui_knobs.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 22 KiB  | 
							
								
								
									
										82
									
								
								nanogui_knobs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								nanogui_knobs.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
from random import randint, shuffle, uniform
 | 
			
		||||
 | 
			
		||||
import nanogui
 | 
			
		||||
from nanogui import BoxLayout, Color, Label, Orientation, Screen, Widget, Window, glfw
 | 
			
		||||
from nanogui.nanovg import RGB
 | 
			
		||||
 | 
			
		||||
from knob import Knob
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class KnobsApp(Screen):
 | 
			
		||||
    def __init__(self, size=(450, 250)):
 | 
			
		||||
        super().__init__(size, "NanoGUI Knobs")
 | 
			
		||||
        self.set_background(Color(96, 96, 96, 255))
 | 
			
		||||
        self.win = Window(self, "Envelope")
 | 
			
		||||
        self.win.set_layout(BoxLayout(Orientation.Horizontal, margin=20, spacing=20))
 | 
			
		||||
        self.resize_event(size)
 | 
			
		||||
 | 
			
		||||
        knobs = (
 | 
			
		||||
            ("Attack", 0.0, 0.0, 5.0, "s", 0.01),
 | 
			
		||||
            ("Decay", 0.0, 0.0, 5.0, "s", 0.01),
 | 
			
		||||
            ("Sustain", 0.0, 0.0, 100.0, "%", 0.01),
 | 
			
		||||
            ("Release", 0.0, 0.0, 5.0, "s", 0.01),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.knobs = []
 | 
			
		||||
        self.entries = []
 | 
			
		||||
        self.labels = []
 | 
			
		||||
 | 
			
		||||
        for i, knobspec in enumerate(knobs):
 | 
			
		||||
            box = Widget(self.win)
 | 
			
		||||
            box.set_layout(BoxLayout(Orientation.Vertical, spacing=10))
 | 
			
		||||
 | 
			
		||||
            rgb = [128 + randint(0, 127), randint(0, 127), 64]
 | 
			
		||||
            shuffle(rgb)
 | 
			
		||||
            knob = Knob(box, *knobspec)
 | 
			
		||||
            knob.color = RGB(*rgb)
 | 
			
		||||
            knob.value = round(uniform(knob.min_val, knob.max_val), 2)
 | 
			
		||||
            self.knobs.append(knob)
 | 
			
		||||
            self.entries.append(knob.create_value_entry(box))
 | 
			
		||||
            self.labels.append(Label(box, knob.label, font="sans", font_size=20))
 | 
			
		||||
 | 
			
		||||
        self.draw_all()
 | 
			
		||||
        self.set_visible(True)
 | 
			
		||||
        self.perform_layout()
 | 
			
		||||
 | 
			
		||||
    def resize_event(self, size):
 | 
			
		||||
        self.win.set_fixed_size(size)
 | 
			
		||||
        self.win.set_size(size)
 | 
			
		||||
        self.win.center()
 | 
			
		||||
        self.win.perform_layout(self.nvg_context())
 | 
			
		||||
        super().resize_event(size)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def keyboard_event(self, key, scancode, action, modifiers):
 | 
			
		||||
        if super().keyboard_event(key, scancode, action, modifiers):
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
 | 
			
		||||
            self.set_visible(False)
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
        # callbacks referencing other widgets cause memory leakage at cleanup
 | 
			
		||||
        for entry in self.entries:
 | 
			
		||||
            entry.set_callback(lambda x: True)
 | 
			
		||||
        for entry in self.knobs:
 | 
			
		||||
            entry.set_callback(lambda x: True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    import gc
 | 
			
		||||
 | 
			
		||||
    nanogui.init()
 | 
			
		||||
    app = KnobsApp()
 | 
			
		||||
    nanogui.mainloop(refresh=1 / 60.0 * 1000)
 | 
			
		||||
    del app
 | 
			
		||||
    gc.collect()
 | 
			
		||||
    nanogui.shutdown()
 | 
			
		||||
							
								
								
									
										1
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
nanogui==0.2.0
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user