Programmatic Panel Design

Is there anyway to programmatically lay out a panel instead of creating an SVG? I’d really like to skip the Inkscape step when I’m prototyping and would be more than happy to just code up the panel. For me, the panel design is really the last step I care about.

1 Like

I’ve looked at the code generated and I think I can just work with that. Sorry for asking obvious questions. :frowning:

Sure, you can subclass Widget, make a draw() method with calls to nanovg functions, put it in a FramebufferWidget, and add it as a child of your ModuleWidget. You’ll need to manually set sizes for each step.

In my opinion you would be much better off mocking up at least a basic sketch before starting something, you then have an idea of what you want to accomplish. At the very least make one blank panel and use this as a template for other modules, with enough room for components. You can just add things where needed. But doing so is a lot harder because you then have to design the panel and go back over the code to see what should be where. There would be a lot of back and forth with Inkscape and code when designing the panel at the end. Having the design first makes things a lot clearer for me anyway.

I tend to think of incremental functionality. Add an output, play with it, what do I need to add/change. I don’t have the experiential knowledge now that I’ll have after playing with it, so it’s really hard for me to anticipate what to design, if that makes sense?

But then I also haven’t experienced any pain that may come from trying to layout everything after I’ve finished the functionality.

That might be lower level than what I was after. I’m just wanting to fill in the parts that helper.py fills in for me from the SVG. At this point I’m just manually adding addOutput() calls, etc.

Yeah, just make a basic solid color panel and add components programmatically until your parameter list is stable. Then spend time designing a panel SVG and have it generate component positions.

In my (still very limited) experience, doing most of the widget placement with code, then giving your layout a final graphic design pass after the fact is a perfectly viable way to go about things, at least for me. I’m no longer using the the python script for my new plugins.

My Meander module panel is now 99.9% procedurally generated. I started out with an Inkscape SVG file and slowly migrated everything to nanovg calls in my module. Since I was porting an existing application, I had a fairly complete idea of the controls and UI for the panel. Fairly quickly it became too cumbersome to use helper.py to generate C++ code from the SVG fle. So, I began doing all parameters, inputs, outputs and lights manually in my module. There is a symmetry in which there is a conservation of effort whichever way you go. I had to add a lot of code to do almost all of the panel text via nanovg since the SVG route “bakes” text into a “path” which is a graphic representation that can be rendered once when the panel is created or at most as a framebuffer once per frame, I’m a bit unclear on that. Using nanovg, all of the text has to be rendered during each frame at run time.

Meander is more of a sequencer than a sound generator/modifier and as such, I was able to get away with a lot of nanovg drawing per frame, which DSP sample modifiers might not be able to get away with.

We all have our own styles of doing development. But, I’m a lot like you if I am starting a new software project. I consider my programming methodology scultping. I start with the absolute minimal functionality so that I can quickly have a functioning program or plugin. Then I incrementally increase the functionality and complexity in a strange here and there approach. It works for me, but surely not for everyone.

There may be a way to avoid rendering all nanovg stuff each frame by using frame buffers, but I have not explored that.

the time budget for drawing and the time budget for DSP processing come from different pools, so whether or not a module draws too slowly had nothing do to with how much processing it might do.

The obvious thing that happens if your drawing is crazy slow is that the UI or rack gets slow and jerky, just like you would expect.

Remember that there is one UI thread for the entire rack app, so ideally every module can draw one after the other and maintains 60 fps.

The unexpected thing is that doing too much drawing also heats up laptops, especially mac ones, and the CPU clock gets lowered to compensate (thermal throttling).

That’s why it’s always good to measure your drawing time. The standard way to make it faster is using the FramebufferWidget to cache unchanging pieces of the UI.

2 Likes

So the next question is what are the dimension of all the GUI components? Is this documented somewhere?

I work the same way and draw everything apart from controls at run time. The GUI components are whatever size you choose to make them. Have a look at: https://github.com/ContemporaryInsanity/RacketScience Specifically RS.hpp & RSWidgetDraw.cpp, still a work in progress.

I meant the base components, (inputs, outputs, etc) not custom components. (Hope I’m using the right terminology…)

Load them into inkscape, inspect the dimensions there.

For any widget, including param, input, output and light, you can use the box data member, which is a rack::math::Rect type, from which you can get the position and size. You can also then modify that position to be what you want.

Unfortunately changing the size does not seem to work, which is odd, considering the widgets are based on SVG images…

I know I’ve not done many modules but I spent a long time sketching the panels on paper, just trying to think through the interface. Just a personal thing, but I found it helped.

1 Like

This is a major help for me also. Even if the sketch looks like a 4 year old’s drawing it is still a representation that you do not have to remember as it is already on paper! Doing this also helps with getting a better GUI on the first go in Inkscape, you’ve already done the bad layout on paper.


to go slightly off topic: Just as a note to everyone the co-ordinates in Inskape v0.x are reversed for Y so in rack you would do (380px - inskapeYpx), or the mm (128.693mm - inskapeYmm). But this will not be the case in Inkscape v1.x which is fairly close to release. There is a beta available that is not fully featured but easier to get co-ordinates from. https://inkscape.org/release/inkscape-1.0beta2/

to go bring it back on topic: if you are to use nanovg to draw and you have labels etc the Y positions will still have to take Inkscape 0, 0 into account in either versions so positions for each box will be Y + labelHeight

2 Likes

