Script Recipes

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

Moderators: Víctor Paredes, Belgarath, slowtiger

User avatar
strider2000
Posts: 506
Joined: Sat Mar 07, 2015 5:14 pm
Contact:

Script Recipes

Post by strider2000 »

I'm wondering, especially with recent changes in Moho, if others would find it valuable to have a collection of scripting recipes? I've started doing that on my own, because it helps me remember how to do common things.

As an example, I've been wanting to rename styles. The reason is that I start with a common rig for creating characters, but I'd like their styles to be unique, because even if you unlink shared styles they have the same names, so it can be hard to find which style matches which character. The problem is that I've never been able to get Moho:RenameStyle to work. So my recipe for that would be

Rename Style
Rename the first style to "lulu". Shapes using the first style will be updated so that the new name is displayed.

Code: Select all

  local firstStyle = self.moho.document:StyleByID(0)
  if firstStyle ~= nil then
    firstStyle.fName:Set("lulu")
  end
Another example might be

Process top level layers
CoutLayers enumerates the top level layers. This code allows you to run through the top level layers.

Code: Select all

  for i = 0, self.moho.document:CountLayers()-1 do
    local layer = self.moho.document:LayerByAbsoluteID(i)
    -- do some work here
  end
Last edited by strider2000 on Fri Feb 24, 2017 2:54 pm, edited 1 time in total.
User avatar
synthsin75
Posts: 9935
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Script Recipes

Post by synthsin75 »

Those don't seem generalized enough for reference purposes. self.moho will only work once you've assigned it moho, and is not necessary outside of dialogs. Your style renaming only renames the first style. moho.document:CountLayers() only gets the top level layers. These being so specific to your own needs may not make them good learning examples.

I have a collection of these "recipes" too. Many of them would not be easy to categorize in a useful, searchable format. Here's a few recent ones:

Code: Select all

--set project to image dimensions
	if (moho.layer:LayerType() == MOHO.LT_IMAGE) then
		local image = moho:LayerAsImage(moho.layer)
		local width = image:PixelWidth()
		local height = image:PixelHeight()
		moho.document:SetShape(width, height)
		local source = image:SourceImage()
		image:SetSourceImage(source)
	end

Code: Select all

--cache layers
	local sel = moho.document:CountSelectedLayers()-1
	for i=0, sel do
		local layer = moho.document:GetSelectedLayer(i)
		moho:SetSelLayer(layer)
		print(layer:Name())
	end

Code: Select all

--counting through animation channels and select all keys on frame
	local chanCnt = moho.layer:CountChannels()-1
	local ch = MOHO.MohoLayerChannel:new_local()
	for i=0, chanCnt do
		moho.layer:GetChannelInfo(i, ch)
		--moho.layer:GetChannelInfo(2, ch)--point curvature is ID 2
		--print(i.." - "..ch.name:Buffer())
		--print(ch.subChannelCount)
		for j=0, ch.subChannelCount-1 do
			local chan = moho.layer:Channel(i, j, moho.document)
			if (moho.frame > 0) and (chan:HasKey(moho.frame)) then
				--chan:DeleteKey(moho.frame)
				chan:SetKeySelected(moho.frame, true)
			end
		end
	end

Code: Select all

--set selected images to not warp with bones
	local sel = moho.document:CountSelectedLayers()-1
	for i=0, sel do
		local layer = moho.document:GetSelectedLayer(i)
		if (layer:LayerType() == MOHO.LT_IMAGE) then
			layer:SetLayerParentBone(-1)
		end
	end

Code: Select all

--moving keyframes
	local frame = moho.frame
	local ch = moho.layer:Channel(2, 1, moho.document)

	if (ch:HasKey(frame)) then
		local ID = ch:GetClosestKeyID(frame)
		ch:SetKeyWhen(ID, frame+10)
	end
User avatar
hayasidist
Posts: 3492
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Script Recipes

Post by hayasidist »

Wes, these would be a useful addition to http://mohoscripting.com/ -- we just need a category for them...
User avatar
strider2000
Posts: 506
Joined: Sat Mar 07, 2015 5:14 pm
Contact:

Re: Script Recipes

Post by strider2000 »

