Death to helper.py, Long live SvgHelper

Sounds like the right decision not to have dynamic resizing, would bring a lot of complexity for little gain.

I have recently been trying to automate my module creation a bit better by hijacking the original helper.py script to also; add unit test stubs, add entries into readme.md, and produce the c code from template files, I shall be giving this a go in my next module.

1 Like

Yes! I’ve been thinking about even more streamlining of the workflow, and some kind of filesystem event would be even faster than having to manually click on a menu item. As soon as I copy a new SVG into my plugin folder, it should automatically reload it! I will research this next.

Follow-up: I looked at efsw for filesystem notifications. It looks like a wonderful library, especially for its cross-platform support, but it is a large amount of code to pull into a module for such a modest purpose.

There must be a more lightweight way to do what I want. Is there some other way an external program/script can fire a “trigger” that signals a VCV Rack module that it’s time to reload the SVG file? All I need is some kind of asynchronous event.

Perhaps my module can open a socket and listen on it.

As a very low-tech fallback, perhaps the existence of a flag file (e.g. “reload_enable.txt”) in the res folder at startup tells my module to create a thread to poll the SVG file’s date/time every second.

While I wouldn’t draw efsw into SvgHelper I really wouldn’t worry too much about it for your module. I’m using it in Prefabs. It’s 2023.

In the end you can use preprocessor directives to not compile this functionality into your releases.

Now I’m looking at using a pipe file created by the Linux command mkfifo. I can definitely use conditionally compiled code (#ifdef __linux__) because I do all my development from Linux anyway.

It’s super simple to do a blocking read from a thread in my plugin. Just open it like any other file and read from it. Then my Python script can regenerate the SVG file, then open the pipe file, write to it, and close it. That unblocks the pipe read and acts as an instant trigger.

I think the only thing that’s not clear to me is how to safely initiate the SVG reload from this separate thread. So now I’m diving through the Rack code trying to understand how it manages threads and communicates between them. If anyone can give a shortcut answer, I’d love that!

I think I answered my own question. It looks like I can have my background thread set a flag inside each interested ModuleWidget. Then I can override the step function, which is always called from the same thread that mouse/menu events are. There I can check the flag and trigger a reload. I just need to use some mutex locking to subscribe/unsubscribe for notifications from my background thread to avoid nasty race conditions.

Why not just have a mode to stat the file every 5 seconds in step and turn that on when you do dev builds? Like this is not a thing you need at release time right? So have an env var or file exists check and if it’s there just stat the file on the ui thread every 5 seconds. It’s not the audio thread so who cares!

Yeah, I gave up on the pipe thing because it was getting more complicated than it was worth. And yes, I’m going to use a flag file whose existence enables the feature. Dirt simple and good enough!

I thought about this, and I realized there is a reason I want manual control after all. I would like to have two instances of my module side by side, and reload only one of them, so I can do an A/B test to decide which of two alternatives I like better. I can’t see different versions simultaneously if they both automatically reload.

1 Like

I sometimes use Widget::step() as a cheap low priority worker thread. Andrew says that’s bad, because then your module won’t work headless. I never cared, but it’s a good point. don’t know if what you are proposing is similar.

Also, make very sure you never, ever try to lock a mutex on the process thread.

You don’t really need a mutex anyway. I would think std::atomic would do the job, with no worry about deadlocks, etc…

1 Like

In this case since this is a feature which only works with a ui using step as the worker thread is exactly appropriate.

Repaired the svg to relay out the widget not working in headless is correct :slight_smile:

1 Like

Right so another choice is set a book that starts polling per widget in a menu so you can toggle into poll mode and just always have it default to don’t. Then at least you can freeze versions as you go!

Good info, thanks! I will have to learn about std::atomic.

Headless mode would be fine with my approach because I still load the SVG as normal and the module is fully functional without being able to update the SVG later (which nobody can see anyway in headless mode).

I like this idea! Is there a convenient clock I can poll for elapsed seconds in my step function, or should I just use the C time function? Or even better, a timer callback I can register that runs on the UI thread?

There’s a clock which rack provides in seconds.

there’s a bit of code which shows you how I make single-click vs double-click both work as events in one case where I wanted both by using a little timer.

1 Like

The polling option is working very well. I like that it is off by default, but once I turn it on, I can settle in for a nice session of interactive development:

3 Likes

This is a tangent, but Inkscape can create the geometric shapes you have there.

Here’s a giff of how you can create the picture you want there:

[resize output image]

Steps are:

  1. Create a rectangle.
  2. Object to Path
  3. Edit Path by Nodes Tool
  4. Select Right two Notes
  5. Add a note between them.
  6. Click off to deselect nodes
  7. Select the new node
  8. Use the arrow keys to move it horizontally
  9. Copy Shape
  10. Enable Snapping
  11. “H” key to flip horizontally
  12. Place copy in position.
  13. Copy and paste shapes to make the remaining shapes.
2 Likes

I should have added. IF the code works for you. Go with it. But Inkscape CAN do it, and the visual feedback is nice :slight_smile:

Yes, I certainly would benefit from understanding Inkscape better. At the very least, I can use it to generate SVG files that I can then study, and figure out how to write code to generate SVG files that do similar things.

Trying to implement this code in a module and I am having some issues. I am totally new to VCV rack module development, so I know nothing about the inner workings of the code. This project is a way to learn this, and maybe get me into developing some modules. Because of this please phrase any replies as if I know nothing about VCV modules and even programming in C to be honest.

I do have quite a bit of experience writing software, and a bit of this is with various “C” languages. but just to be sure I do not miss something assume I know nothing and go from there.

When I try to use the make command in MSYS2 I am getting this error:

src/SvgHelper.hpp:73:10: error: 'optional' in namespace 'std' does not name a template type
   73 |     std::optional<Vec> findNamed(std::string name) {

what have I done wrong? I have the include at the top of my .cpp file:

#include "SvgHelper.hpp"

and the way I am trying to use it…

struct FreqCountWidget : ModuleWidget {
	FreqCountWidget(FreqCount* module) {
		setModule(module);
		loadPanel(asset::plugin(pluginInstance, "res/FreqCount.svg"));

		forEachPrefixed("input_", [&](int i, Vec pos) {
			addInput(createInputCentered<MyPort>(pos, module, MyModule::INPUT_1 + i));
			});
	}
};

the error is pointing to the SvgHelper.hpp file however, so I am not sure what is going on.

thanks for any help you can provide.

Long live helperA.py and long live macro raster grid nano SVG. :smiley: .

How do I load an auto background full size panel colour again?

:whale:

By the way … not sure… never used … unlikely to use.

EDIT: HP*CYAN, can’t be that hard.