I just extended the development experience with hot reloading for the draw function of the widgets. Now you can prototype your nanovg code very quick. I probably end up integrating this two tools.
For this I used GitHub - mitsuba-renderer/nanogui: Minimalistic C++/Python GUI library for OpenGL, GLES2/3, Metal, and WebAssembly/WebGL and a package called reloadr that hot swaps functions or classes on save. This approach should be fairly save here, because I extracted the draw function, and just calling it on every redraw. So swapping this function out shouldn’t show side effects as long as you just draw and don’t do nasty stuff with it.
What does it mean? Quite easy: You change your code, save it (or let auto save do it, like in my case) and you get an instant change/draw of your code. I think this will very usefull, for scatching/testing/prototyping.
Maybe this is something we could even integrate into rack. I’ll have a look at whether I can create a plugin, that runs a python runtime and integrate it with rack. Maybe a custom build/fork … it that’s something Andrew can agree with. Let’s see how far I can get it.
Could be a good extension, or companion to VCV - Prototype. Or maybe Prototype could have a second modul with nanovg bindings to let you draw your own UI in Rack, that this would solutions would be obsolete.
Maybe it even would allow performant code in Combination with cython to prototype/develop full blown prototypes with the same or nearly the same performance (to be verified). The Performance of Python, Cython and C on a Vector — Cython def, cdef and cpdef functions 0.1.0 documentation
I don’t know how far I’ll dig into this c/python cython thing, but if there will be some result I’ll let you know.
Now for the setup:
git clone git@github.com:mitsuba-renderer/nanogui.git
cd nanogui
pipenv install --editable .
pipenv install reloader
I’m using pipenv because I like to keep my env clean. But feel free to use pip or directly install into your system with pip and without any virutalenv.
Here is the rack test chamber:
import gc
import nanogui
from nanogui import Color, Screen, Widget, nanovg
from nanogui.nanovg import RGB, RGBA, NVGsolidity
from reloadr import autoreload
RACK_GRID_WIDTH = 15
RACK_GRID_HEIGHT = 380
def draw_rail(ctx, size, theme):
hole_radius = 4.0
rail_height = 15
ctx.BeginPath()
ctx.Rect(0.0, 0.0, 1000, 1000)
ctx.FillColor(RGB(*theme['background']))
ctx.Fill()
for y in range(0, size.y, RACK_GRID_HEIGHT):
ctx.FillColor(RGB(*theme['rail']['fill']))
ctx.StrokeWidth(1.0)
ctx.StrokeColor(RGB(*theme['rail']['stroke']))
# Top rail
ctx.BeginPath()
ctx.Rect(0, y, size.x, rail_height)
for x in range(0, size.x, RACK_GRID_WIDTH):
ctx.Circle(x + RACK_GRID_WIDTH / 2, y + rail_height / 2, hole_radius)
ctx.PathWinding(NVGsolidity.HOLE)
ctx.Fill()
ctx.BeginPath()
ctx.MoveTo(0, y + rail_height )
ctx.LineTo(size.x, y + rail_height)
ctx.Stroke()
# Bottom rail
ctx.BeginPath()
ctx.Rect(0, y + RACK_GRID_HEIGHT - rail_height, size.x, rail_height)
for x in range(0, size.x, RACK_GRID_WIDTH):
ctx.Circle(x + RACK_GRID_WIDTH / 2, y + RACK_GRID_HEIGHT - rail_height + rail_height / 2, hole_radius)
ctx.PathWinding(NVGsolidity.HOLE)
ctx.Fill()
ctx.BeginPath()
ctx.MoveTo(0, y + RACK_GRID_HEIGHT - 0.5)
ctx.LineTo(size.x, y + RACK_GRID_HEIGHT - 0.5)
ctx.Stroke()
@autoreload
def draw(rack, ctx):
try:
ctx.Scale(2,2)
ctx.Translate(0,-RACK_GRID_HEIGHT/1.25)
size=rack.size()
light = {
"background": (0x30, 0x30, 0x30),
"rail": {
"fill": (0xc9, 0xc9, 0xc9),
"stroke": (0x9d, 0x9f, 0xa2)
}
}
dark = {
'background': (0x13, 0x13, 0x13),
'rail': {
"fill": (0x25, 0x25, 0x25),
"stroke": (0x00, 0x00, 0x00)
}
}
theme = light
draw_rail(ctx, size, theme)
# Panel
rack_units = 10
margin=2
ctx.BeginPath()
ctx.Rect(RACK_GRID_WIDTH*margin, RACK_GRID_HEIGHT, RACK_GRID_WIDTH*rack_units, RACK_GRID_HEIGHT)
ctx.FillColor(RGBA(0xff,0x05,0x17,0x40))
ctx.Fill()
except expression:
pass
class Rack(Screen):
def draw(self, ctx):
draw(self, ctx)
if __name__ == "__main__":
nanogui.init()
# create a fixed size screen with one window
rack = Rack((800, 600), "Rack Test Chamber", resizable=True)
rack.set_background(Color(0x13, 0x13, 0x13, 0xff))
rack.draw_all()
rack.set_visible(True)
nanogui.mainloop(refresh=1 / 60.0 * 1000)
del rack
gc.collect()
nanogui.shutdown()
Run the script: python rack.py
and change the draw.
UPDATE: Ported the rack rail code and added some exception handling so the client doesn’t crash instantly when having a syntax error. Colors are adapted for my dark theme, but original theme is available. But feel free to use the original values