synthsin75 wrote:Those don't seem generalized enough for reference purposes.
There's a pretty big distinction between a recipe and a fully reusable function or module. The intention of a recipe is not to provide code that you can cut and paste without modification, or use in a library, but to provide code that you can
  • easily understand without a larger context
  • easily modify to meet your specific needs
  • see an example with reasonable parameters and easy to follow context
For example, even writing this post I had to try several things to get the "list" to work. I don't do it often and it's clear that it somehow involves the list button at the top, but it wasn't clear how they should go together. So if I provide a recipe like:

Create a bullet list
Create a bullet list for the moho forum

Code: Select all

[list]
[*]easily understand without a larger context
[*]easily modify to meet your specific needs
[*]see an example of actual code calls 
[/list]
it makes it very easy for any new user to know exactly how to use the list syntax.

It is true that the assumption is that the user can actually read the code and understand what's going on, just like you did. However, if there are questions, members of the forum can answer and the answers are keep long range. Thus, it helps many people understand (kind of like Stack Overflow). The header and description are intended to make it easy to find and understand.
hayasidist wrote:Wes, these would be a useful addition to http://mohoscripting.com/ -- we just need a category for them...
That would be great. The scripting link is a great! And I fully expect that anyone wanting to do scripting _has_ to be familiar with that. If there is a way to contribute there I'd be willing, but I don't know how easy it is to allow others to add recipes. If it is it would be great to link them to actual functions. Ideally, each function would have an example usage, but I wouldn't want to add burden to the maintainers of the doc. I'm extremely grateful that it's there! This is an easy solution where everyone can contribute. Plus, if you wanted, someone could cut and paste the best from here into the doc. This would be a good QA location and that could keep the doc cleaner. Just a thought.

The rename style is a perfect example. If anyone else wanted to rename their styles they could easily do it with my recipe. I still have absolutely no idea how it could be done with MohoDoc:RenameStyle :( I spent several hours trying unsuccessfully. I'm trying to help others avoid losing several hours like I did, plus remind myself if I forget or just want to snip the code. However, if someone does now how to properly use MohoDoc:RenameStyle, I'd love to see a recipe like mine on how to do it. That's another value; finding alternative ways to do things.
synthsin75 wrote:I have a collection of these "recipes" too.
Thanks for sharing these :)
synthsin75 wrote:Many of them would not be easy to categorize in a useful, searchable format.
For mine, I was counting on using large heading to easily find recipes via quick scan or search. I also planed to put a simple description below the header and before the recipe to let people know what it's doing and what any assumptions are.
User avatar
strider2000
Posts: 506
Joined: Sat Mar 07, 2015 5:14 pm
Contact:

Re: Script Recipes

Post by strider2000 »

Just updated the title for the Process layers, to Process top level layers. See feedback helps :)
User avatar
synthsin75
Posts: 9935
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Script Recipes

Post by synthsin75 »

hayasidist wrote:Wes, these would be a useful addition to http://mohoscripting.com/ -- we just need a category for them...
Yeah, every time I try to think of where they would naturally fit in the documentation, preferably without repeating in multiple places and somewhere logical to find, I fail entirely. I have no idea where even I would expect to find most these. Would it be at all useful to have a rather unordered collection of these? Maybe diligent scripters would read through it so they'd have some idea where they may have seen some code they need, when the need arises? I don't know.

Maybe it would need a tag system of some sort, so each example could be tagged to multiple items in the documentation?

We'd need to get Stan's ideas on this.
Last edited by synthsin75 on Fri Feb 24, 2017 6:21 pm, edited 1 time in total.
User avatar
synthsin75
Posts: 9935
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Script Recipes

Post by synthsin75 »

strider2000 wrote:
synthsin75 wrote:Those don't seem generalized enough for reference purposes.
There's a pretty big distinction between a recipe and a fully reusable function or module. The intention of a recipe is not to provide code that you can cut and paste without modification, or use in a library, but to provide code that you can
  • easily understand without a larger context
  • easily modify to meet your specific needs
  • see an example with reasonable parameters and easy to follow context
The problem is that relatively new scripters won't know why "self.moho" doesn't work...unless your recipes include "self.moho = moho".
For mine, I was counting on using large heading to easily find recipes via quick scan or search. I also planed to put a simple description below the header and before the recipe to let people know what it's doing and what any assumptions are.
For me personally, I don't always have a lot of time to explain the workings of every code snippet I may post, so maybe that's why I'd prefer to post more generalized and ready-to-use examples. But if you use stuff like "self.moho" a description of how that works would be necessary, IMO. Otherwise the examples could cause unnecessary confusion...and there's already a pretty steep learning curve to the Moho API.
Last edited by synthsin75 on Sun Mar 05, 2017 7:39 pm, edited 1 time in total.
Stan
Posts: 199
Joined: Sun Apr 19, 2009 3:22 pm

