It is currently March 29th, 2024, 2:39 am

IfCondition bangs, nested syntax and inline Lua

Report bugs with the Rainmeter application and suggest features.
User avatar
Yincognito
Rainmeter Sage
Posts: 7029
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

IfCondition bangs, nested syntax and inline Lua

Post by Yincognito »

I'm already a bit tired of debugging this kind of issues lately in this related thread, so I'll be short...

The skin
SectionIndex_1.0.0.rmskin

The problem
The log message from:

Code: Select all

IfTrueAction=[!Log "Last index of #CURRENTSECTION# (i.e. #CURRENTSECTIONINDEX#) equals 3"]
IfFalseAction=[!Log "Last index of #CURRENTSECTION# (i.e. #CURRENTSECTIONINDEX#) is not 3"]
will incorrectly be Last index of MeasureBlah7Blah15Blah3 (i.e. 0) equals 3 (SectionIndex\SectionIndex.ini)
while the log message from:

Code: Select all

IfTrueAction=[!Log "Last index of #CURRENTSECTION# (i.e. [&Script:SectionIndex('[#CURRENTSECTION]','last','(<x>)')]) equals 3"]
IfFalseAction=[!Log "Last index of #CURRENTSECTION# (i.e. [&Script:SectionIndex('[#CURRENTSECTION]','last','(<x>)')]) is not 3"]
will correctly be Last index of MeasureBlah7Blah15Blah3 (i.e. 3) equals 3 (SectionIndex\SectionIndex.ini)


The question
Solutions to the former Log message (and implicitly, bangs) working appropriately and display the correct 3 instead of the incorrect 0?


EDIT: Oversimplified version of the problem below: a very simple script that gives the value of the #CURRENTSECTION# built-in variable in Rainmeter, but from Lua. While in the measures and meters themselves Lua provides current section's correct value, in the Log bangs of the IfTrueAction/IfFalseAction options from the IfCondition it provides ... nothing at all:
Test_1.0.0.rmskin
Now, if you tell me this is not a bug... :Whistle
You do not have the required permissions to view the files attached to this post.
Last edited by Yincognito on July 3rd, 2021, 10:39 am, edited 2 times in total.
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
nek
Posts: 105
Joined: November 3rd, 2019, 12:00 am

Re: IfCondition bangs, nested syntax and inline Lua

Post by nek »

Hi Yincognito!
I really don't understand what the codes are doing, but I hope you can find something from these debug log.

Code: Select all

function SectionIndex(section, occurrence, formula)

  SKIN:Bang('!Log', 'Script: SectionIndex | '..section..' | '..occurrence..' | '..formula)

  local indexes = {}
  for index in section:gmatch('%d+') do table.insert(indexes, index) end
  if occurrence == 'first' then occurrence = 1 elseif occurrence == 'last' then occurrence = #indexes end
  
  SKIN:Bang('!Log', 'Script: SectionIndex | '..(tostring(SKIN:ParseFormula(string.gsub(formula, '<x>', tostring(indexes[tonumber(occurrence)] or '0')))) or '0'))
  
  return tostring(SKIN:ParseFormula(string.gsub(formula, '<x>', tostring(indexes[tonumber(occurrence)] or '0')))) or '0'

end

Code: Select all

IfTrueAction=[!Log "True | Last index of #CURRENTSECTION# (i.e. [&Script:SectionIndex('[#CURRENTSECTION]','last','(<x>)')]) equals 3"]
IfFalseAction=[!Log "False | Last index of #CURRENTSECTION# (i.e. [&Script:SectionIndex('[#CURRENTSECTION]','last','(<x>)')]) is not 3"]
SectionIndex01.png

Code: Select all

IfTrueAction=[!Log "Last index of #CURRENTSECTION# (i.e. #CURRENTSECTIONINDEX#) equals 3"]
IfFalseAction=[!Log "Last index of #CURRENTSECTION# (i.e. #CURRENTSECTIONINDEX#) is not 3"]
SectionIndex02.png
You do not have the required permissions to view the files attached to this post.
User avatar
Yincognito
Rainmeter Sage
Posts: 7029
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: IfCondition bangs, nested syntax and inline Lua

