Introducing tparameter: parameters for any table

Have you ever found yourself wishing you that you could specify parameters for non-global variables in Codea? If so, you’re in luck. I’ve just released a new library for Codea that allows binding parameters to any table’s variables, not just _G. It works by creating proxy global variables for all of an object’s parameters, feeding those into Codea’s parameter.* functions, and updating those variables every frame to reflect the tracked variable’s value. tparameter usage should already feel familiar to you, as it is designed to be as similar as possible to Codea’s parameter class. However there are a couple of differences: you specify a name for the parameter (for example the class name of the object bound to the parameter, or some other identifying string), as well as the table that contains the variable you wish to parameterize. For example:


function MyClass:init()
    self.x = 10
    -- parameter for self.x
    tparameter.integer('MyClass', self, 'x', 0, 100)
end

tparameter tracks all of the tables that have specified parameters, and adds them to the parameter selector where they can be selected. To select, slide the selector slider to the parameters you wish to see, and hit the ‘SELECT’ button. When an object is selected, the parameter list reloads with that object’s parameters, so that you can tweak them.

The main features of tparameter are listed below.

##Metavariables

For all parameter types except for ‘action’ and ‘method’, when creating a tparameter, you can parameterize metavariables (i.e. variables of variables). For example, you could parameterize just the ‘r’ component of a color:


function MyClass:init()
    self.color = color()
    tparameter.integer('MyClass', self, 'color.r', 0, 255)
end

This is done by specifying an ‘index path’ to your metavariable. In the above example, ‘color.r’ is the index path to self.color.r. You can also specify array indices in your index path:


function MyClass:init()
    self.array = {'Codea', 'is', 'awesome'}
    tparameter.text('MyClass', self, 'array[2]')
end

If you need to specify a numeric string as an index, use the ‘.’ in your index path instead of []:


function MyClass:init()
    self.list = {}
    self.list['1'] = 10
    tparameter.integer('MyClass', self, 'list.1')

end

##New parameter types

In addition to the normal parameter types, tparameter also allows specifying new ‘pseudo-parameters’. These are:

method : like action, but calls a member function on an object instead of a regular function

itext, ntext : like ‘text’, but for strictly numeric values; converts entered values to integers/numbers.

##can be used with _G

Since you can’t really use Codea’s parameter class if you are using tparameter due to the way tparameter must frequently clear and reload the parameter list, tparameter provides special consideration when creating parameters for the _G table. Usage is straightforward:


function setup()
    myGlobalInt = 50
    tparameter.integer('Global', _G, 'myGlobalInt', 0, 100)
end

A parameter list will be created for _G with the name ‘Global’. Simply select this list in the parameter inspector, and you can modify your globals just like you would using Codea’s parameter class. When _G is specified as the table, the variables specified are truly global variables. In the above example, the parameter operates on the global variable ‘myGlobalInt’. Metavariables may be specified for parameters on _G.

##other features:

  • using tparameter.notify, you can specify that your object would like to know when it’s been highlight/selected in the inspector. You can use this to do things like modify the visual state of the highlighted/selected object, making it easier to tell exactly which object you are choosing.
  • all tparameters except ‘action’ and ‘method’ can specify optional callback functions when the variable values change (just like Codea’s parameters)
  • parameter lists for an object can have multiple names. This is especially useful when creating parameters for subclasses of classes that also create parameters. For example:
MyClass = class()

function MyClass:init()
    self.value1 = 10
    tparameter.integer('MyClass', self, 'value1', 0, 100)
end

MySubClass = class()

function MySubClass:init()
    MyClass.init(self)
    self.value2 = 20
    tparameter.integer('MySubClass', self, 'value2', 0, 100)
end

The parameter list for this object will now contain ids with the names “MyClass” and “MySubClass”. In the parameter inspector, the names of the parameter variables will be _MyClass_value1 and _MySubClass_value2 respectively.

A note about tparameter.watch

Currently there is a bug in Codea where ‘watch’ parameters in a long list of parameters will crash the app. As a workaround for now,
tparameter.watch actually uses a parameter.text, but modifying the text in the window will not change the watched variable’s value.