Re: Script Recipes

Post by Stan »

Yeah, I was thinking of having a library of code snippets on mohoscripting.com for a long time. Eventually I'll create some system to keep and sort them, that will be a great addition to the site. I'm just too busy now with my daily job, but If you guys have any ideas, please let me know.

And here's my 2 cents. Every now and then I have a situation when I need to iterate through all the layers in the project, regardless of the hierarchy. So, I came up with a universal iterator, should be very fast, because it doesn't use recursion:

Code: Select all

	local count = 0
	repeat
		local layer = moho.document:LayerByAbsoluteID(count)
		if layer then
			count = count + 1
			-- do whatever you like with the layer, for example print the name:
			print(layer:Name())
		end
	until not layer	
	print("Total number of layers in the project: " .. count)
________________________________________________________________________
https://mohoscripting.com/ - Unofficial Moho Lua scripting documentation
https://mohoscripts.com/ - The best place to publish and download scripts for Moho
User avatar
synthsin75
Posts: 9935
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Script Recipes

Post by synthsin75 »

Nice one, Stan. I'm always forgetting about LayerByAbsoluteID.
User avatar
hayasidist
Posts: 3492
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Script Recipes

Post by hayasidist »

HI Stan -- in the other forum we've gathered script standard routines http://www.kelleytown.com/forum/animato ... IC_ID=3426 - the ones that Moho calls rather than the API as currently described. This would maybe be a good sub-category in that proposed segment when your time permits that to get set up??
User avatar
strider2000
Posts: 506
Joined: Sat Mar 07, 2015 5:14 pm
Contact:

Re: Script Recipes

Post by strider2000 »

synthsin75 wrote:The problem is that relatively new scripters won't know why "self.moho" doesn't work
My idea of recipes are definitely not tutorials. They're more common cut, paste and morph code snippets that I use frequently and am willing to share with others. Also, I think that's one of the advantages of the forum format. Other formats are wonderful if someone can create them, but here, if someone doesn't know about self.moho they can ask, and if this is a kind of thing that's of interest the original author doesn't have to be the one that answers the question. In the worst case, the reader is at least exposed to new things and sees them in a context. They can go play.

Headers are really useful
synthsin75 wrote:For me personally, I don't always have a lot of time to explain the workings of every code snippet I may post
I don't think any contributor should have to explain the workings of their code snippet. They're generous enough just to share and we all have real lives :) However, I think a header above each snippet is extremely helpful. I do that for my Current Approaches thread viewtopic.php?f=9&t=28849 If people haven't read the threads they can still quickly scroll through the pages and easily see the different headers. If they actually have read a post, they may not need it at the time, but later they may think, "I know I saw something about <whatever> in that thread." Then they can search for the word at the top of the thread and the search will look through all the pages, but will be limited to the thread. I even do that for my own posts sometimes in Current Approaches. There may not be a lot of other contributors there, but there have been over 6000 reads in just over a year, so hopefully it's helping some people.

So ... if you guys could, I think it would be helpful to add headers above snippets. But even if you don't I'm learning things already from the things you've shared. So thank you :)

A single sentence or two is nice
I think a header is most important, but a single sentence or two, just below the snippet is nice. But again, I'm not talking explanations. It's up to the reader to do the homework.
Stan wrote:If you guys have any ideas, please let me know.
Github is a public place where code can be shared. I have an account there https://github.com/Strider22/AnimeStudioScripts, but it's typically about full applications/scripts. Hypothetically, there could be files by category and people could add there. But ... that's not for the uninitiated.

Stack Overflow http://stackoverflow.com/ is a site that's specifically for Q & A related to code. People could add stuff there. Don't know if sorting and categorization are as easy, but snippets could be added by asking and answering questions "How do I iterate without recursion?" :)

I'm also thinking of adding a blog to my new site http://strider2000.wixsite.com/familyfunanimation. I'm brand new to creating my own website so I don't know much about them, but I have seen people's blogs where categorization and tags helped find things. In my case I'm thinking of manually creating an index if there isn't some automatic way, since I do best with an index.

Anyway, those are just some ideas. If I/we create any such things we could always link to it from here.
Stan wrote:I came up with a universal iterator
Ok, so there's the "Process all layers" recipe :) Thanks for sharing Stan! (Stan if you missed it, I posted a recipe that processed just top level layers and I mislabeled it as "Process all layers" :oops: I've corrected that in an edit :)

For drilling down I typically do a recursion, but often don't have to drill down into all layers. Have you actually done a timing test to show that your iterator is noticeably faster than recursion. That would be of interest to me. Yours is certainly a nice approach if you have to hit all layers, but if you can top at some top level layers I think a recursion may be faster. But that's just an interesting thought beyond your generosity of sharing the script and I can go play if I really want :) By the way does anyone know of how to do timings for a question like that?
hayasidist wrote:we've gathered script standard routines http://www.kelleytown.com/forum/animato ... IC_ID=3426
Thanks for sharing hayasidist! Man there's so much information everywhere that it's just hard to keep up. That's really helpful.
User avatar
strider2000
Posts: 506
Joined: Sat Mar 07, 2015 5:14 pm
Contact:

Re: Script Recipes

Post by strider2000 »

Drill into groups
Uses recursion to find all switch layers by drilling into group layers

Code: Select all

function msPrintSwitchLayers:PrintSwitchLayers(layer)
	if layer:IsGroupType() then
		local group = self.moho:LayerAsGroup(layer)
		for i = 0, group:CountLayers()-1 do
			local sublayer = group:Layer(i)
			self:PrintSwitchLayers(sublayer)
		end
	end
	-- print the switch layer name
	-- putting this at the end prints children
	-- before parent
	if  layer:LayerType() == MOHO.LT_SWITCH then
		print(layer:Name())
	end
end

function msPrintSwitchLayers:ProcessTopLayers()
	for i = 0, self.moho.document:CountLayers()-1 do
		local layer = self.moho.document:LayerByAbsoluteID(i)
		self:PrintSwitchLayers(layer)
	end
end
https://www.codeproject.com/Articles/32 ... ade-simple

Random number
Generate a random integer between 0 and numLayers-1

Code: Select all

math.random(numLayers) - 1
Activate a switch layer
Activate a switch layer for frame 0.

Code: Select all

        local group = self.moho:LayerAsGroup(layer)
	local switchLayer = self.moho:LayerAsSwitch(layer)
	local switch = switchLayer:SwitchValues()

	switch:SetValue(0, group:Layer(randomlySelectedLayer):Name())
Combine recipes
Combine the three recipes above and you can create a script that randomly selects switch layers. That can be a nice way to play with character design. You can also check the name of the switch layers to control which switch layers you allow to be randomly selected.
User avatar
synthsin75
Posts: 9935
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Script Recipes

Post by synthsin75 »

strider2000 wrote:
synthsin75 wrote:The problem is that relatively new scripters won't know why "self.moho" doesn't work
My idea of recipes are definitely not tutorials. They're more common cut, paste and morph code snippets that I use frequently and am willing to share with others. Also, I think that's one of the advantages of the forum format. Other formats are wonderful if someone can create them, but here, if someone doesn't know about self.moho they can ask, and if this is a kind of thing that's of interest the original author doesn't have to be the one that answers the question. In the worst case, the reader is at least exposed to new things and sees them in a context. They can go play.
Then as to your question, "...if others would find it valuable to have a collection of scripting recipes?", I'd have to say it's not as valuable as it could be. "Common cut, paste and morph code snippets" should be generalized. Using things like "self.moho" means they are not cut & paste for anyone but you (and would not fly as answers on Stack Overflow). It would take you less time to remove the "self." than to make your headings. If every contributor posts with unexplained and unneeded idiosyncrasies, it greatly reduces the value of this as a reference or learning resource.

The Moho API is hard enough to learn without adding unnecessary hurdles.
User avatar
strider2000
Posts: 506
Joined: Sat Mar 07, 2015 5:14 pm
Contact:

Re: Script Recipes

Post by strider2000 »