Post by Yincognito »

nek wrote: April 22nd, 2021, 3:43 pm Hi Yincognito!
I really don't understand what the codes are doing, but I hope you can find something from these debug log.
Thanks, nek, for taking a look. The logs are helpful, but they are not something I've never seen before. I used similar logging (using the print() function in Lua, as opposed to using the SKIN:Bang('!Log'... interface with Rainmeter) inside the same IfCondition, and had the same result in certain cases: despite passing a Section string argument to my SectionIndex(section, occurrence, formula) function, which should be the value of #CURRENTSECTION# for the current measure, the value of that argument is the empty string. This is why I constructed the tostring(indexes[tonumber(occurrence)] or '0') the way I did, so that Rainmeter doesn't get a NIL value from Lua instead and throw an error.

Point is, I'm aware of what happens, I just don't know WHY that happens and WHAT is the solution. The IfCondition works fine, but as soon as Rainmeter reaches the bangs from the IfCondition's IfTrueAction and IfFalseAction it somehow "loses" the value of the first parameter of my function (i.e. section) when using the #CURRENTSECTIONINDEX# variable I created in the skin (which otherwise works as intended), and I have to use the inline Lua syntax in order for things to get their correct values (which I don't want to do, as it defeats the purpose of having a variable providing the same result while shortening the code in the first place).

The other thread explains what is the purpose of the skin - in short, it's a Lua solution to get the "index" from a section name in Rainmeter. For example, say you have [Measure27] in your skin; the solution would get the 27 part from the section name, so it can be used dynamically in other places (including within the same measure). Being able to do this can really shorten one's code and make the copy pasting much easier, especially when he has a ton of measures/meters using the same approach, where only the "index" used in the operations differs. Think of many WebParser child measures like [Child1]..StringIndex=1, [Child2]..StringIndex=2, [Child3]..StringIndex=3 and so on - instead of having to modify the incrementing index both in the section name and in the section body, one modification per section (i.e. in its name) is enough, by using my Lua function.

So, again, thank you for looking into it. Do you also have an idea of how to FIX the fact that in the aforementioned bangs the Lua function doesn't receive its proper arguments?
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Yincognito
Rainmeter Sage
Posts: 7029
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: IfCondition bangs, nested syntax and inline Lua

Post by Yincognito »

nek wrote: April 22nd, 2021, 3:43 pmHi Yincognito!
I really don't understand what the codes are doing...
Just posted a much simpler version of the problem (not that the earlier skin was complicated, but anyway) in the EDIT from the first post. In the bangs from the options of an IfCondition, Lua can't retrieve the correct value of the #CURRENTSECTION# built-in variable, yielding nothing instead.

I used SKIN:ReplaceVariables() to get the value in Lua, and this is supposed to work for all variables. This problem happens even if I use other ways to get #CURRENTSECTION# though (one of them being present in my earlier SectionIndex skin, where I passed the built-in variable directly from Rainmeter).
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Brian
Developer
Posts: 2674
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: IfCondition bangs, nested syntax and inline Lua

Post by Brian »

This actually has nothing to do with IfCondition bangs, nested syntax or inline lua. This issue is specific to the CURRENTSECTION variable and is caused by how/when bangs are parsed.