Example project containing source

An example Codea project containing the tparameter class (and it’s dependencies, ‘memoize’ and ‘Timer’) can be found here:

https://gist.github.com/apendley/5401761

I tried to flesh out the example to illustrate all of the features of tparameter, but I’m sure there are details that have slipped my mind. Feel free to ask me any questions about it, I’ll do my best to answer them.

@toadkick: you are a Lua Jedi Master!
I grabbed your project ( thanks for using a single file, thats much faster).
I corrected for the ‘black screen bug’, thanks to dave1707.
And it runs perfectly!
I am going to use your code in my windows library which has loads of settings! I way really annoyed about creating all these globals. But before i’ll have to rework your code (unless you do it…):

  • i will encapsulate everything as much as possible, specially the included libraries Timer an Memoize. I want no globals to be added by the code (if possible).
  • i already have a timer library, managing many timers, so i will try o use mine in your code: i dont know how difficult it will be…
    And there is one last thing: for me the feature ‘callback is fired at first pass’ is not a wanted feature. Could it be an option in your lib so we can disable it? That would great!

Fantastic job!
You are great!

Oh, some more: i was planning to replace codea parameters by a variant that does not open the side panel (this is really annoying when working fullscreen: everything is shifted or hidden). How difficult will it be starting from your lib?

Thanks @Jmv38, I really appreciate it! :slight_smile:

I can actually go ahead and modify the source myself to put memoize and Timer in the tparameter tab so that they are not exposed as a globals, however I am going to also leave Timer in a separate tab because the example uses it. I included them as a separate tab because I thought some may find it useful, but I’ll go ahead and post those in a different gist (actually I’ve already posted Timer). Replacing the timer should hopefully be straight forward. However, since I’m going to go ahead and put Timer as a local in the tparameter tab, you really don’t need to do it.

As for your other questions:

  1. I’ll see what I can do about the ‘callback is fired at first pass’. I am guessing the reason this happens is because I am setting an initial value when I call Codea’s parameter functions? If that’s the case, then it should be a pretty simple fix…I’ll modify the tparameter.* functions to accept an initial value, and I’ll only pass this to Codea’s parameter* functions if that value is not nil.
  2. tparameter already should not force the output window open, because it does not actually call Codea’s parameter function until you’ve actually selected your parameter list. In fact, you don’t even have to start with the output window open. Simply don’t call tparameter.show() in your setup function. You can call it from the command line window instead. Incidentally, tparameter() is a shortcut for tparameter.show(), so you can just type tparameter() in the window.

Thank you for your proposals!

  • encapsulating memoize would be great.
  • encapsulating Timers: maybe not. i just had a quick look at your code, and i see what i have removed from my lib: some fps-eater. I think you check each timer at each draw: this eats fps. I do only 1 check per draw whatever the number of timers, to save fps. But maybe i am wrongly understanding, or maybe you want to implement my spec?
    (i’ve more to say but my wife and daughter urge me to dinner… Back in 45 mn)…

Okay, I’ve updated the code to put memoize and a very minimal version of Timer as locals in tparameter. I think I’d prefer to keep the timer internal so it does not conflict with other code.

As far as not checking/updaing each timer every frame…how exactly do you pull that off? You must be doing some sort of check to see if each timer is active or something, and you must actually be updating their accumulators? At any rate, the 3 function calls to update the 3 timers used is not that much overhead, and should not be that much of an fps eater. I can add an additional check before calling the updates to see if the timers are actually active, but I think that’s about as optimal as it’s possible to make it, unless you know about some Lua voodoo that I don’t :wink:

Also, keep in mind that tparameter is meant to be a debugging/tweaking library…I’ve tried to optimize it as much as I can (and will continue to), but speed/efficiency is not the primary goal.

An update on the ‘callback is fired at first pass’ feature: turns out that when you provide a callback to Codea’s parameter.* functions (which tparameter has to do so that it can update your variable’s value from the proxy variable), it gets called automatically no matter what :frowning: There might be a workaround, but I’m hesitant to implement it yet without some consideration.

If you could explain to me how it’s causing issues for you it might help me figure out a way to implement an option for this (or a workaround for you at least) without having to do anything too invasive.

(back from dinner… Good thing my wife must drive my daughter to her zumba course…)
Thank you for your concern!
-timers: if you only have 3 timers that will be okay, dont try to improve it. My trick is simply, when a timer fires or when i add/remove a timer, to check what is the closest next ElapsedTime value for which i have to wakeup, and check only that one at each draw.A timer does not need an accumulator, it just need to know the time when it has to fire, which saves fps.

  • callback fires at first pass: this was just an opportunity to remove this. If it is complex, just let it there, i have a (ugly) workaround, i’ve posted that in the other thread.http://twolivesleft.com/Codea/Talk/discussion/comment/21106#Comment_21106
  • window open: i understand you do not force the panel to open. It is just i dont like this panel at all, and i want to implement stand alone settings (Cider style). We can discuss that later, it is not important at this stage. This Is something i’ll do in 2-3 months, if evething goes well…
  • on callback fires at first pass: EDIT: after seeing your example in the other thread I see exactly what you mean, and yes, that is actually quite easily doable. In fact, I think I’m going to make this the default…if I get feedback that people need that callback to be fired immediately then I’ll consider making it optional. Update forthcoming…
  • Ah, I see what you mean :slight_smile: Yeah, I made a few compromises in the implementation of tparameter, and one of those was piggybacking on Codea’s parameter system. Honestly though, my goal wasn’t to provide a slick UI, my goal was to extend functionality that Codea already provides, so it was kind of a natural fit.

That being said, it would be entirely possible to adapt this to use a completely different renderer (and in fact, it would be better, because we wouldn’t have to reload the entire parameter list everytime we needed to update the parameter that has the object selector. In fact, the object selector could be implemented entirely differently, say using a drop-down menu or some such).

Incidentally I’ve got a couple other projects going on, among them a module system and a UI framework (sorry, not trying to compete with Cider, it’s just that UI programming is one of my passions and I’ve always wanted to write a Cocoa-like MVC framework from the ground up). If I ever actually get the UI framework to a point where it’s not prototypical then I might one day revisit tparameter to use it. If not, Cider may very well be the best way to go, and I may be interested in pur

Great! I think the sole callback improvement will be a reason for everyone to switch to your library!
Btw, i’ve noticed some intability of codea when palying a lot with parameters (codea crahes). Do you think using your lib will improve that? (there is no reason for this, but who knows? Maybe you do a lot of collectgarbage()? )

I haven’t come across any stability issues, but what I have seen is that parameter.watch will crash codea if they are in a long list that is scrolled (I think this is actually a known issue, it’s on the bug tracker). I’ve actually worked around this by having tparameter create text parameters instead (but they don’t actually ever set your variable, just reflect it’s value).

Also, I’ve just updated so that your callbacks don’t get fired when the parameter is initialized. Let me know if that works for you.

EDIT: incidentally, it occurs to me that you could patch Codea’s parameters.* functions so that they don’t fire your callback immediately. Here’s a patched parameter.integer, I’ll leave it for someone else if they want to implement the others.


do
    local function _wrapCB(cb)
        if not cb then return end
        
        local _cb, first = cb, true
        cb = function(v)
            if first then first = nil; return end
            _cb(v)
        end
        
        return cb
    end
    
    local _integer = parameter.integer
    function parameter.integer(name, min, max, init, cb)
        _integer(name, min, max, init, _wrapCB(cb))
    end
    
    -- do the others here
end

@toadkick thank you, this is awesome!

Just downloaded the laest version: looks great. And everithing in included in the tparam tab, good job! I’ll give you some feedback when i have integrated it (not before a few days).

@toadkick this is an amazing library. Thank you for sharing it. It seems like something that should we should have built in to Codea.

@Simeon Thank you, that means a lot to me!

