It is currently May 5th, 2024, 10:36 pm

Backwards WebParsing?

Get help with creating, editing & fixing problems with skins
Bishop149
Posts: 25
Joined: March 23rd, 2016, 3:07 pm

Re: Backwards WebParsing?

Post by Bishop149 »

Thanks for this, as I said I have zero experience of lua so I'll sit down today and look at your code in an attempt to reverse engineer it and learn something!
jsmorley wrote: I'm happy to explain any or all of this.
I may well be taking you up on that, lets see how much of it I can grasp!
User avatar
FreeRaider
Posts: 826
Joined: November 20th, 2012, 11:58 pm

Re: Backwards WebParsing?

Post by FreeRaider »

jsmorley, I have a question to ask. Is there a special reason about why you do not use function Update() in LUA code?

Thank you in advance
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Backwards WebParsing?

Post by jsmorley »

FreeRaider wrote:jsmorley, I have a question to ask. Is there a special reason about why you do not use function Update()in LUA code?

Thank you in advance
Yes, I wanted the WebParser measure to drive the Lua with FinishAction. There is just absolutely no reason to run the Lua if you have not just received data from the site. In particular, you don't want it to run on the first skin update, when the WebParser measure has not yet done its work, and the value of the measure is "" (an empty string). While you can test for that in the Lua to ensure it doesn't just blow up, its a waste of time and resources to have the Lua reach back to the skin and get the value of the WebParser measure, when you know for certain that on the first skin update it will be empty.

Even after that, what would be the point of not having the Lua and the WebParser just be "sync'd up"? The Lua is entirely dependent on the WebParser measure, and there is no benefit to ever parsing the data twice if it hasn't changed.

So I could have used Update() in Lua, but I would have had to jump through a couple of hoops. I would have had to set it to Disabled=1 initially, and used UpdateDivider=-1 on it. Then in the WebParser FinishAction, I could !EnableMeasure it and send it an !UpdateMeasure.

However, using Update doesn't buy you anything over directly calling a Function() with !CommandMeasure in this case. The only thing that Update() provides that this doesn't, is the ability to set a "return" value, so the value of the Script measure can be used in the skin. Don't need that here. I have far more to "return" than a single measure value can usefully handle, so I used !SetOption to just directly set the Text options on the meters.

There is just no useful work that the Lua can or should do on every skin update in this particular case.
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Backwards WebParsing?

Post by jsmorley »

Bishop149 wrote:I may well be taking you up on that, lets see how much of it I can grasp!
There are several "pattern matching" functions that I use in the skin, that are good to at least wrap your head around, as they show off the very powerful and flexible "string" capabilities in Lua. Let me explain at a high level what this skin is doing, as I think it might be of value for others who are dipping their toes into the Lua water.

The Skin

The skin is pretty straightforward. We have a WebParser measure, that simply gets the entire contents of the site with RegExp=(?siU)^(.*)$. At the end of the WebParser measure, we have a FinishAction that simply fires the Lua code with !CommandMeasure.

Then we have the Script measure. We do two things there, that we will use in the Lua in a second. We set two custom options on the measure:

Operator=SN
MaxTrains=4

That allows us to "tell" the Lua that the "operator" we \want to match on is "SN", and that we want the first 4 trains that match. Those are of course are very specific to this skin, but it also demonstrates that you can set any custom option on a Script measure that you want, and you can use them in the Lua. We will see how in a minute.

Then we just have a bunch of numbered meters, with no MeasureName or Text options on them. They are going to be entirely controlled by the Lua. They are initially hidden, so we can unhide them as needed in the Lua, or just leave them hidden if they don't end up getting a value.

So the WebParser measure simply goes out and gets the contents of the site as its value on every UpdateRate, and fires the ParseSite() function in the Lua each time it is done doing so.

The Lua

First let me display our example .html code, and the entire .lua file here, then we will go through how it works:

Code: Select all

