WhiteMagic / JoystickGremlin

A tool for configuring and managing joystick devices.
http://whitemagic.github.io/JoystickGremlin/
GNU General Public License v3.0
313 stars 46 forks source link

Allow dynamic creation/destruction of Variable objects (and associated UI element) in user plugin #391

Closed ian-bertolacci closed 2 years ago

ian-bertolacci commented 2 years ago

It'd be nice to be able to dynamically create Variables in a user plugin. For example, I might use a static IntegerVariable that allows the user to choose a number of PhysicalButtons to use as input, and when the IntegerVariable's value changes, it adds/removes PhysicalButton variables from existence.

Currently this is not possible, because the UI creates the UI elements for each Variable object by statically looking at the plugin's module namespace, and picking out instances that extend AbstractVariable.

Adding variable's to the module's namespace dynamically does not work, since the UI has already analyzed the module and collected the Variable labels.

Although, maybe I hsould be using this variable_registry instance, and registering variables against it?

ian-bertolacci commented 2 years ago

This is about as far as I got when I realized it wouldn't work:

import gremlin
from gremlin.user_plugin import *

num_buttons_var = IntegerVariable(
    "Number of buttons to check",
    "Number of buttons to check",
    initial_value = 2,
    min_value = 1
)

virtual_button_var = VirtualInputVariable(
        "Logical Output",
        "vJoy button to use as the output",
        [gremlin.common.InputType.JoystickButton]
)

class Listner:
    def __init__(this):
        this.buttons = []

    def getListner(this):
        return lambda *args, **kwargs: this.onChange(*args, **kwargs)

    def _build_button(id):
        return \
            PhysicalInputVariable(
                f"Physical Input {id}",
                "Button to check.",
                [gremlin.common.InputType.JoystickButton]
            )

    def onChange(this, *args, **kwargs):
        value = args[0]
        new_num_buttons = value["value"]
        current_num_buttons = len(this.buttons)
        if new_num_buttons < current_num_buttons:
            del this.buttons[new_num_buttons:]
        elif new_num_buttons > current_num_buttons:
            this.buttons[current_num_buttons:]  = (Listner._build_button(id) for id in range(current_num_buttons, new_num_buttons))

        # TODO: Set buttons in UI?

num_buttons_var.value_changed.connect(Listner().getListner())
WhiteMagic commented 2 years ago

This is not possible with how the whole variable thing works, nor am I sure it would technically be doable based on the dodgy nature of the variable system. On top of that, I'm not sure what the use of such a functionality would be, as these additional variables would be of no use to the code that relies on them, as such code wouldn't exist.