When I sit down to code in Codea I find it particularly fun to try to ameliorate Codea’s shortcomings by implementing solutions directly in Codea (Lua is fantastic for that!), and this one in particular was a ton of fun to work on. And, if it so happens that my experiments result in code that’s actually useful to people, that’s a total plus for me :slight_smile:

Update: fixed a couple of minor bugs, and added an example of creating a parameter list for a class’ defaults for the Ellipse class.

Hi @Toadkick.
Can you put a version number at the beginning of your code, to manage updates?
I have a question: in my lib i will have several class, that are globals. I would like to minimize this. I saw how you did that: you put all classes in the same tab and decalre them local (except one). This is excellent. But then i have to put it all in one tab, i want to keep tabs separated for maintenance. Any idea on how i could mask the classes and keep the tabs? Maybe i could define the class variables as fields of one global class? Mmmmm… Should work.

Ah yes, sorry about that. I’ll start right now. We’ll call this v1.0

@Jmv38: on minimizing globals: funny you should ask that, because I’ve actually been prototyping a Codea-centric module system for the last couple of months, and one of the (several) benefits of the system is that you must explicitly require your dependencies. It works like this:

-- BaseClass

-- import module from this project
local BaseClass = import('BaseClass')
local SubClass = class(BaseClass)

-- define your class

return SubClass

This really allows you to minimize your use of globals. Another benefit of this system is that only the code you actually use gets loaded, since you are explicitly loading it via the import() function, which can be a killer feature: this means you could put all of your frequently used libraries in a single project and just load the modules you need selectively, without a) having them pollute the global namespace, and b) take up memory even though they aren’t being used. Yet another benefit is that the code generated will be ever so slightly faster, because now all the references that would have otherwise been global references become local references, which produces slightly faster code.

Anyway, I had a major breakthrough today on a problem I’ve been mulling over for a few weeks that put me light years closer to where I want to be with it. I am considering releasing a preview version of it next week so I can solicit feedback for the final version (which won’t be released until Codea supports optional tab execution, because right now the system requires that the contents of your tabs be wrapped in a block comment so that Codea won’t execute the code automatically. Also, right now it only works within the Codea app, not exported projects, so I need to get that working). Interested?

I’m quite keen to see the code for actually loading the tabs. I did have a go with loadstring but couldn’t get it to work. It would be quite nice to remove the necessity of loading everything in my system (although I’ve yet to notice any appreciable slow-down on doing so so this is more for elegance than otherwise).

One minor thing, you both are talking a lot about local vs global and I don’t completely follow the reasoning. Here’s what I think (but I’m probably very wrong). Doing things like:

WFC = {}
local myclass = Class()
WFC.myclass = myclass

does not save on global look-ups except (perhaps) when myclass is being defined. When you want to access myclass later on, the code needs to look up WFC (a global look-up) and then find the key myclass within it. Now it might be that global look-ups get slower when more things are added to the top-level table so this could still be a useful speed-up, but I would guess that the point at which this becomes such is way beyond what we’re coding.

It’s still a good way to avoid name clashes, but it isn’t saving on local/global lookups.

I’m similarly curious about:

Yet another benefit is that the code generated will be ever so slightly faster, because now all the references that would have otherwise been global references become local references, which produces slightly faster code.

I think I’d need to see an example of how this works. Since tabs are lua chunks, surely this can be done in the same way without an import module. What I mean is, suppose we have a tab:

local base = class()

function base:init()
end

...

then base is local to that tab and doesn’t exist outside it. Now if we want to get at it outside we need to pass base out of the chunk. toadkick says that the import class “memoizes” its code from which I infer that it stores base somewhere that it can access it whenever it wants. This is presumably in some table, say import.imports. So we do import.imports.base = base. As the import module is global, this is effectively global. Thus base is now effectively global and we haven’t really saved any local/global stuff that we couldn’t have done before.
That is, if we’d made base global to begin with and then later just said local base = base if we wanted to make a local copy for speed reasons, then would this be any worse than the import method?

One problem I can see with wrapping tabs in block comments is that lua doesn’t like nested block comments. Is there a way around this?