If / Then / Else with Conditional Operators in Rainmeter
Posted: June 12th, 2013, 3:39 pm
One of the things folks run into when creating skins is that the IfActions functionality, while powerful, has some limitations.
The limitation is that there can only be one of each of the three IfActions, (IFBelow, IfEqual, IfAbove) so it is not possible to say "if I am above 50 do A, if I am above 60 do B". Since there can only be one IfAboveValue option, the answer to this will have to be "A", even if the value being compared is 70.
The solution to this is to explore the use of conditional operations in a formula in Rainmeter. We will focus on using them in Calc measures for purposes of this guide.
The conditional operation in Rainmeter is a way to have a more complex "If / Then / ElseIf / Then / Else" comparison, allowing you to set the value of the measure based on a broad range of comparisons. You can then use IfActions on the measure to take actions, or Substitute to change the number value into some text you might use in a meter later.
The form of the conditional operation is condition ? expression if true : expression if false. When the "condition" is evaluated, it is either "true" or "false". When it is "true", the expression after the "?" is evaluated. If it is "false", the expression after the ":" is done. This is equivalent to a block like this in a programming language:
Let's start with a simple example:
[MeasureCond] will be 1
What this is doing is asking "is [Val] greater than 49?". If it is, the the condition is "true", and the value of this Calc measure is set to "1" (the "then" part of the formula). If it is not true, then the value is set to "-1" (the "else" part of the formula).
I'm just using a simple Calc measure [Val] to set a value we can use for our comparison. Normally you are going to be comparing with some measure that is returning some value based on a measurement.
It should be noted as we get started that there must always be all three parts of the formula. This whole thing is known in programming languages as a "Ternary Operation", which comes from the Latin for "three operations". You must always have an "if" a "then" and an "else" or an error will result.
Now this isn't much different than just using an IfAction right on the [Val] measure like "IfAboveValue=49", but I wanted to start simple and work up to more complex examples that go beyond what an IfAction is capable of.
You can use formulas in the condition part of the formula as well:
With [Val] still set to 50, [MeasureCond] will be 1
You can use Section Variables as well. Just be sure to set DynamicVariables=1 on the measure to use the current value:
Since the value of [Val] is larger than the value of [Val2], [MeasureCond] will be 1
Or you can use formulas or Section Variables in the "then" or "else" parts as well:
Since the value of [Val] is 50, the condition is "false" and [MeasureCond] is set to the value of [Val] instead of "1"
You can also use compound conditions using things like AND and OR.
These are using the "logical AND" and "logical OR". Note that you must enclose the components of the condition in parentheses. In both cases, [MeasureCond] and [MeasureCond2], the result is "1"
We should stop for one second and touch on terminology.
(parentheses)
[brackets]
{braces}
Don't confuse parentheses with brackets or braces, or I will just send you to visit Grammar Girl
Ok, that should touch on most of the simple uses, things that you can also logically get to with IfActions in most cases. Now let's look at how we can use "nesting" of "If / Then / ElseIf / Then / Else" to get the full benefit of conditional operations.
Let's look at what this is saying. It is in effect walking through a series of "If / Then" statements, stopping when the result is "true" and continuing to evaluate the next "Else" when the result is "false". At the very end is the last "Else", in this example : 1, which will be the value if the entire expression is never "true".
"Is Val greater than 74? No. Well then, is Val greater than 49? Yes!" So the value of [MeasureCond] will be "3".
Important Notes: You must use parentheses to "nest" the conditional operations within the whole of the expression. So each of the "If" conditions after the initial one would start with an open parentheses, and you would put as many end parentheses at the very end of the entire option as there are "If" conditions after the first one. Look carefully at the Formula above to see how this should be done. Also, there is a limit of 30 nested operations in a single formula in Rainmeter.
If the Formula was more complex, with more "ElseIf" comparisons, it might look like this:
Eight starting parentheses to begin each "If", and eight ending parentheses at the very end to "close" them all.
Using the result:
You can certainly directly use the value of [MeasureCond] (in our examples) directly as MeasureName in a meter or as a Section Variable in other measures or meters.
You can also use IfActions on the measure to take actions as long as the result lends itself to the limitations of IfActions:
Or use a Substitute to turn the numeric value into some text you can use in a meter:
Remember our example of the limitations of IfAction from the very beginning of this guide?
The limitation is that there can only be one of each of the three IfActions, (IFBelow, IfEqual, IfAbove) so it is not possible to say "if I am above 50 do A, if I am above 60 do B". Since there can only be one IfAboveValue option, the answer to this will have to be "A", even if the value being compared is 70.
Using a Calc measure with a nested conditional operation, we can in fact bend the IfAction to our will:
Extra credit:
I mentioned that I would be using Calc measures to demonstrate how conditional operations work, but it should be noted that they will work anywhere a formula is allowed!
Just remember that outside of a Calc measure, formulas must be totally enclosed in starting and ending parentheses, aside from any you might need for "nesting".
Hope this is of some help with understanding this feature of Rainmeter.
Code: Select all
[MeasureCond]
Measure=Calc
Formula=50
IfAboveValue=50
IfAboveAction=[!SetOption MeterCond Text "I'm above 50"]
IfBelowValue=50
IfBelowAction=[!SetOption MeterCond Text "I'm below 50"]
IfEqualValue=50
IfEqualAction=[!SetOption MeterCond Text "I'm exactly 50"]
The solution to this is to explore the use of conditional operations in a formula in Rainmeter. We will focus on using them in Calc measures for purposes of this guide.
The conditional operation in Rainmeter is a way to have a more complex "If / Then / ElseIf / Then / Else" comparison, allowing you to set the value of the measure based on a broad range of comparisons. You can then use IfActions on the measure to take actions, or Substitute to change the number value into some text you might use in a meter later.
The form of the conditional operation is condition ? expression if true : expression if false. When the "condition" is evaluated, it is either "true" or "false". When it is "true", the expression after the "?" is evaluated. If it is "false", the expression after the ":" is done. This is equivalent to a block like this in a programming language:
Code: Select all
if (condition)
then
expr. if true
else
expr. if false
end if
Let's start with a simple example:
Code: Select all
[Val]
Measure=Calc
Formula=50
[MeasureCond]
Measure=Calc
Formula=Val > 49 ? 1 : -1
What this is doing is asking "is [Val] greater than 49?". If it is, the the condition is "true", and the value of this Calc measure is set to "1" (the "then" part of the formula). If it is not true, then the value is set to "-1" (the "else" part of the formula).
I'm just using a simple Calc measure [Val] to set a value we can use for our comparison. Normally you are going to be comparing with some measure that is returning some value based on a measurement.
It should be noted as we get started that there must always be all three parts of the formula. This whole thing is known in programming languages as a "Ternary Operation", which comes from the Latin for "three operations". You must always have an "if" a "then" and an "else" or an error will result.
Now this isn't much different than just using an IfAction right on the [Val] measure like "IfAboveValue=49", but I wanted to start simple and work up to more complex examples that go beyond what an IfAction is capable of.
You can use formulas in the condition part of the formula as well:
Code: Select all
[MeasureCond]
Measure=Calc
Formula=Val * 2 = 100 ? 1 : -1
You can use Section Variables as well. Just be sure to set DynamicVariables=1 on the measure to use the current value:
Code: Select all
[Val]
Measure=Calc
Formula=50
[Val2]
Measure=Calc
Formula=40
[MeasureCond]
Measure=Calc
Formula=Val > [Val2] ? 1 : -1
DynamicVariables=1
Or you can use formulas or Section Variables in the "then" or "else" parts as well:
Code: Select all
[MeasureCond]
Measure=Calc
Formula=Val > 50 ? 1 : [Val]
DynamicVariables=1
You can also use compound conditions using things like AND and OR.
Code: Select all
[Val]
Measure=Calc
Formula=50
[MeasureCond]
Measure=Calc
Formula=(Val > 49) && (Val < 60) ? 1 : -1
[MeasureCond2]
Measure=Calc
Formula=(Val = 50) || (Val = 100) ? 1 : -1
We should stop for one second and touch on terminology.
(parentheses)
[brackets]
{braces}
Don't confuse parentheses with brackets or braces, or I will just send you to visit Grammar Girl
Ok, that should touch on most of the simple uses, things that you can also logically get to with IfActions in most cases. Now let's look at how we can use "nesting" of "If / Then / ElseIf / Then / Else" to get the full benefit of conditional operations.
Code: Select all
[Val]
Measure=Calc
Formula=50
[MeasureCond]
Measure=Calc
Formula=Val > 74 ? 4 : (Val > 49 ? 3 : (Val > 24 ? 2 : 1))
"Is Val greater than 74? No. Well then, is Val greater than 49? Yes!" So the value of [MeasureCond] will be "3".
Important Notes: You must use parentheses to "nest" the conditional operations within the whole of the expression. So each of the "If" conditions after the initial one would start with an open parentheses, and you would put as many end parentheses at the very end of the entire option as there are "If" conditions after the first one. Look carefully at the Formula above to see how this should be done. Also, there is a limit of 30 nested operations in a single formula in Rainmeter.
If the Formula was more complex, with more "ElseIf" comparisons, it might look like this:
Code: Select all
Formula=Val > 90 ? 9 : (Val > 80 ? 8 : (Val > 70 ? 7 : (Val > 60 ? 6 : (Val > 50 ? 5 : (Val > 40 ? 4 : (Val > 30 ? 3 : (Val > 20 ? 2 : (Val > 10 ? 1 : 0))))))))
Using the result:
You can certainly directly use the value of [MeasureCond] (in our examples) directly as MeasureName in a meter or as a Section Variable in other measures or meters.
You can also use IfActions on the measure to take actions as long as the result lends itself to the limitations of IfActions:
Code: Select all
[Val]
Measure=Calc
Formula=50
[MeasureCond]
Measure=Calc
Formula=Val * 2 = 100 ? 1 : -1
IfEqualValue=1
IfEqualAction=[!SetOption MeterCond Text "I'm One"]
IfBelowValue=0
IfBelowAction=[!SetOption MeterCond Text "I'm Negative"]
Code: Select all
[Val]
Measure=Calc
Formula=50
[MeasureCond]
Measure=Calc
Formula=Val > 74 ? 4 : (Val > 49 ? 3 : (Val > 24 ? 2 : 1))
Substitute="4":"Last Quarter","3":"Third Quarter","2":"Second Quarter","1":"First Quarter"
Code: Select all
[Val]
Measure=Calc
Formula=70
Using a Calc measure with a nested conditional operation, we can in fact bend the IfAction to our will:
Code: Select all
[MeasureCond]
Measure=Calc
Formula=Val > 60 ? 2 : (Val > 50 ? 1 : -1)
IfEqualValue=1
IfEqualAction=[!SetOption MeterCond Text "I'm above 50"]
IfAboveValue=1
IfAboveAction=[!SetOption MeterCond Text "I'm above 60"]
IfBelowValue=0
IfBelowAction=[!SetOption MeterCond Text "I'm below 50"]
I mentioned that I would be using Calc measures to demonstrate how conditional operations work, but it should be noted that they will work anywhere a formula is allowed!
Code: Select all
[MeterImage]
Meter=Image
W=10
H=10
SolidColor=0,0,0,255
X=(#SCREENAREAWIDTH# = 1680 ? 160 : (#SCREENAREAWIDTH# = 1280 ? 128 : 0))
Y=0
Hope this is of some help with understanding this feature of Rainmeter.