strider2000 wrote:"Common cut, paste and morph code snippets" should be generalized. Using things like "self.moho" means they are not cut & paste for anyone but you (and would not fly as answers on Stack Overflow).
That's fine as your opinion, but I don't agree. I have yet to find anything on Stack Overflow that I could use without modification and using self.moho is not just my way.
strider2000 wrote: If every contributor posts with unexplained and unneeded idiosyncrasies, it greatly reduces the value
I agree with that, but I cannot see how using "self" is in any way idiosyncratic. It is common Lua OO practice https://www.lua.org/pil/16.html In addition, self is used all over the place in the scripts that come with moho. It is true that much of the existing code passes moho around as a parameter, but that could be avoided by simply assigning to self in the Run method. The whole point of self it so optimize parameterization in the OO framework. From the lua.org doc link above:
This use of a self parameter is a central point in any object-oriented language. Most OO languages have this mechanism partly hidden from the programmer, so that she does not have to declare this parameter (although she still can use the name self or this inside a method). Lua can also hide this parameter, using the colon operator. We can rewrite the previous method definition as

function Account:withdraw (v)
self.balance = self.balance - v
end
I'm not trying to argue for an OO approach, just saying self.moho is not idiosyncratic, not just my approach, and not without basis in the existing scripts.

I'll assume that I'm just reading things with an incorrect tone, but your responses seem a bit critical to me. I'm not sure why that is, because I'm not trying to tell anyone how they need to do things, I was just offering to provide things that might help others. If I have said or done something to cause a problem, I certainly didn't intend to. I know that you know a great deal about Moho scripting, so I can see how none of the recipes I listed would be helpful to you.

In any case, I'm am more than happy to recognize that the things that I consider valuable and helpful are not what is valuable and helpful to others. I see that there is interest in sharing code, but since no one has really followed up to say that posting recipes here is a great idea, I won't post them here any more.

Since I think it's an interesting project, I've posted them to my website, so if there are any later readers that have an interest then can look there
http://strider2000.wixsite.com/familyfu ... ho-Recipes It may fit better to put something on github (I already have full scripts there), but for now I'm just putting them to the website.

Thanks to all for the feedback.
User avatar
synthsin75
Posts: 9935
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Script Recipes

Post by synthsin75 »

strider2000 wrote:That's fine as your opinion, but I don't agree. I have yet to find anything on Stack Overflow that I could use without modification and using self.moho is not just my way.

I'm not trying to argue for an OO approach, just saying self.moho is not idiosyncratic, not just my approach, and not without basis in the existing scripts.

I'll assume that I'm just reading things with an incorrect tone, but your responses seem a bit critical to me. I'm not sure why that is, because I'm not trying to tell anyone how they need to do things, I was just offering to provide things that might help others. If I have said or done something to cause a problem, I certainly didn't intend to. I know that you know a great deal about Moho scripting, so I can see how none of the recipes I listed would be helpful to you.
I've already said that if you insist on using self.moho, which is idiosyncratic compared to stock scripts (not "self." for script-defined objects, just for self.moho), you just need to include in your recipes how to make it work...what you need to call them. Since this is only two lines of code, it really shouldn't be an unreasonable request to help make these more generally useful (especially for new scripters...those who might need these the most). So you can either add this, as an example of how to call your functions:

Code: Select all

	msPrintSwitchLayers.moho = moho
	msPrintSwitchLayers:ProcessTopLayers()
Or you can make the code standard to the stock tools:

Code: Select all

function msPrintSwitchLayers:PrintSwitchLayers(moho, layer)
   if layer:IsGroupType() then
      local group = moho:LayerAsGroup(layer)
      for i = 0, group:CountLayers()-1 do
         local sublayer = group:Layer(i)
         self:PrintSwitchLayers(sublayer)
      end
   end
   -- print the switch layer name
   -- putting this at the end prints children
   -- before parent
   if  layer:LayerType() == MOHO.LT_SWITCH then
      print(layer:Name())
   end
end

function msPrintSwitchLayers:ProcessTopLayers(moho)
   for i = 0, moho.document:CountLayers()-1 do
      local layer = moho.document:LayerByAbsoluteID(i)
      self:PrintSwitchLayers(moho, layer)
   end
   return moho, layer
end
Which by looking at the stock tools as example, most people can easily figure out is called using:

Code: Select all

msPrintSwitchLayers:ProcessTopLayers(moho)
Or you can ignore all that. Your prerogative.
The discussion is here for others to reference.
Post Reply