Lua basics

Moho allows users to write new tools and plugins. Discuss scripting ideas and problems here.

Moderators: Víctor Paredes, Belgarath, slowtiger

User avatar
rylleman
Posts: 750
Joined: Tue Feb 15, 2005 5:22 pm
Location: sweden
Contact:

Post by rylleman »

This seems to be a useful tool you have in the works here.
synthsin75 wrote:...I'll go with your idea of having just check boxes for 'button' or 'tool' and others for group ('draw', 'bone', 'fill', 'layer', 'camera', or 'workspace'). May not need 'camera', but i know there are some workspace buttons out there. ...
What if people have custom tool-categories? Perhaps your main target audience for this script doesn't but since it's easy to do I bet a lot of people have customized their toolbox.
Why not read the -tool_list for categories and present them in a drop-box for the user to choose from? Could even have an option to create new ones?

Another cool thing you could try to include in the script is giving the user the possibility to change the moho-strings file which would mean easy access to changing menu commands. (viewtopic.php?t=2001)
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Yeah, it'd be nice if luazip were in there. That way you could read zipped files. It'd help with this, since most scripts are downloaded as zipped files.
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Yeah rylleman, I plan to do that, but like Vern said, I should start out simple. I have thought about the 'strings' file, but I'm not sure how much is really useful to change in there.

A bit of code that would allow a script to write new strings may be useful though. :wink:
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

I am making some progress, but I have another non sequitur question. Are the paths to embedded scripts available to the scripting interface, or would I need to get them from the file itself?

I intend this script to eventually be a complete script management tool. So there will be an option to automatically copy embedded scripts to the current project folder. I always hate having to re-embed scripts after I've moved them, and I usually forget until I have twenty embedded layers.

They're easy enough to find in the file ('layer_script'), but finding specific layers that may be named the same could be trouble. I guess I'd have to order them when read and then write back in the same order.


-----------------------------------------------


But as to current progress... I was trying to organize what I need the script to do in order. I didn't realize the amount to error checking I'd need.

Vern, what script would have a good example of setting/retreiving script prefs? (That 'void' bit in the documentation always throws me.) The first thing I need to do is set a variable that can be checked when the tool is run. This would be the main path info. I need to check if the path variable is "" or nil, and if so, lm.gui.openfile. Can I do those in a single statement? (i.e. "if (path==""or nil)")

Then once the path is selected by the user I have to make sure it is the expected path (i.e. ...scripts\tool ). If it isn't I need an alert dialog which will retry the lm.gui.openfile on 'ok' or 'cancel'.

I'm thinking that this bit will need a for loop that is broken by either the expected path or the 'cancel' button.

Just rehashing my thinking here. Let me know if any of that sounds wrong. :wink:
User avatar
heyvern
Posts: 7035
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