<station>
<station_name>Crystal Palace</station_name>
<request_time>2016-04-12T17:56:19</request_time>
<station_code>CYP</station_code>
<updates>
<update type="departure">
<mode>train</mode>
<service>24787000</service>
<train_uid>W64127</train_uid>
<origin_name>Norwood Junction</origin_name>
<destination_name>South Bermondsey</destination_name>
<platform>1</platform>
<operator>SN</operator>
<aimed_departure_time>17:55:00</aimed_departure_time>
<expected_departure_time>18:00:00</expected_departure_time>
<best_departure_estimate_mins>3</best_departure_estimate_mins>
<aimed_arrival_time>17:53:00</aimed_arrival_time>
<expected_arrival_time>17:58:00</expected_arrival_time>
<best_arrival_estimate_mins>1</best_arrival_estimate_mins>
<status>LATE</status>
<source>Network Rail</source>
</update>
<update type="departure">
<mode>train</mode>
<service>24787000</service>
<train_uid>W63651</train_uid>
<origin_name>London Bridge</origin_name>
<destination_name>Beckenham Junction</destination_name>
<platform>2</platform>
<operator>SN</operator>
<aimed_departure_time>17:57:00</aimed_departure_time>
<expected_departure_time>18:00:00</expected_departure_time>
<best_departure_estimate_mins>3</best_departure_estimate_mins>
<aimed_arrival_time>17:57:00</aimed_arrival_time>
<expected_arrival_time>18:00:00</expected_arrival_time>
<best_arrival_estimate_mins>3</best_arrival_estimate_mins>
<status>LATE</status>
<source>Network Rail</source>
</update>
<update type="departure">
<mode>train</mode>
<service>24782000</service>
<train_uid>W65388</train_uid>
<origin_name>Epsom</origin_name>
<destination_name>London Victoria</destination_name>
<platform>1</platform>
<operator>SN</operator>
<aimed_departure_time>17:58:00</aimed_departure_time>
<expected_departure_time>18:14:00</expected_departure_time>
<best_departure_estimate_mins>17</best_departure_estimate_mins>
<aimed_arrival_time>17:57:00</aimed_arrival_time>
<expected_arrival_time>18:13:00</expected_arrival_time>
<best_arrival_estimate_mins>16</best_arrival_estimate_mins>
<status>LATE</status>
<source>Network Rail</source>
</update>
<update type="departure">
<mode>train</mode>
<service>24782000</service>
<train_uid>W65357</train_uid>
<origin_name>London Victoria</origin_name>
<destination_name>Sutton (Surrey)</destination_name>
<platform>2</platform>
<operator>SN</operator>
<aimed_departure_time>18:04:00</aimed_departure_time>
<expected_departure_time>18:06:00</expected_departure_time>
<best_departure_estimate_mins>9</best_departure_estimate_mins>
<aimed_arrival_time>18:03:00</aimed_arrival_time>
<expected_arrival_time>18:05:00</expected_arrival_time>
<best_arrival_estimate_mins>8</best_arrival_estimate_mins>
<status>LATE</status>
<source>Network Rail</source>
</update>
<update type="departure">
<mode>train</mode>
<service>22215003</service>
<train_uid>L42553</train_uid>
<origin_name>Crystal Palace</origin_name>
<destination_name>Highbury & Islington</destination_name>
<platform>5</platform>
<operator>LO</operator>
<aimed_departure_time>18:06:00</aimed_departure_time>
<expected_departure_time>18:06:00</expected_departure_time>
<best_departure_estimate_mins>9</best_departure_estimate_mins>
<aimed_arrival_time/>
<expected_arrival_time/>
<best_arrival_estimate_mins/>
<status>STARTS HERE</status>
<source>Network Rail</source>
</update>
<update type="departure">
<mode>train</mode>
<service>24783000</service>
<train_uid>W63085</train_uid>
<origin_name>London Bridge</origin_name>
<destination_name>London Victoria</destination_name>
<platform>4</platform>
<operator>SN</operator>
<aimed_departure_time>18:14:00</aimed_departure_time>
<expected_departure_time>18:14:00</expected_departure_time>
<best_departure_estimate_mins>17</best_departure_estimate_mins>
<aimed_arrival_time>18:13:00</aimed_arrival_time>
<expected_arrival_time>18:13:00</expected_arrival_time>
<best_arrival_estimate_mins>16</best_arrival_estimate_mins>
<status>EARLY</status>
<source>Network Rail</source>
</update>
<update type="departure">
<mode>train</mode>
<service>24787000</service>
<train_uid>W63656</train_uid>
<origin_name>Beckenham Junction</origin_name>
<destination_name>South Bermondsey</destination_name>
<platform>1</platform>
<operator>SN</operator>
<aimed_departure_time>18:17:00</aimed_departure_time>
<expected_departure_time>18:17:00</expected_departure_time>
<best_departure_estimate_mins>20</best_departure_estimate_mins>
<aimed_arrival_time>18:17:00</aimed_arrival_time>
<expected_arrival_time>18:17:00</expected_arrival_time>
<best_arrival_estimate_mins>20</best_arrival_estimate_mins>
<status>ON TIME</status>
<source>Network Rail</source>
</update>
<update type="departure">
<mode>train</mode>
<service>24782000</service>
<train_uid>W63098</train_uid>
<origin_name>London Victoria</origin_name>
<destination_name>London Bridge</destination_name>
<platform>6</platform>
<operator>SN</operator>
<aimed_departure_time>18:20:00</aimed_departure_time>
<expected_departure_time>18:20:00</expected_departure_time>
<best_departure_estimate_mins>23</best_departure_estimate_mins>
<aimed_arrival_time>18:19:00</aimed_arrival_time>
<expected_arrival_time>18:19:00</expected_arrival_time>
<best_arrival_estimate_mins>22</best_arrival_estimate_mins>
<status>ON TIME</status>
<source>Network Rail</source>
</update>
</updates>
</station>

Code: Select all

function Initialize()

   inputMeasure = SKIN:GetMeasure('MeasureSite')
   trainPattern = '<update type=.-</update>'
   destinationPattern = '<destination_name>(.-)</destination_name>'
   aimedPattern = '<aimed_departure_time>(.-)</aimed_departure_time>'
   expectedPattern = '<expected_departure_time>(.-)</expected_departure_time>'
   
end
   
function Update()
      
end

function ParseSite()

   SKIN:Bang('!HideMeterGroup', 'All')
   
   operatorString = SELF:GetOption('Operator', 'None')
   maxTrains = tonumber(SELF:GetOption('MaxTrains', '1'))
   
   entireFeed = inputMeasure:GetStringValue()
   dummyString, trainCount = string.gsub(entireFeed, '<update type', '')
   startPos = 0
   meterNumber = 1
   
   for i = 1, trainCount do
   
      trainStart, trainEnd = string.find(entireFeed, trainPattern, startPos)
      oneTrain = string.sub(entireFeed, trainStart, trainEnd)
      destinationName = string.match(oneTrain, destinationPattern)
      aimedTime = string.match(oneTrain, aimedPattern)
      expectedTime = string.match(oneTrain, expectedPattern)
      operatorName = string.match(oneTrain, '<operator>('..operatorString..')</operator>')
      
      if operatorName then
         
         SKIN:Bang('!SetOption', 'MeterDest'..meterNumber, 'Text', destinationName)
         SKIN:Bang('!SetOption', 'MeterAimed'..meterNumber, 'Text', aimedTime)
         SKIN:Bang('!SetOption', 'MeterExpected'..meterNumber, 'Text', expectedTime)
         SKIN:Bang('!ShowMeterGroup', 'Train'..meterNumber)
         meterNumber = meterNumber + 1
         
      end
      
      if meterNumber > maxTrains then break end
      
      startPos = trainEnd + 1
      
   end
   
end
Initialize()

The Initialize() function is where you can define things that only need to be done "once". It is executed only one time, when the skin is loaded or refreshed. We are going to do a couple of things.

First we use inputMeasure = SKIN:GetMeasure('MeasureSite') to create a measure "object" in the Lua, this allow us to tie an object name, "inputMeasure" to a skin measure, "MeasureSite". Think of it as a "handle" to the measure we will use later to get the measure value.

Then we define some "patterns" that we will use later when parsing the data.

Code: Select all

   trainPattern = '<update type=.-</update>'
   destinationPattern = '<destination_name>(.-)</destination_name>'
   aimedPattern = '<aimed_departure_time>(.-)</aimed_departure_time>'
   expectedPattern = '<expected_departure_time>(.-)</expected_departure_time>'
The first, "trainPattern", just gets all the contents of a single "train". Everything between <update type= and </update>.

Then we set patterns to match the three individual bits we want to capture from inside the result that trainPattern is going to return. So later we are going to get a "train" using trainPattern, and the bits for that train using those last three pattern definitions. Don't worry too much about those now, they will become more clear in a minute.

Note that we use .- to say "zero or more characters of any type", instead of the .* you might be more familiar with in WebParser or other PCRE regular expressions in Rainmeter. Pattern matching in Lua is very much, but not exactly, like regular expression. What "-" does is tell the pattern matching engine that you want the match to be "ungreedy". It is what you would do in regular expression with the "U" in (?siU) you often see. This is just how Lua does it. .* is "greedy" and .- is "ungreedy".

ParseSite()

So on to our ParseSite() function. Everything in this function is done each time the ParseSite() function is executed by the FinishAction in the WebParser measure.

First we (re)hide all meters. We will unhide them as needed later.

Then we ask the skin for the current value of those two custom options we defined on the Script measure. We are going to use those values in our functionality. We use options from the skin (you could also just use skin variables defined in [Variables]) rather than hard-coding them, so you can dynamically change them in the skin if you want. The Lua will check them each time ParseSite() is run.

entireFeed = inputMeasure:GetStringValue()
Here we ask the skin for the value of the measure [MeasureSite] referencing it with our object handle inputMeasure that we created in Initialize(). So now the variable entireFeed contains, well, the entire feed, as a single string.

The next line: dummyString, trainCount = string.gsub(entireFeed, '<update type', '') bears some explaining. It uses a clever trick of the built-in string.gsub(StringToSearch, PatternToFind, Repalcement) function. That function will search a string, and replace all text that matches the pattern with some other desired text. We don't really need or want to do that, BUT the tricky bit is that this function returns two values. The new string with the replacement done (we don't care, so that is dummyString) and the number of times it was able to do so. Ah ha. If we know how many instances of <update type there are in the feed, we know how many "trains" there are. That is the value "trainCount".

This trick with string.gsub is a really good one to remember. It can be used when parsing any xml website data, to tell you how many "entries" or "items" were returned by the feed. This can be very useful information.

Then we are just resetting two numeric variables, startPos and meterNumber, to their starting values. We want them to be back to start on each execution of ParseSite().

The meat and potatoes

for i = 1, trainCount do. This will start a loop. It will execute the loop as many times as there are "trains" in the feed, which is the value of trainCount that we got with the string.gsub above.

Inside the loop we:

trainStart, trainEnd = string.find(entireFeed, trainPattern, startPos)
This uses the built-in string.find(StringToSearch, PatternToFind, PositionInTheStringToStartLooking) function in Lua, to start looking in our entireFeed string, at position startPos (remember, we set that to zero a minute ago) and return the numeric position in the string of the "start" and "end" of the match on trainPattern. This will tell is that for instance, the first "train" starts at position 10, and ends at position 100. I don't know what those real numbers are, and don't care, the Lua will know and care.

So now we know where in entireFeed the first "train" is. We then use oneTrain = string.sub(entireFeed, trainStart, trainEnd) to extract just that first train out to a string variable oneTrain. This uses the built-in string.sub(StringToSearch, PositionInTheStringToStart, PositionInTheStringToEnd) function in Lua, to extract a "sub-string" from a string.

Code: Select all

      destinationName = string.match(oneTrain, destinationPattern)
      aimedTime = string.match(oneTrain, aimedPattern)
      expectedTime = string.match(oneTrain, expectedPattern)
Then we use the built-in string.match(StringToSearch, PatternMatchingExpression) function in Lua to "capture" the bits of data we want from that first train into variables. These are going to end up as the values used in our meters in the skin.

Now we want to see if this "train" has that desired "operator" value we got from the skin into operatorName.

operatorName = string.match(oneTrain, '<operator>('..operatorString..')</operator>')
Using the same string.match function, we try to specifically capture <operator>(SomeValue)</operator>, with "SomeValue" perhaps being "SN" in the example we have been working with. So it might look for and try to (capture) <operator>(SN)</operator>.

That use of string.match will return one of two things. It will either successfully return the value "SN" if it makes a match, or it will fail and return the Lua constant "nil" if not. Now nil simply means "doesn't exist". It's not quite the same as a "false" boolean value in most programming languages, but in Lua, if you test a variable value for "true" or "false", nil will be seen as "false".

So now we check that variable... if operatorName then if it is nil or "false" it will skip to the matching "end" and won't do anything. If if exists or is "true", then we do the following:

Use the SKIN:Bang() function and !SetOption to tell the skin to set the Text options on our first numbered set of meters to the values we extracted from the first "train". Then we "unhide" that numbered set of meters.

The we get ready for the next train by setting meterNumber = meterNumber + 1.

if meterNumber > maxTrains then break end
If we have parsed and set the meter values for the maximum number of "trains" we wanted to return, we just break out of our main loop and we are done. Head on back to the skin.

If not, then move the pointer in the entireFeed string to the next train with startPos = trainEnd + 1 and head on back to the start of the loop to do the next train.

Important Functions We Used

string.gsub(StringToSearch, PatternToFind, Repalcement)
string.find(StringToSearch, PatternToFind, PositionInTheStringToStartLooking)
string.sub(StringToSearch, PositionInTheStringToStart, PositionInTheStringToEnd)
string.match(StringToSearch, PatternMatchingExpression)

A decent reference for them is here:

http://lua-users.org/wiki/StringLibraryTutorial

But a cheat sheet for them might be:

newString, replacementCount = string.gsub(StringToSearch, PatternToFind, Repalcement)
Replaces a matched pattern in a string with a replacement. Returns two values, the new string and the number of times it was able to do the replacement.

startingPosition, endingPosition = string.find(StringToSearch, PatternToFind, PositionInTheStringToStartLooking)
Searches a string starting at PostionInTheStringToStartLooking, and returns two values, the point in the string where the matching value starts, and the point in the string where the matching value ends.

newString = string.sub(StringToSearch, PositionInTheStringToStart, PositionInTheStringToEnd)
Extracts a sub-string from a string, starting at PositionInTheStringToStart and ending at PositionInTheStringToEnd.

var1, var2, var3... = string.match(StringToSearch, PatternMatchingExpression)
Searches a string using a pattern matching expression, which should contain (captures) to extract out parts of the string to any number of matching variables you define. It will return as many string results as there are (captures) in the pattern matching expression. Note that if any of them fail, they all will.

Failure in any of these functions for pattern matching will result in a returned string value of nil. That literally means "doesn't exist", but can be treated as "false".

Obligatory final note on Lua. Lua is ALWAYS case-sensitive. newString and NewString are two entirely different variables. Keep that in mind at all times...

I hope this helps some. Feel free to ask any questions.
User avatar
balala
Rainmeter Sage
Posts: 16200
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Backwards WebParsing?

Post by balala »

Here is my rewritten code. As I promised, this code reads, together with the destination, the aimed departure time and the expected departure time. The MaxTrains variable controls the number of show trains. This number can have a value up to 15 (because the skin has 15 Item variables on the RegExp, being able this way to read up to 15 destinations, operators, aimed and expected departure times).
Sorry jsmorley, with this code I don't want to underestimate yours, which with that lua solution for sure is a great one. But I promised to rewrite my code:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
BackgroundMode=2
SolidColor=80,80,80,160

[Variables]
URL=file://c:\Users\blaci\Desktop\Film.txt
Item=(?(?=.*<d).*estination_name>(.*)</destination_name>.*<platform>.*</platform>.*<operator>(.*)</operator>.*<aimed_departure_time>(.*)</aimed_departure_time>.*<expected_departure_time>(.*)</expected_departure_time>)
MaxTrains=4

[StringStyle]
Padding=15,3,15,3
FontColor=220,220,220
FontSize=8
FontFace=Segoe UI
StringStyle=BOLD
StringAlign=LEFT
AntiAlias=1
Text=%1#CRLF#%2#CRLF#%3

[MeasureParent]
Measure=Plugin
Plugin=WebParser
URL=#URL#
RegExp=(?siU)#Item##Item##Item##Item##Item##Item##Item##Item##Item##Item##Item##Item##Item##Item##Item#

[MeasureDestination1]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=1

[MeasureOperator1]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=2
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult1"][!SetOption MeterResult1 Hidden "(#MaxTrains#<1)"][!UpdateMeter "MeterResult1"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult1"][!Redraw]

[MeasureAimedDep1]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=3

[MeasureExpectedDep1]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=4

[MeasureDestination2]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=5

[MeasureOperator2]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=6
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult2"][!SetOption MeterResult2 Hidden "(#MaxTrains#<2)"][!UpdateMeter "MeterResult2"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult2"][!Redraw]

[MeasureAimedDep2]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=7

[MeasureExpectedDep2]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=8

[MeasureDestination3]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=9

[MeasureOperator3]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=10
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult3"][!SetOption MeterResult3 Hidden "(#MaxTrains#<3)"][!UpdateMeter "MeterResult3"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult3"][!Redraw]

[MeasureAimedDep3]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=11

[MeasureExpectedDep3]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=12

[MeasureDestination4]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=13

[MeasureOperator4]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=14
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult4"][!SetOption MeterResult4 Hidden "(#MaxTrains#<4)"][!UpdateMeter "MeterResult4"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult4"][!Redraw]

[MeasureAimedDep4]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=15

[MeasureExpectedDep4]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=16

[MeasureDestination5]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=17

[MeasureOperator5]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=18
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult5"][!SetOption MeterResult5 Hidden "(#MaxTrains#<5)"][!UpdateMeter "MeterResult5"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult5"][!Redraw]

[MeasureAimedDep5]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=19

[MeasureExpectedDep5]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=20

[MeasureDestination6]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=21

[MeasureOperator6]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=22
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult6"][!SetOption MeterResult6 Hidden "(#MaxTrains#<6)"][!UpdateMeter "MeterResult6"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult6"][!Redraw]

[MeasureAimedDep6]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=23

[MeasureExpectedDep6]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=24

[MeasureDestination7]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=25

[MeasureOperator7]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=26
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult7"][!SetOption MeterResult7 Hidden "(#MaxTrains#<7)"][!UpdateMeter "MeterResult7"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult7"][!Redraw]

[MeasureAimedDep7]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=27

[MeasureExpectedDep7]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=28

[MeasureDestination8]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=29

[MeasureOperator8]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=30
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult8"][!SetOption MeterResult8 Hidden "(#MaxTrains#<8)"][!UpdateMeter "MeterResult8"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult8"][!Redraw]

[MeasureAimedDep8]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=31

[MeasureExpectedDep8]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=32

[MeasureDestination9]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=33

[MeasureOperator9]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=34
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult9"][!SetOption MeterResult9 Hidden "(#MaxTrains#<9)"][!UpdateMeter "MeterResult9"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult9"][!Redraw]

[MeasureAimedDep9]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=35

[MeasureExpectedDep9]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=36

[MeasureDestination10]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=37

[MeasureOperator10]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=38
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult10"][!SetOption MeterResult10 Hidden "(#MaxTrains#<10)"][!UpdateMeter "MeterResult10"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult10"][!Redraw]

[MeasureAimedDep10]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=39

[MeasureExpectedDep10]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=40

[MeasureDestination11]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=41

[MeasureOperator11]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=42
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult11"][!SetOption MeterResult11 Hidden "(#MaxTrains#<11)"][!UpdateMeter "MeterResult11"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult11"][!Redraw]

[MeasureAimedDep11]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=43

[MeasureExpectedDep11]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=44

[MeasureDestination12]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=45

[MeasureOperator12]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=46
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult12"][!SetOption MeterResult12 Hidden "(#MaxTrains#<12)"][!UpdateMeter "MeterResult12"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult12"][!Redraw]

[MeasureAimedDep12]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=47

[MeasureExpectedDep12]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=48

[MeasureDestination13]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=49

[MeasureOperator13]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=50
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult13"][!SetOption MeterResult13 Hidden "(#MaxTrains#<13)"][!UpdateMeter "MeterResult13"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult13"][!Redraw]

[MeasureAimedDep13]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=51

[MeasureExpectedDep13]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=52

[MeasureDestination14]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=53

[MeasureOperator14]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=54
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult14"][!SetOption MeterResult14 Hidden "(#MaxTrains#<14)"][!UpdateMeter "MeterResult14"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult14"][!Redraw]

[MeasureAimedDep14]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=55

[MeasureExpectedDep14]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=56

[MeasureDestination15]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=57

[MeasureOperator15]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=58
IfMatch=SN
IfMatchAction=[!ShowMeter "MeterResult15"][!SetOption MeterResult15 Hidden "(#MaxTrains#<15)"][!UpdateMeter "MeterResult15"][!Redraw]
IfNotMatchAction=[!HideMeter "MeterResult15"][!Redraw]

[MeasureAimedDep15]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=59

[MeasureExpectedDep15]
Measure=Plugin
Plugin=WebParser
Url=[MeasureParent]
StringIndex=60

[MeterResult1]
Meter=STRING
MeasureName=MeasureDestination1
MeasureName2=MeasureAimedDep1
MeasureName3=MeasureExpectedDep1
X=0
Y=0
MeterStyle=StringStyle

[MeterResult2]
Meter=STRING
MeasureName=MeasureDestination2
MeasureName2=MeasureAimedDep2
MeasureName3=MeasureExpectedDep2
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult3]
Meter=STRING
MeasureName=MeasureDestination3
MeasureName2=MeasureAimedDep3
MeasureName3=MeasureExpectedDep3
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult4]
Meter=STRING
MeasureName=MeasureDestination4
MeasureName2=MeasureAimedDep4
MeasureName3=MeasureExpectedDep4
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult5]
Meter=STRING
MeasureName=MeasureDestination5
MeasureName2=MeasureAimedDep5
MeasureName3=MeasureExpectedDep5
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult6]
Meter=STRING
MeasureName=MeasureDestination6
MeasureName2=MeasureAimedDep6
MeasureName3=MeasureExpectedDep6
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult7]
Meter=STRING
MeasureName=MeasureDestination7
MeasureName2=MeasureAimedDep7
MeasureName3=MeasureExpectedDep7
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult8]
Meter=STRING
MeasureName=MeasureDestination8
MeasureName2=MeasureAimedDep8
MeasureName3=MeasureExpectedDep8
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult9]
Meter=STRING
MeasureName=MeasureDestination9
MeasureName2=MeasureAimedDep9
MeasureName3=MeasureExpectedDep9
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult10]
Meter=STRING
MeasureName=MeasureDestination10
MeasureName2=MeasureAimedDep10
MeasureName3=MeasureExpectedDep10
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult11]
Meter=STRING
MeasureName=MeasureDestination11
MeasureName2=MeasureAimedDep11
MeasureName3=MeasureExpectedDep11
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult12]
Meter=STRING
MeasureName=MeasureDestination12
MeasureName2=MeasureAimedDep12
MeasureName3=MeasureExpectedDep12
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult13]
Meter=STRING
MeasureName=MeasureDestination13
MeasureName2=MeasureAimedDep13
MeasureName3=MeasureExpectedDep13
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult14]
Meter=STRING
MeasureName=MeasureDestination14
MeasureName2=MeasureAimedDep14
MeasureName3=MeasureExpectedDep14
X=0r
Y=0R
MeterStyle=StringStyle

[MeterResult15]
Meter=STRING
MeasureName=MeasureDestination15
MeasureName2=MeasureAimedDep15
MeasureName3=MeasureExpectedDep15
X=0r
Y=0R
MeterStyle=StringStyle
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Backwards WebParsing?

Post by jsmorley »

balala wrote: Sorry jsmorley, with this code I don't want to underestimate yours, which with that lua solution for sure is a great one. But I promised to rewrite my code:
Not a bit of a problem. There is almost always more than one way to skin a cat in Rainmeter. I think my only concern with using an approach that "hides" meters in the middle of a series of them is that it can cause some challenges, maybe even some limitations, with the cosmetics of the skin. Positioning, either explicit or relative, can get tricky if you just "hide" a meter in the middle of things.

But truth be told, I have a tendency to jump to Lua as soon as any "parsing" gets more than just falling-off-a-log simple. I just prefer the power and flexibility I have in an actual programming language, particularly one that is as good at string manipulation as Lua.
User avatar
balala
Rainmeter Sage
Posts: 16200
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Backwards WebParsing?

Post by balala »

jsmorley wrote:There is almost always more than one way to skin a cat in Rainmeter. I think my only concern with using an approach that "hides" meters in the middle of a series of them is that it can cause some challenges, maybe even some limitations, with the cosmetics of the skin. Positioning, either explicit or relative, can get tricky if you just "hide" a meter in the middle of things.
For sure you're right about this. But that's why I used the R operator when I positioned vertically the meters. This way the holes can be avoided, even if some meters must be hidden in the middle of the skin.
jsmorley wrote:But truth be told, I have a tendency to jump to Lua as soon as any "parsing" gets more than just falling-off-a-log simple. I just prefer the power and flexibility I have in an actual programming language, particularly one that is as good at string manipulation as Lua.
Yes, lua is more powerful, but the problem with this approach can be that many folks are not familiar with it and they just can't rewrite anything later if they need to, because don't know how that script works.
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Backwards WebParsing?

Post by jsmorley »

balala wrote:For sure you're right about this. But that's why I used the R operator when I positioned vertically the meters. This way the holes can be avoided, even if some meters must be hidden in the middle of the skin.
I think you will find that in many cases the R suffix alone won't be enough, unless you are using 0R as you are. That seems a little limiting to me. No spacing between things at all?

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterOne]
Meter=String
FontSize=15
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=First Line

[MeterTwo]
Meter=String
Y=10R
FontSize=15
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Second Line

[MeterThree]
Meter=String
Y=10R
FontSize=15
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Third Line

[MeterTrigger]
Meter=Image
X=150
Y=0
W=50
H=50
SolidColor=255,255,255,255
MouseOverAction=[!HideMeter MeterTwo][!UpdateMeter *][!Redraw]
MouseLeaveAction=[!ShowMeter MeterTwo][!UpdateMeter *][!Redraw]
test.gif
Why is there 20 between one and three when I hide two instead of 10? Because both of the 10R options are still being obeyed, even with the meter hidden. The "R" on three is STILL using the "position" of two in order to be "relative", even though two is hidden.

This can be corrected, but it needs some more action, perhaps like this:

Code: Select all

[MeterTrigger]
Meter=Image
X=150
Y=0
W=50
H=50
SolidColor=255,255,255,255
MouseOverAction=[!HideMeter MeterTwo][!SetOption MeterThree Y "0r"][!UpdateMeter *][!Redraw]
MouseLeaveAction=[!ShowMeter MeterTwo][!SetOption MeterThree Y "10R"][!UpdateMeter *][!Redraw]
test2.gif
Anyway, not criticizing. To each his own. There are ways with some thought to get a result you want with your approach, and I grant that it is simpler to implement. I'm pretty used to Lua, and it holds no fear for me. That won't be true for many, probably most.

Still, there is only one reliable way not to be a virgin anymore... ;-)
You do not have the required permissions to view the files attached to this post.
User avatar
balala
Rainmeter Sage
Posts: 16200
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Backwards WebParsing?

Post by balala »

As you said, each question has more solutions. A simpler solution to the last question, which won't let transparent spaces between the consecutive meters, would be the use of Padding option, on the meters.
But yes, I think each method has its own advantages and disadvantages. The user must take them into account and decide.
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Backwards WebParsing?

Post by jsmorley »

balala wrote:As you said, each question has more solutions. A simpler solution to the last question, which won't let transparent spaces between the consecutive meters, would be the use of Padding option, on the meters.
But yes, I think each method has its own advantages and disadvantages. The user must take them into account and decide.
Yes, Padding would be a good solution here...

Unlike X and Y on hidden meters, which was poorly thought through when Rainmeter was originally written in 2001, Padding is properly handled when a meter is hidden. A meter that is hidden should for all intents and purposes "not exist". But oh well, that ship has sailed.