Just did a quick XPath scan through the SVG files from res/ComponentLibrary in a clone of the main Rack repository to get the dimensions.

Not all are in millimetres. Some are points. Where there’s no suffix they are in pixels.

Filename Width Height
ADAT.svg 10.415202mm 10.667406mm
BefacoBigKnob.svg 26.000004mm 26.001146mm
BefacoPush_0.svg 28 28
BefacoPush_1.svg 28 28
BefacoSlidePot.svg 8.5913172 104
BefacoSwitch.svg 27.993454 31.564199
BefacoSwitch_0.svg 27.993454 31.564199
BefacoSwitch_1.svg 27.993454 31.564199
BefacoSwitch_2.svg 27.993454 31.564199
BefacoTinyKnob.svg 9.0000019mm 9.0000801mm
CKD6_0.svg 28 27.995878
CKD6_1.svg 28 27.995878
CKSS1.svg 14 20.641106
CKSS_0.svg 14 20.641106
CKSSThree_0.svg 4.7473302mm 10.000424mm
CKSSThree_1.svg 4.7473302mm 10.000424mm
CKSSThree_2.svg 4.7473302mm 10.000424mm
CL1362.svg 11.28851mm 10.148451mm
Davies1900hBlack.svg 36 36.001503
Davies1900hLargeBlack.svg 54 54.002251
Davies1900hLargeRed.svg 54 54.002399
Davies1900hLargeWhite.svg 54 54.002399
Davies1900hRed.svg 36.000004 36.001602
Davies1900hWhite.svg 36.000004 36.001602
LEDBezel.svg 7.5001564mm 7.5000095mm
LEDButton.svg 17.999601 18
LEDSlider.svg 7.00002mm 26.999905mm
LEDSliderBlueHandle.svg 1.52411mm 4.1437631mm
LEDSliderGreenHandle.svg 1.52411mm 4.1423831mm
LEDSliderHandle.svg 1.52414mm 4.1423802mm
LEDSliderHandleHorizontal.svg 4.1423802mm 1.52414mm
LEDSliderHorizontal.svg 27mm 7mm
LEDSliderRedHandle.svg 1.52414mm 4.1423802mm
LEDSliderWhiteHandle.svg 1.52411mm 4.1423841mm
LEDSliderYellowHandle.svg 1.52411mm 4.1423841mm
MIDI_DIN.svg 16.999479mm 17.000858mm
NKK.svg 31.999855 43.882507
NKK_0.svg 31.999855 43.882507
NKK_1.svg 31.999855 43.882507
NKK_2.svg 31.999855 43.882507
PB61303.svg 10.000438mm 10.000403mm
PJ301M.svg 8.3556204mm 8.3556299mm
PJ3410.svg 10.355158mm 10.35652mm
RackBusboard.svg 288pt 92.389pt
Rogan1PBlue.svg 11.076657mm 11.076675mm
Rogan1PGreen.svg 11.076657mm 11.076672mm
Rogan1PRed.svg 11.076657mm 11.076672mm
Rogan1PSBlue.svg 13.999492mm 13.999491mm
Rogan1PSGreen.svg 13.999492mm 13.999487mm
Rogan1PSRed.svg 13.999492mm 13.999487mm
Rogan1PSWhite.svg 13.999492mm 13.999491mm
Rogan1PWhite.svg 11.076657mm 11.076672mm
Rogan2PBlue.svg 12.097797mm 12.097801mm
Rogan2PGreen.svg 12.097797mm 12.097805mm
Rogan2PRed.svg 12.097797mm 12.097802mm
Rogan2PSBlue.svg 15.290711mm 15.292072mm
Rogan2PSGreen.svg 15.290711mm 15.292065mm
Rogan2PSRed.svg 15.290711mm 15.292068mm
Rogan2PSWhite.svg 15.290711mm 15.292067mm
Rogan2PWhite.svg 12.097797mm 12.0978mm
Rogan2SGray.svg 15.290711mm 15.29209mm
Rogan3PBlue.svg 14.732606mm 14.732597mm
Rogan3PGreen.svg 14.732606mm 14.732608mm
Rogan3PRed.svg 14.732606mm 14.732612mm
Rogan3PSBlue.svg 18.289322mm 18.287951mm
Rogan3PSGreen.svg 18.289322mm 18.287947mm
Rogan3PSRed.svg 18.289322mm 18.287951mm
Rogan3PSWhite.svg 18.289322mm 18.287943mm
Rogan3PWhite.svg 14.732606mm 14.732607mm
Rogan5PSGray.svg 20.999922mm 20.999929mm
Rogan6PSWhite.svg 31.437183mm 31.439936mm
RoundBlackKnob.svg 10.0004mm 10.000431mm
RoundHugeBlackKnob.svg 19.000362mm 19.000389mm
RoundLargeBlackKnob.svg 12.69999mm 12.7mm
RoundSmallBlackKnob.svg 7.9995098mm 7.9995222mm
ScrewBlack.svg 15 14.998887
ScrewSilver.svg 14.999999 14.9989
SynthTechAlco.svg 15.804726mm 15.803342mm
SynthTechAlco_cap.svg 15.804726mm 15.803342mm
TL1105_0.svg 5.4186664mm 5.4178686mm
TL1105_1.svg 5.4186664mm 5.4178686mm
Trimpot.svg 6.2990298mm 6.3003922mm
USB-B.svg 10.90027mm 12.199803mm
3 Likes