First off, no. AS scripting has no access to "layer" scripts (that's what you mean by embedded? Correct?).

Layer scripts are only in the file format itself. AS has no access to them at all. The only way to find them is by parsing (reading) the document file itself.

-------

On script prefs:

This is the very top section of the LM translate ponts tool. The one that comes with AS not one of the modified tools. At the bottom you see the two functions that load and save the tools preference variables.

In this case it is saving what ever check boxes were selected so that the next time AS is opened this tool will have the check box values the same as when it was last opened.

Whenever you see "void" for a function it just means there is no return value. Sometimes you get a return value for instance when you "get" something it will return something, a string, an integer (0), a float (0.0) like that. When it says "void" it just means nothing is sent back or returned. You can't assign a variable the return value of a function that returns nothing, but you can set a variable to a function that does return a value so you can use it.

Setting script prefs doesn't return a value, getting script prefs returns the value for that key.


All this code is just the begging part of the tool script. Further below I've separated out the scrip prefs bit.

Code: Select all

-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************

ScriptName = "LM_TranslatePoints"

-- **************************************************
-- General information about this script
-- **************************************************

LM_TranslatePoints = {}

LM_TranslatePoints.BASE_STR = 2375

function LM_TranslatePoints:Name()
	return "Translate Points"
end

function LM_TranslatePoints:Version()
	return "5.0"
end

function LM_TranslatePoints:Description()
	return MOHO.Localize(self.BASE_STR, "Move selected points (hold <shift> to constrain, press <space> to weld, hold <alt> to disable auto-welding)")
end

function LM_TranslatePoints:Creator()
	return "Lost Marble"
end

function LM_TranslatePoints:UILabel()
	return(MOHO.Localize(self.BASE_STR + 1, "Translate Points"))
end
Here is where the script prefs are loaded and saved.

Code: Select all

function LM_TranslatePoints:LoadPrefs(prefs)
	self.autoWeld = prefs:GetBool("LM_TranslatePoints.autoWeld", true)
	self.autoFill = prefs:GetBool("LM_TranslatePoints.autoFill", false)
end

function LM_TranslatePoints:SavePrefs(prefs)
	prefs:SetBool("LM_TranslatePoints.autoWeld", self.autoWeld)
	prefs:SetBool("LM_TranslatePoints.autoFill", self.autoFill)
end

-- **************************************************
-- Recurring values
-- **************************************************
LM_TranslatePoints is the name of this tool. It is the "key" that is used to read in prefs. You use this so your prefs can't get mixed up with other tools.

prefs is the "inernal" AS table that holds the prefs values that were saved. So...

Code: Select all

function LM_TranslatePoints:LoadPrefs(prefs)
loads the "prefs" from AS that were saved for this tool the last time that AS was open.

Code: Select all

self.autoWeld = prefs:GetBool("LM_TranslatePoints.autoWeld", true)
.. gets the value that was saved for the "autoWeld" check box. "true" is just a default value if no prefs were saved. If that pref HAS a value that was saved then "true" is ignored. It's only used as a default.

Then the reverse happens for saving.

Code: Select all

prefs:SetBool("LM_TranslatePoints.autoWeld", self.autoWeld)
self.autoWeld is the name of the checkbox. That value is either true or false and is saved as a "bool" or boolean (true/false) value.

You can save, numbers, or strings as well as boolean values in the prefs. You should be able to save the path of the _tool_list.txt file but you may need to "escape" the characters. Not sure about that. It might not be necessary. Some of those "funky" characters like; /\: might cause trouble.

-vern
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Okay I now have an alert box explaining the following open file dialog, and this returns the path. If the path is already set, it doesn't ask for one. Right now this only works for the current session. I need to figure out how to verify this is the path I want, and if not, ask for it again. Then I need to provide that path to the script prefs so that it will be loaded for each session.

Any pointers for any of that? :wink:
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Okay you got that script prefs covered. Thanks for the detailed explaination on that and the 'void return'.

Any suggestions on repeating an 'if' statement under certain conditions? I keep thinking it should go after it, but it just occurred to me that I probably need a parent 'if' statement.

Like:

Code: Select all

if (string.sub(tlpath, -27) ~= 'scripts\\tool\\_tool_list.txt') then
LM.GUI.Alert(LM.GUI.ALERT_INFO, "Wrong file, please select again.", nil, nil, "OK", nil, nil)
elseif (tlpath == nil) then
  LM.GUI.Alert(LM.GUI.ALERT_INFO, "Syn_Addons needs to know where your tool list is located.", "Please navigate to '...Anime Studio\\scripts\\tool\\_tool_list.txt'", nil, "OK", nil, nil)
  
  local path = LM.GUI.OpenFile("Select ...scripts\\tool\\_tool_list.txt")
  tlpath = path
  if (path == "") then
    return
    end

  print(tlpath)
I just wrote in some of that here, I'll have to see if any of that will work when I get a chance. :wink:
User avatar
heyvern
Posts: 7035
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

In that "script prefs" stuff I posted you saw how the auto weld prefs was saved by using the true or false value of the check box.

If you want a preference to be saved with a specific value derived from a dialog, then use a global variable. Make that global variable the path value. Then in the prefs set the path preference to be the global variable.

I just made this up;

This variable would be created at any time during an AS "session".

Code: Select all

SYN_ToolLoader.ToolPath = "path to tool list"
when the prefs are saved, just use that variable.

Code: Select all

function SYN_ToolLoader:SavePrefs(prefs)
   prefs:SetStringl("SYN_ToolLoader.ToolPath", self.ToolPath)
end
Side note:
You use "self.ToolPath" because it's easier to type and means the same thing as "SYN_ToolLoader.ToolPath". You could actually call "SYN_ToolLoader.ToolPath" from a completely different tool or even a layer script and it would return the variable value from that tool. Everything is connected. All tools global variables and functions can be used by every other tool.

-vern
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Woohoo! I know this is probably very underwhelming and painfully slow progress, but I get thrilled with every little bit I get to work.

I have it so it will initially alert, requesting the tool list location, followed by the 'open' dialog. This path is then checked by a 'while' statement to verify it is the tool list. As long as it is not, it will continue to ask following any explainitory alert. Upon success, it prints this path.

I'm thinking I need to provide a 'cancel' on the reselection so no one gets stuck in this loop if for any reason they can't locate the tool list. So I need the reselect alert to be able to break the loop.

I know the alert can return 0, 1, or 2 depending on which button is pressed, but I'm not yet sure how I need to utilize that. My first guess would be to assign a variable to the alert so I could perform a operation test. (i.e. ~=, ==)

Hopefully my progress isn't boring the few here who have some idea of what I'm talking about. :wink:
User avatar
heyvern
Posts: 7035
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

Not boring in the least. I think it's important to see how you are progressing so I can help if I can. I have gone through much of what you are doing. I know that "thrill" you get when something works.

It is funny though that you chose an area to start with that I was terrified to attempt. It took me AGES to get up the courage so to speak, to tackle reading and writing files. It made me nervous.

----

The return value for the alert is used exactly for that purpose. You have an option for 3 buttons. Each button returns 0, 1, 2. If you make one of those buttons a "Cancel" button it will return a specific value (probably 1 or 2). You then use that value in an "if" function to cancel whatever is going on.

For example:

Code: Select all

local MyAlert = LM.GUI.Alert(2, "Eat My Shorts?", nil, nil, "OK", "No thanks", nil)
print(MyAlert)
-- prints 0 for OK, 1 for No thanks.

The code above creates the local variabel "MyAlert" and assigns it the return value of the button pressed (0 for "OK", 1 for "No thanks".)

Make sure you don't create a situation where you get caught in an endless loop. An "open file" dialog will return an empty string if cancel is pressed. I would "cancel" the whole thing if the path dialog box returns an empty string.

If the user picks the WRONG file and THEN hits cancel on the second alert (wiith OK or Cancel) the variable should be the number of the button pressed (cancel) and that can stop whatever while statement is checking the path name. Just use "return" or "break" in an "if/then" statement.

For example from the sample above this if statement could be inside a larger function...

Code: Select all

.....
       if(MyAlert == 1) then
           return -- or use "break"
       end
.....
return stops the whole thing dead in its tracks. Using break will only break out of the if statement but finishes any code after it.

p.s. You probably figured this out already but the moho script reference has a typo for the "alert" function. There is an extra "label2=nil" in the sample code.
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Yeah, I saw the typo, several in fact. And yeah, I've already caused several infinite loops testing this bit. Could only quit AS through the task manager.

I'm having some trouble with this. If I can't get it working tomorrow, or just get frustrated, I'll post it for you to look at.

Actually the only reason I got interested in the reading and parsing is because you hadn't exploited it yet. It'll be quite awhile before I'm doing the sort of parsing gymnastics you're up to. :wink:
User avatar
heyvern
Posts: 7035
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

synthsin75 wrote:Yeah, I saw the typo, several in fact. And yeah, I've already caused several infinite loops testing this bit. Could only quit AS through the task manager.
Ouch! I've been there. I don't do it very much now. I'm more careful when designing my loops. You have to make sure there is "a way out". Read the code through logically. Each line in your head. Most times you can see the infinite loop.

My initial concern with what you describe is the two different dialog boxes and when the variables or return values are declared. If they happen "out of sync" or maybe if one variable is defined AFTER the loop starts it won't stop it in time.

If the alert pops up and you hit OK and then ANOTHER alert pops up immediately... is the first one declaring the variable? Or does it have to wait for the second alert to finish before the variable is declared? That could cause an infinite loop if a variable is "nil" or undeclared when it should contain a value.

p.s. You have to be VERY careful with "while" loops. Especially if you do those "while true" loops. They will go on forever unless you intentionally put in a break or return based on some value that WILL be false to break the loop. While loops are the ones that bite me every once in a while.

-vern
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Latest success here!!

Whew! Finally got that bit working well. You can now cancel out at any step, and it won't accept anything but the tool list path.

One of my problems was that I had put some of it in a function (AssignTLP) so I couldn't read it very easily. I was trying to keep the code fairly clean (without a lot of repetitive stuff), but I should probably wait until I'm done to do the clean up, huh?

This code is probably ugly as hell. There's a ton of testing stuff commented out, and I assigned tlpath as nil so ctrl-F5 would reset the script. Once you've selected the right path, this bit won't run again unless you ctrl-F5.

Let me know if you have any suggestions for simplifying this code. I'm sure I'll end up assigning the repetitive bits to functions again later.



So now I can strip this path down and assign it as my base path for the tool pref. Since I know the file structure of the AS folder, this base system path is all I need. I think everything else will be asked for every time. I'm not real sure I want to 'set' a default 'get' location for the unzipped script downloads. If I did, I'd have to include an option to change it, and after what I just went through I think I'll avoid too much complex user UIs for the moment. :wink:


p.s. Let me know if any of the alert messages seem unclear. Thanks.
User avatar
heyvern
Posts: 7035
Joined: Fri Sep 02, 2005 4:49 am

Post by heyvern »

synthsin75 wrote:
This code is probably ugly as hell..
I've given up apologizing for my ugly code. ;)

My fear here is that my lack of proper programming training will result in "the blind leading the blind". Since there isn't anyone else trying to teach "lua in AS" I'm taking it on the best I can... just be aware I am probably not the best resource for good programming practices. :oops:

One thing I have always been bad at is COMMENTING the code. Writing in little descriptions to give a hint at what is going on. If you don't do this then 6 months down the road you have no clue how your script works. I hate that when I do it. ;)

My use of "weird" variable names is really really bad. A variable or function name should give a clue to what it does. When in a hurry I may use a funky name and plan to change it later but I often forget to.

-----

I will check out your script and see if there is anything I can help with.

-vern
User avatar
synthsin75
Posts: 9981
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Post by synthsin75 »

Hey even if it's a case of the blind leading the blind, at least if I trip I'm taking someone with me. :lol:

Yeah I'm really trying to use meaningful variables, and I plan to comment this well. But like you, I may or may not get around to that.

As always, any input is appreciated.
Post Reply