For action options, normal variables (ie. #Variable#) are parsed when the option is read or updated. This happens when the skin is refreshed, during the Update cycle when DynamicVariables=1 is used, and when the !SetOption bang is performed on any option in that section. However, section variables (ie. [Measure]) and nested variables (ie. [#Variable]) are not parsed at this time...they are only parsed when the action is executed.

The CURRENTSECTION variable is the only variable that needs special parsing throughout Rainmeter, both in its regular #CURRENTSECTION# and nested [#CURRENTSECTION] forms. The reason is because of the different "times" when the variable *can* be parsed. Basically, Rainmeter has to keep track of not only when the action is to be performed (or where the variable was referenced), but by the section that has executed the action. In most cases, the current section is tracked, but obviously not all of them.

For example, in your case above, when the IfConditions actions are read, the #CURRENTSECTIONINDEX# variable is replaced with [&Script:SectionIndex('[#CURRENTSECTION]','last','(<x>)')] (note the *'s were removed in this step). Normally, the current section special processing would kick in and parse the [#CURRENTSECTION] variable here, but that does not happen since the action only re-evaluates section/nested variables when the action is executed. By that time, section tracking is lost and nothing is retrieved. This particular case seems to be related to variables referencing other variables containing the current section variable.

Here is a similar test skin that shows the CURRENTSECTION problem with a basic OnUpdateAction:

Code: Select all

[Rainmeter]
DynamicWindowSize=1
AccurateText=1
OnRefreshAction=[!About]

[Variables]
var=[#*CURRENTSECTION*]

[Measure1]
Measure=String
String=#var#
DynamicVariables=1

[Measure2]
Measure=Calc
Formula=1
DynamicVariables=1
UpdateDivider=-1
OnUpdateAction=[!Log "Measure1=[Measure1]"][!Log "CurrentSection(regular)=#CURRENTSECTION#"][!Log "CurrentSection(nested)=[#CURRENTSECTION]"][!Log "var(regular)=#var#"][!Log "var(nested)=[#var]"]

[Meter]
Meter=String
FontColor=255,255,255
SolidColor=0,0,0
Padding=5,5,5,5
Text=Measure1=[Measure1]#CRLF#var=#var#
DynamicVariables=1

Unfortunately, this current section issue has been around since we introduced the variable. Fixing it for all the special cases has been a challenge.

-Brian


PS - The complicated implementation of current section variable and its special processing is the main reason why we are hesitant to introduce any indexing variable. That doesn't mean we can't find a way to do it, it just means we haven't worked out the best way to do it.

PSS - Unfortunately, the SKIN:ReplaceVariables() is another portion of the code that does not contain the current section tracking code either.
User avatar
Yincognito
Rainmeter Sage
Posts: 7029
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: IfCondition bangs, nested syntax and inline Lua

Post by Yincognito »

Brian wrote: April 23rd, 2021, 6:33 am This actually has nothing to do with IfCondition bangs, nested syntax or inline lua. This issue is specific to the CURRENTSECTION variable and is caused by how/when bangs are parsed.
[...]

Unfortunately, this current section issue has been around since we introduced the variable. Fixing it for all the special cases has been a challenge.

-Brian


PS - The complicated implementation of current section variable and its special processing is the main reason why we are hesitant to introduce any indexing variable. That doesn't mean we can't find a way to do it, it just means we haven't worked out the best way to do it.

PSS - Unfortunately, the SKIN:ReplaceVariables() is another portion of the code that does not contain the current section tracking code either.
Thanks for responding, Brian. So that was the meaning of when you said #CURRENTSECTION# is the "black sheep" of variables and is tricky to get...

I was aware of most of the things in your explanation, but still don't quite understand how the value can be "lost", even if Rainmeter re-evaluates section/nested variables when the action is executed. In my mind, the value should even more be correctly retrieved in such a case since the re-evaluation time is closer to when the value is needed, but you know best how Rainmeter does what it does.

Unfortunately I can't help (at least not yet) with the code in Rainmeter itself, C++ is missing from my "knowledge repository", but I can definitely help with ideas on that code, assuming I know how the process is happening.

Side question: is there any other way of getting the value of the current section (but not the value of the built-in #CURRENTSECTION# variable!) from Lua? If so, this would avoid relying on the tricky #CURRENTSECTION# and instead get that value directly from Lua...
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Brian
Developer
Posts: 2674
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: IfCondition bangs, nested syntax and inline Lua

Post by Brian »

Yincognito wrote: April 23rd, 2021, 7:06 am I was aware of most of the things in your explanation, but still don't quite understand how the value can be "lost", even if Rainmeter re-evaluates section/nested variables when the action is executed. In my mind, the value should even more be correctly retrieved in such a case since the re-evaluation time is closer to when the value is needed...
Think of it like this, all variables live at the "skin" level, meaning they are available to the entire skin. CURRENTSECTION also lives at the skin level, but its meaning changes depending on "where" it is located in the skin. Tracking this location is the problem since variables can be parsed at different times. Trying to account for all the possibilities is why this bug exists.

Yincognito wrote: April 23rd, 2021, 7:06 am Side question: is there any other way of getting the value of the current section (but not the value of the built-in #CURRENTSECTION# variable!) from Lua? If so, this would avoid relying on the tricky #CURRENTSECTION# and instead get that value directly from Lua...
I can't think of way to get this in the way you want it without using the #CURRENTSECTION# variable directly (without the use of another variable). There is no "which section called me" function of lua.

Hopefully this discussion can spur some "new" thoughts on getting this issue(s) resolved.

-Brian
User avatar
Yincognito
Rainmeter Sage
Posts: 7029
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: IfCondition bangs, nested syntax and inline Lua

Post by Yincognito »

Brian wrote: April 23rd, 2021, 1:08 pm Think of it like this, all variables live at the "skin" level, meaning they are available to the entire skin. CURRENTSECTION also lives at the skin level, but its meaning changes depending on "where" it is located in the skin. Tracking this location is the problem since variables can be parsed at different times. Trying to account for all the possibilities is why this bug exists.

I can't think of way to get this in the way you want it without using the #CURRENTSECTION# variable directly (without the use of another variable). There is no "which section called me" function of lua.

Hopefully this discussion can spur some "new" thoughts on getting this issue(s) resolved.

-Brian
I understand, thanks for letting me know. For the moment, I found a "solution" (more like a "hack", but anyway) to the problem: splitting the main variable so #CURRENTSECTION# can be written literally in the middle of the "pieces", see here. A temporary one, sure, but maybe it will be of some help... :confused:
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Yincognito
Rainmeter Sage
Posts: 7029
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: IfCondition bangs, nested syntax and inline Lua

Post by Yincognito »

Brian wrote: April 23rd, 2021, 1:08 pm Think of it like this, all variables live at the "skin" level, meaning they are available to the entire skin. CURRENTSECTION also lives at the skin level, but its meaning changes depending on "where" it is located in the skin. Tracking this location is the problem since variables can be parsed at different times. Trying to account for all the possibilities is why this bug exists.

I can't think of way to get this in the way you want it without using the #CURRENTSECTION# variable directly (without the use of another variable). There is no "which section called me" function of lua.

Hopefully this discussion can spur some "new" thoughts on getting this issue(s) resolved.

-Brian
Brian, I saw that you worked to "improve the resolution of the #CURRENTSECTION# built-in variable when used in either Lua or Plugin inline section variables" for the 3452 revision of Rainmeter 4.4 Beta (which didn't exactly solve the problem in this post - the Log still prints 0 instead of 3 in the IfTrue and IfFalse actions of the IfCondition, but anyway), just wanted to let you know a similar problem exists when it comes to Rainmeter's formula parser, as illustrated in the code below...

The same Lua:

Code: Select all

function SectionIndex(section, occurrence, formula)
  local indexes = {}
  for index in section:gmatch('%d+') do table.insert(indexes, index) end
  if occurrence == 'first' then occurrence = 1 elseif occurrence == 'last' then occurrence = #indexes end
  return tostring(SKIN:ParseFormula(string.gsub(formula, '<x>', tostring(indexes[tonumber(occurrence)] or '0')))) or '0'
end
Sample Rainmeter code:

Code: Select all

[Variables]
; Inline Lua Usage: [&Script:SectionIndex(SomeSection,SomeOccurrence,SomeFormula)]
; SomeSection     = the name of any section, enclosed by ' (apostrophes), like: 'Measure1' or 'Meter1'
; SomeOccurrence  = the desired occurrence of an index, like: 3 or '5' or 'first' or 'last'
; SomeFormula     = the Rainmeter formula applied to the original <x> index, like: '(<x>)' or '(<x>+1)' or '(Sqrt(<x>))'
; Section variables need to use their nested form, like: [&Script:SectionIndex('[#Section]',[#Occurrence],'[#Formula]')]

; Applicability In:
; - Simulation of getting the current section's "last" index using variables, to avoid repeating inline Lua syntax
;       (Usage: #GET#SomeSection#INDEX#, where SomeSection can be the name of any section, quotes not needed)
; This "hack" of using multiple variables instead of just one hypothetical #CURRENTSECTIONINDEX# was necessary because of
; these #CURRENTSECTION# particularities here: https://forum.rainmeter.net/viewtopic.php?f=14&t=37403&p=190363#p190362
GET=[&Script:SectionIndex('
INDEX=','last','(<x>)')]
; - Alternative equivalent using inline Lua in the skin code: [&Script:SectionIndex('[#CURRENTSECTION]','last','(<x>)')]

; PROBLEMATIC
CURRENTSECTIONINDEX=[&Script:SectionIndex('[#*CURRENTSECTION*]','last','(<x>)')]

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1
BackgroundMode=2
SolidColor=47,47,47,255

---Measures---

[Script]
Measure=Script
ScriptFile=#@#Scripts\Script.lua
UpdateDivider=-1
DynamicVariables=1

---Meters---

[Background]
Meter=Image
W=300
H=300
SolidColor=255,0,0,255
UpdateDivider=-1

[ObjTooltip1]
Meter=Image
X=0
Y=0
W=20
H=5
SolidColor=0,255,0,255
UpdateDivider=-1

[ObjIcon1]
Meter=Image
X=([&ObjIcon#GET##CURRENTSECTION##INDEX#:X]+[&ObjIcon#GET##CURRENTSECTION##INDEX#:W]/2-[&ObjTooltip#CURRENTSECTIONINDEX#:W]/2)
Y=10
W=30
H=30
SolidColor=0,0,255,255
DynamicVariables=1
UpdateDivider=-1

[ObjTooltip2]
Meter=Image
X=50
Y=50
W=20
H=5
SolidColor=0,255,0,255
UpdateDivider=-1

[ObjIcon2]
Meter=Image
X=([&ObjIcon#GET##CURRENTSECTION##INDEX#:X]+[&ObjIcon#GET##CURRENTSECTION##INDEX#:W]/2-[&ObjTooltip#GET##CURRENTSECTION##INDEX#:W]/2)
Y=60
W=40
H=40
SolidColor=0,0,255,255
DynamicVariables=1
UpdateDivider=-1

[ObjTooltip3]
Meter=Image
X=100
Y=110
W=30
H=5
SolidColor=0,255,0,255
UpdateDivider=-1

[ObjIcon3]
Meter=Image
X=([&ObjIcon#GET##CURRENTSECTION##INDEX#:X]+[&ObjIcon#GET##CURRENTSECTION##INDEX#:W]/2-[&ObjTooltip#GET##CURRENTSECTION##INDEX#:W]/2)
Y=120
W=50
H=50
SolidColor=0,0,255,255
DynamicVariables=1
UpdateDivider=-1
Basically, in the [ObjIcon1]'s X, I replaced the working (based on my last reply) #GET##CURRENTSECTION##INDEX# in the subtracted element of that formula operation with the "problematic" (see the last comment from [Variables]) #CURRENTSECTIONINDEX#, leading to errors (apart from the initial harmless ones) occuring and the result being wrong / incomplete. In other words, if I make, say (slightly different example, to put it more simple):

Code: Select all

X=([&ObjIcon[#CURRENTSECTIONINDEX]:W])
it will work, but if I make:

Code: Select all

X=([&ObjIcon[#CURRENTSECTIONINDEX]:W]+[&ObjIcon[#CURRENTSECTIONINDEX]:W])
the second element of the operation (the one after the +) will be incorrect and the result won't work.

I just wanted to let you know of the "side effects" of the problem, that's all. As I already said, my last "technique" of splitting such a custom variable in 3 parts in order to be able to write #CURRENTSECTION# literally (e.g. #GET##CURRENTSECTION##INDEX#, where GET=[&Script:SectionIndex(' and INDEX=','last','(<x>)')]) is able to circumvent the problem and provide the expected result.
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth