It is currently April 20th, 2024, 5:22 am

WebParser is too slow

Get help with creating, editing & fixing problems with skins
User avatar
raiguard
Posts: 660
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

WebParser is too slow

Post by raiguard »

So in an attempt to make changes for Rainmeter.ini becoming UTF-16 LE, I have been rewriting my Update Checker and LoadSkin scripts to pass through WebParser. The update checker is fine (since it doesn't need to finish instantly). However, the results with my LoadSkin script are less than satisfactory.

As it is, the script itself functions perfectly. WebParser reads the file and passes it to the script when it finishes, for ReadINI to parse. However, WebParser simply takes too long. It's not a functional defect, rather it's a cosmetic and QoL issue.

See this GIF:
LoadSkin.gif
As you can clearly see, WebParser is very inconsistent in the time it takes to parse the file and pass the contents on to the LUA. Sometimes it is instantaneous, other times it takes over a second.

Like I said, this isn't a functional issue, rather an issue of the toggles not updating "right away" like you would expect them to. My question is, is there a non-convoluted way to work around this?

For comparison, the previous method of directly parsing the file with ReadINI worked practically instantaneously.

Obligatory code snippets:

Code: Select all

--[[
--------------------------------------------------

LoadSkin.lua
raiguard
v1.3.0

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

Release Notes:
v1.3.0 - 2018-6-21
- The script now gets the input from a WebParser measure, rather than directly parsing
  Rainmeter.ini (for Rainmeter 4.2+ compatibility)
v1.2.0 - 2017-12-27
 - Added ability to specifically load or unload skins, rather than always toggling them
v1.1.0 - 2017-12-7
 - Consolidated LoadConfig() into LoadSkin()
v1.0.0 - 2017-10-2
 - Initial release

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

This script loads / unlaods the specified skin or config, and sets parameters for toggle
buttons related to those skins.


INSTRUCTIONS FOR USE:
Copy this file and paste it into your own suite, then create a Rainmeter script
measure pointing to this file, like so:

[MeasureLoadSkinScript]
Measure=Script
ScriptFile=#@#Scripts\LoadSkin.lua
ToggleOn=#@#Images\toggle-on.png
ToggleOff=#@#Images\toggle-off.png
ToggleGroup=ToggleButtons

The 'ToggleOn' and 'ToggleOff' parameters are for the toggle buttons. If you are using
images, these will be the image paths for the buttons' respective on and off states. If
you are using strings, these will be the 'on' and 'off' strings that will show on the
buttons. If you do not include these parameters, the script will default to using what's
contained in '#toggleOn#' and '#toggleOff#' variables.

The 'ToggleGroup' parameter specifies the group that the toggle button meters belong to.
If you do not include this option, it will default to 'SkinToggles'.


A toggle button meter should look something like this:

[MeterToggleSkin]
Meter=Image
ImageName=[&MeasureLoadSkinScript:GetIcon('ToggledSkin')]
X=5
Y=5
W=31
H=20
LeftMouseUpAction=[!CommandMeasure MeasureLoadSkinScript "ToggleSkin('ToggledSkin')"]
DynamicVariables=1
Group=SkinToggles

The toggle buttons get their parameters via inline LUA, which requires that
'DynamicVariables=1' must be set on all the buttons. The buttons must also belong to the
'SkinToggles' group, unless otherwise specified in the script measure.

Please note that if you load or unload a skin without using the toggle buttons, the
buttons will not update until one of the buttons is clicked or the skin is refreshed.

--------------------------------------------------
]]--

debug = true

function Initialize()

	webParserName = SELF:GetOption('WebParser', 'MeasureWebParser')
	webParser = SKIN:GetMeasure(webParserName)
	rootConfig = SKIN:GetVariable('ROOTCONFIG') .. '\\'

	toggleOn = SELF:GetOption('ToggleOn', '[#toggleOn]')
	toggleOff = SELF:GetOption('ToggleOff', '[#toggleOff]')
	toggleGroup = SELF:GetOption('ToggleGroup', 'SkinToggles')

	iniTable = {}

end

function Update() end

function UpdateIniTable()

	iniTable = ReadIni(webParser:GetStringValue())
	SKIN:Bang('!UpdateMeterGroup', toggleGroup)
	SKIN:Bang('!Redraw')

end

-- Toggles the specified skin.
function ToggleSkin(config, skin, variant, state)
	-- CONFIG: The name of the config you wish to toggle, omitting the root config
	-- SKIN (optional): The file name of the skin you wish to toggle
	-- VARIANT (optional): The skin file's numeric location in the list of variants
	-- STATE (optional): The state you wish to toggle to
	config = rootConfig .. config
	if variant == nil then variant = -1 end
	local activeState = 0
	if iniTable[config] ~= nil then activeState = tonumber(iniTable[config]['Active']) end

	if skin == nil then
		if activeState > 0 then
			SKIN:Bang('!DeactivateConfig', config)
		else
			SKIN:Bang('!ActivateConfig', config)
		end
	else
		if state == true then SKIN:Bang('!ActivateConfig', config, skin)
		elseif state == false then SKIN:Bang('!DeactivateConfig', config, skin)
		elseif activeState > 0 and activeState ~= variant then SKIN:Bang('!ActivateConfig', config, skin)
		else SKIN:Bang('!ToggleConfig', config, skin) end
	end

	SKIN:Bang('!CommandMeasure', webParserName, 'Update')

end

-- Returns whether or not the specified skin or variant is loaded.
function GetIcon(config, variant)

	config = rootConfig .. config
	if variant == nil then variant = -1 end
	local state = 0
	if iniTable[config] ~= nil then state = tonumber(iniTable[config]['Active']) end

	if state == variant then return toggleOn
	elseif state > 0 and variant == -1 then return toggleOn
	else return toggleOff end

end

-- parses a INI formatted text file into a 'Table[Section][Key] = Value' table
function ReadIni(file)
  local tbl, num, section = {}, 0

  for line in string.gmatch(file,'[^\r\n]+') do
    num = num + 1
    if not line:match('^%s-;') then
      local key, command = line:match('^([^=]+)=(.+)')
        if line:match('^%s-%[.+') then
          section = line:match('^%s-%[([^%]]+)')
            if section == '' or not section then
              section = nil
              LogHelper('ReadINI: Empty section name found in source', 'Debug')
            end
            if not tbl[section] then tbl[section] = {} end
        elseif key and command and section then
          tbl[section][key:match('^%s*(%S*)%s*$')] = command:match('^%s*(.-)%s*$')
        elseif #line > 0 and section and not key or command then
          LogHelper('ReadINI: ' .. num .. ': Invalid property or value.', 'Debug')
        end
      end
    end

    if not section then LogHelper('ReadINI: No sections found in source', 'Debug') end
   
    return tbl
end

-- function to make logging messages less cluttered
function LogHelper(message, type)

  	if debug == true then
    	SKIN:Bang("!Log", message, type)
  	elseif type ~= 'Debug' then
  		SKIN:Bang("!Log", message, type)
	end

end

Code: Select all

[Rainmeter]
MiddleMouseUpAction=[!Refresh]
AccurateText=1

[Variables]
colorButtonPress=230,30,30

[MeasureScript]
Measure=Script
ScriptFile=#@#Scripts\LoadSkin.lua
ToggleOn=[\x5a]
ToggleOff=[\x56]
ToggleGroup=SkinToggles
WebParser=MeasureWebParser

[MeasureWebParser]
Measure=WebParser
URL=file://#SETTINGSPATH#Rainmeter.ini
RegExp=(?siU)^(.*)$
StringIndex=1
CodePage=1200
FinishAction=[!Log "WebParser Finished" "Debug"][!CommandMeasure MeasureScript "UpdateIniTable()"]
DynamicVariables=1

[StyleLabelString]
FontFace=Roboto
FontColor=240,240,240
FontSize=10
AntiAlias=1
X=3R
Y=-1r

[StyleToggleButton]
FontFace=ElegantIcons
FontColor=138,210,250
FontSize=12
AntiAlias=1
X=5
Y=4R
DynamicVariables=1
Group=SkinToggles

[MeterBackground]
Meter=Shape
Shape=Rectangle 0,0,120,102 | Fill Color 15,15,15 | StrokeWidth 0

[MeterToggleConfig]
Meter=String
MeterStyle=StyleToggleButton
Y=5
Text=[&MeasureScript:GetIcon('ToggledSkin')]
LeftMouseUpAction=[!CommandMeasure MeasureScript "ToggleSkin('ToggledSkin')"]

[MeterToggleConfigLabelString]
Meter=String
MeterStyle=StyleLabelString
Text="Toggle Config"

[MeterToggleSkin1]
Meter=String
MeterStyle=StyleToggleButton
Text=[&MeasureScript:GetIcon('ToggledSkin', 1)]
LeftMouseUpAction=[!CommandMeasure MeasureScript "ToggleSkin('ToggledSkin', 'Variant1.ini', 1)"]

[MeterToggleSkin1LabelString]
Meter=String
MeterStyle=StyleLabelString
Text="Toggle Skin 1"

[MeterToggleSkin2]
Meter=String
MeterStyle=StyleToggleButton
Text=[&MeasureScript:GetIcon('ToggledSkin', 2)]
LeftMouseUpAction=[!CommandMeasure MeasureScript "ToggleSkin('ToggledSkin', 'Variant2.ini', 2)"]

[MeterToggleSkin2LabelString]
Meter=String
MeterStyle=StyleLabelString
Text="Toggle Skin 2"

[MeterLoadSkin2]
Meter=String
MeterStyle=StyleLabelString
X=5
Y=3R
Text="Load Skin 2"
LeftMouseUpAction=[!CommandMeasure MeasureScript "ToggleSkin('ToggledSkin', 'Variant2.ini', 2, true)"]
MouseOverAction=[!SetOption MeterLoadSkin2 FontColor "#colorButtonPress#"][!UpdateMeter MeterLoadSkin2][!Redraw]
MouseLeaveAction=[!SetOption MeterLoadSkin2 FontColor ""][!UpdateMeter MeterLoadSkin2][!Redraw]

[MeterUnloadSkin2]
Meter=String
MeterStyle=StyleLabelString
X=5
Y=3R
Text="Unload Skin 2"
LeftMouseUpAction=[!CommandMeasure MeasureScript "ToggleSkin('ToggledSkin', 'Variant2.ini', 2, false)"]
MouseOverAction=[!SetOption MeterUnloadSkin2 FontColor "#colorButtonPress#"][!UpdateMeter MeterUnloadSkin2][!Redraw]
MouseLeaveAction=[!SetOption MeterUnloadSkin2 FontColor ""][!UpdateMeter MeterUnloadSkin2][!Redraw]
Or, if you prefer, a .RMSKIN containing the example skin and all other necessary files:
You do not have the required permissions to view the files attached to this post.
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
User avatar
SilverAzide
Rainmeter Sage
Posts: 2604
Joined: March 23rd, 2015, 5:26 pm

Re: WebParser is too slow

Post by SilverAzide »

So, it seems the only real solution is a plug-in that reads Rainmeter.ini and can tell you the state of any skin (and be sensitive to skin variants). Is that about it? Perhaps this should be something for the Suggestions board?
Gadgets Wiki GitHub More Gadgets...
User avatar
raiguard
Posts: 660
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

Re: WebParser is too slow

Post by raiguard »

SilverAzide wrote:So, it seems the only real solution is a plug-in that reads Rainmeter.ini and can tell you the state of any skin (and be sensitive to skin variants). Is that about it? Perhaps this should be something for the Suggestions board?
I was actually thinking about writing a plugin to read any INI formatted file and make the results available to you neatly. Though I have absolutely no experience in C++ or C# so it would be a massive learning experience for me (the extent of my computer science education is one semester of Java in freshman year of high school).

Something like this, maybe:

Code: Select all

[MeasureRainmeterIniParent]
Measure=Plugin
Plugin=IniParse
IniFile=#SETTINGSPATH#Rainmeter.ini

[MeasureCpuMeterActive]
Measure=Plugin
Plugin=IniParse
IniParent=MeasureRainmeterIniParent
IniSection=ModernGadgets\CPU
IniKey=Active
Group=RainmeterIniParent

[MeterCpuMeterButton]
Meter=String
MeterStyle=StyleString
(etc.)
LeftMouseUpAction=[!ToggleConfig "ModernGadgets\CPU"][!CommandMeasure MeasureRainmeterIniParent "Update"][!UpdateMeasureGroup RainmeterIniParent]
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
User avatar
SilverAzide
Rainmeter Sage
Posts: 2604
Joined: March 23rd, 2015, 5:26 pm

Re: WebParser is too slow

Post by SilverAzide »

raiguard wrote:I was actually thinking about writing a plugin to read any INI formatted file and make the results available to you neatly. Though I have absolutely no experience in C++ or C# so it would be a massive learning experience for me (the extent of my computer science education is one semester of Java in freshman year of high school).
Yeah, that's actually pretty simple. Reading (and writing) to INI files is easy and it's lightning fast because it's Win32 API calls. I guess I've been doing it since before you were born, LOL... I probably shouldn't admit that tho... I haven't tried creating a parent-child plugin before, seems like I'd have to worry about threads and other stuff that way.

What about something a little flatter:

Code: Select all

[MeasureCpuMeterActive]
Measure=Plugin
Plugin=IniParse
IniFile=#SETTINGSPATH#Rainmeter.ini
IniSection=ModernGadgets\CPU
IniKey=Active
Group=RainmeterIniParent
Also, what would you want if you had variant skins? For example, the above measure could return "2". Would you know what that meant? One of the items JSMorley mentions is that you have to know the sort order of your skins to know what "2" signifies. If that is OK with you, then it's a piece of cake.
Gadgets Wiki GitHub More Gadgets...
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: WebParser is too slow

Post by jsmorley »

We have something in the works for this...
User avatar
raiguard
Posts: 660
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

Re: WebParser is too slow

Post by raiguard »

SilverAzide wrote:Yeah, that's actually pretty simple. Reading (and writing) to INI files is easy and it's lightning fast because it's Win32 API calls. I guess I've been doing it since before you were born, LOL... I probably shouldn't admit that tho... I haven't tried creating a parent-child plugin before, seems like I'd have to worry about threads and other stuff that way.

What about something a little flatter:

Code: Select all

[MeasureCpuMeterActive]
Measure=Plugin
Plugin=IniParse
IniFile=#SETTINGSPATH#Rainmeter.ini
IniSection=ModernGadgets\CPU
IniKey=Active
Group=RainmeterIniParent
Also, what would you want if you had variant skins? For example, the above measure could return "2". Would you know what that meant? One of the items JSMorley mentions is that you have to know the sort order of your skins to know what "2" signifies. If that is OK with you, then it's a piece of cake.
Heh, probably. I don't know how old you are, but probably a bit older than 18... ;-)

My thinking with the parent-child measures was so you would only need to parse the file once, then have infinite child measures to get information from that file. If you have the entire thing as one measure, it would parse the file for every individual measure. If you can make it as efficient as you claim, it probably won't matter anyway. (EDIT: Actually, it would matter. You can't send !CommandMeasures to measure groups, so you would need to write individual !CommandMeasures to update each measure's value when the file changes, which would be a huge pain.)

As it is now, the Rainmeter.ini file ONLY gives you the variant number. I have already written my script to deal with that, so that would be fine with me.

Also, in terms of measure commands, "Update" would parse the file again and update the measure values. It would be nice to have a FinishAction as well so I could send commands to the LUA telling it to get the new values from the measures.

I feel bad. First I get stangowner to make a new SMV for me, and now you're basically offering to write me something as well... I'm sorry. :(

Edit: AND I forgot that I pressured the Rainmeter devs to make UsageMonitor because AdvancedCPU and PerfMon were so slow... I am such a freeloader...
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: WebParser is too slow

Post by jsmorley »

What we have in the works is a plugin that will allow you to specify a "config name", and it will return 1 or -1 as the number value and the name of the variant skin.ini file as the string value. This will tell you that the config is loaded, and which actual skin is running.

We are not, and to be honest have no interest in, reading Rainmeter.ini. No matter how you do that, it will be slow and expensive.
User avatar
raiguard
Posts: 660
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

Re: WebParser is too slow

Post by raiguard »

jsmorley wrote:What we have in the works is a plugin that will allow you to specify a "config name", and it will return 1 or -1 as the number value and the name of the variant skin.ini file as the string value. This will tell you that the config is loaded, and which actual skin is running.

We are not, and to be honest have no interest in, reading Rainmeter.ini. No matter how you do that, it will be slow and expensive.
That... sounds incredibly perfect. Thank you.

But once again, I'm sorry for always asking for more things. I know it's probably annoying, and I can't help but feel like you guys do things just to get me to shut up.

/teenage insecurities rant over
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: WebParser is too slow

Post by jsmorley »

raiguard wrote:That... sounds incredibly perfect. Thank you.

But once again, I'm sorry for always asking for more things. I know it's probably annoying, and I can't help but feel like you guys do things just to get me to shut up.

/teenage insecurities rant over
You got lucky that you caught us in a weak moment when we have some guilt over the minor BWC issue caused by changing Rainmeter.ini... ;-)

TheAzack9 is looking at this, and it is likely to be an external plugin that we will host here on the forums, stay tuned, I'm hoping in a couple of days.
User avatar
balala
Rainmeter Sage
Posts: 16147
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: WebParser is too slow

Post by balala »

jsmorley wrote:What we have in the works is a plugin that will allow you to specify a "config name", and it will return 1 or -1 as the number value and the name of the variant skin.ini file as the string value. This will tell you that the config is loaded, and which actual skin is running.
Even if it's not returning the name of the loaded .ini file, I think a such plugin already exist. I'm talking about the ConfigActive plugin. Am I missing something?