Often times over on Discord there are people who ask how to share measures between skins and if there is a Python measure in Rainmeter, and when they get their answer about the differences, they still ask how though.
While the methods vary since contexts are different, I decided to compile a few methods on what tools are available in Rainmeter, to at least have them written once and to never repeat the same info ever again.
Sending info from a Skin to another skin
Before you can send info between skins, you need to understand that measures and meters in skins are self-contained, as in, the skin only knows about itself and nothing about its neighbors.
An analogy would be, imagine each skin is a prisoner in a jail, and in the jail cells there are all of the skin's belongings, the prisoner can not give the physical items to the other prisoners, but they can shout really loudly and give descriptive information to the other prisoners about what is in their room.
To be more technical, at the moment, there is no way make a reference to the point in memory of another skin, at most you can only get the Window Handle of the skin.
Using the Config Parameter of any bang
So how do we send info between Skins?
Simple! When most Rainmeter bangs execute their actions, the command handler dose the job requested by the bang, and after it finishes the job, it tries to see where it should put the results by searching if the bang has something you might have noticed in the documentation, the Config parameter! By default (so most of the time), if that parameter isn't specified, the bang gets executed inside the current Config. Additionally, you can also use the * parameter instead of Config to act on all currently loaded skins, but that is up to you how you would use it (to not mess with other skins too).
Knowing that, let's look at the docs for some bangs that have this Config parameter:
!SetOption
!SetVariable
!Show, !Hide, !Toggle
!SetWindowPosition
!Update
!Redraw
!Refresh
and so on.
Oh boy, that's quite a few bangs, and some really powerful ones too! Let's use one of them as an example. Choosing at random, I've gone with !SetVariable.
Let's say you enjoy a bit of realism, and are making a skin that's a TV and a remote.
Because we want to move the remote independently of the TV screen (like how people usually want it in real life), we'll make the TV screen be a skin you can't drag and the Remote be another skin. Therefore, we will have a folder structure that will look like this.
Take a note of the way the folders and files are laid out, what you will need to get out of these is the Config (which for a recap, is explained on the Creating Skins page), the easiest way of which is to just right click a loaded skin and by just reading the very first line, that's the Config name. In the example above, we have 2 configs, "Realistic Interaction\TV" and "Realistic Interaction\Remote".
The TV skin is all ready, it's just 2 image meters, you have the Screen image meter which is the channels you're looking at, and you have the TV image meter which will be the TV overlay on top of the channels.
The remote skin is all ready too, we have 9 different meters that have LeftMouseUpAction so we can change between the channels, now the question is, how do we switch between channels?
In my approach, in the TV skin I have a variable named ScreenImage, which is used by Screen image meter (ImageName=#ScreenImage#) to show the channels. Let's say the filenames are "channel-0.png" through "channel-9.png", and we also have a file named "noise.png", which is the noise that used to appear between switching channels.
So, let's switch the channels, we are now in the Remote skin file and we have those 9 meters to change the channel. It should be trivially easy, for any button (I'll go with 1 here), logically we want to transform the phrase 'In the Config named "Realistic Interaction\TV", set the variable named ScreenImage to "channel-1.png". ', so let's do just that.
The bang format is [!SetVariable "Variable" "Value" "Config"], so our own bang should be [!SetVariable ScreenImage "channel-1.png" "Realistic Interaction\TV"] and bam, just like that we're done, if we are to go in the About Rainmeter window > Skins tab, you will notice we changed the variable, that's really good.
We're gonna go a bit tangential here, you'll notice the image didn't change, this is because variables are static, so you can either add DynamicVariables=1 to the meter so variables are re-evaluated every Update or use bangs to do the action. To do the thing we said earlier, have noise for a few bits and to actually change the image, our bang is gonna end up looking like
[!SetVariable ScreenImage "noise.png" "Realistic Interaction\TV"][!UpdateMeter "ScreenMeter" "Realistic Interaction\TV"][!Redraw "Realistic Interaction\TV"][!Delay "1000"][!SetVariable ScreenImage "channel-1.png" "Realistic Interaction\TV"][!UpdateMeter "ScreenMeter" "Realistic Interaction\TV"][!Redraw "Realistic Interaction\TV"]
Why did I come up with a TV example when the last time I watched TV in any capacity was in 2012? I have no idea.
We are also not limited to a single skin, we can do it on multiple skins, though you should be careful.
Let's say we're making a complex suite where we want the current temperature to be in multiple places (like in our skins that are replacements of the taskbar, or the top bar, or the start menu), because the heavy sweating wasn't enough already.
Because we know pulling info from the internet might get us restricted if we do too many requests, we'll keep it simple, we'll have a single invisible skin that gets the info periodically once, and then propagate that info to the other skins we want. In my approach, I have went with the same approach as the TV example for the bangs. On the measure that gets the current temperature, we have an OnChangeAction formatted like this:
OnChangeAction=[!SetVariable Temperature "[&MyTemperatureMeasure]" "Best$uite\Taskbar"][!SetVariable Temperature "[&MyTemperatureMeasure]" "Best$uite\Top"][!SetVariable Temperature "[&MyTemperatureMeasure]" "Best$uite\Start"][!Update "Best$uite\Taskbar"][!Update Best$uite\Top"][!Update "Best$uite\Start"]
This should be a pretty safe approach to doing it, however, we could have also used the * Parameter, which acts on all active skins, and we can easily transform all the bangs above into just 2:
OnChangeAction=[!SetVariable Temperature "[&MyTemperatureMeasure]" *][!Update *]
This one is a bit more dangerous because you might update Update=-1 skins and cause unintended behavior by overriding variables in other skins. While you can do whatever you want, I mostly use * only for !ShowFade and !HideFade.
Sending info to and from Rainmeter
External apps to Rainmeter
Alright, now that we know how to send info between skins, knowing that we need to use bangs, it should be easy.
If we go to the bangs page and look at the top, we will see the Command Line Arguments section:
"C:\Program Files\Rainmeter\Rainmeter.exe" !SetVariable "textSize" "12" "illustro\Clock"
Knowing this information that we can use the command line to execute bangs, then it means we can make any app, as long as it's able to open a command line shell, execute a bang in Rainmeter if we want.
That brings me to my next section though.
Read the room
Let's say we are using an API, but that API only has Python libraries and bindings, essentially, the only way to ever get the info we want is through Python.
So let's make that script. ⚒🛠💥🏗. Alright we're done, we managed to get a variable that represents the value we want, this should be very simple, all we do is just use os.popen and then set a variable/option inside Rainmeter, except... what if we don't want to do this, what if we don't want to open a CMD window to do all of this, can't we just write all the info to a JSON file, and read that file instead, if we do this, we also have the advantage that multiple other apps can open (in read mode) the same file.
Well, we obviously can, the WebParser measure was made specifically for this, where it can read files using file:///, this also decouples the Python script from ever including any hardcoded call to Rainmeter, so it can be more easily shared. Why can't we just use a plugin that has direct bindings to Python instead?
So which approach should we go for? The truth is, whatever works best for you, there is no correct answer here, but it's important to think of the context of all of these things when taking into account what to use and when to use.
RunCommand
Getting info in Rainmeter
The RunCommand plugin is powerful, it allows you to execute programs and to return their output... that is, as long as they STDOUT anything after execution.
So let's write something that has an STDOUT, which RunCommand can get. The RunCommand page already has a great example of this, if we are to open Powershell and type the command (Get-CimInstance -ClassName Win32_Processor -Property Name).Name, we will get our CPU name as the output, which is perfect. If we are to copy the Example on the page into a skin and run it, we will see the RunCommand plugin in action doing the job we want, returning the STDOUT result of the command. Again, this extends to anything that when executed will return some form of exit code with the information you want.
Sending info from Rainmeter
This one can be executed by either External Windows Commands or RunCommand too, but you can just execute commands, in which you include your parameters (pick one, any one), so for example let's open a ffplay window with our skin's width and height.
[ffplay -x [#CurrentConfigWidth] -y [#CurrentConfigHeight]]
You can do much more than that though, up to you really.
WindowMessage
An often forgotten feature is the Windows™ Message protocol, which can be used to send and receive messages, it's similar to RunCommand as in, it can only return strings. Rainmeter has a plugin for this, the WindowMessagePlugin plugin.
Getting info in Rainmeter
Welp, as said previously, we can only get strings. The thing is, if you wanna use this measure, the app you're needs to have functions specifically for the Message protocol inside it. The WindowMessage page once again has a great example of how the feature should be used, for WinAmp, where sending a command to the WinAmp window/class, it returns back to Rainmeter the info it requested, you can also command the measure to send Messages to the windows/class, which the measure that listens on a specific WindowMessage can catch.
Sending info from Rainmeter
Wait, apps can get info from Rainmeter using the Message protocol too? The answer is yes, but very limited. Whenever you start Rainmeter, there's a few invisible processes doing some other tasks you might not usually expect, one of these is the RainmeterTrayClass window/class... thingy, which is used for this purpose. At most this feature is able to return stuff like the Skin Path, the Settings Path, the Window ID of a skin window, but nothing more. This should be used for external tools created to work alongside Rainmeter.
https://github.com/rainmeter/rainmeter/blob/master/Library/RainmeterQuery.h
Community Plugins
We should not be limited only by what comes with Rainmeter, we have a whole community that makes stuff (for us).
Let's say we hate Microsoft for making such a convoluted protocol for no reason, let's pick another protocol, like WebSocket! Over on the forums, there are 2 WebSocket plugin (which are also mirrored on GitHub), which one you end up using is up to you, but it should provide to you that indeed there are more ways to get info into Rainmeter. Because we have started with the Python example, there's also the Python plugin, which should work better for talking/integrating it with your skin. I'll end with also posting about PowershellRM, just to show you should not limit yourself to only native functions and branch out there with Plugins.
And that's the end, my brain ran out of juices, I tried to search on the forum if this is a duplicate, but I didn't find something similar to this, the Rainmeter docs don't emphasize the Config and * Paramters too much, it's good that it exists, just dosen't talk for a lot like I did. It dose tell you though on the Getting Started page that "Skins can interact with other skins and applications using special commands", which is good that it's there for people who want to get in, it's attractive.
While the methods vary since contexts are different, I decided to compile a few methods on what tools are available in Rainmeter, to at least have them written once and to never repeat the same info ever again.
Sending info from a Skin to another skin
Before you can send info between skins, you need to understand that measures and meters in skins are self-contained, as in, the skin only knows about itself and nothing about its neighbors.
An analogy would be, imagine each skin is a prisoner in a jail, and in the jail cells there are all of the skin's belongings, the prisoner can not give the physical items to the other prisoners, but they can shout really loudly and give descriptive information to the other prisoners about what is in their room.
To be more technical, at the moment, there is no way make a reference to the point in memory of another skin, at most you can only get the Window Handle of the skin.
Using the Config Parameter of any bang
So how do we send info between Skins?
Simple! When most Rainmeter bangs execute their actions, the command handler dose the job requested by the bang, and after it finishes the job, it tries to see where it should put the results by searching if the bang has something you might have noticed in the documentation, the Config parameter! By default (so most of the time), if that parameter isn't specified, the bang gets executed inside the current Config. Additionally, you can also use the * parameter instead of Config to act on all currently loaded skins, but that is up to you how you would use it (to not mess with other skins too).
Knowing that, let's look at the docs for some bangs that have this Config parameter:
!SetOption
!SetVariable
!Show, !Hide, !Toggle
!SetWindowPosition
!Update
!Redraw
!Refresh
and so on.
Oh boy, that's quite a few bangs, and some really powerful ones too! Let's use one of them as an example. Choosing at random, I've gone with !SetVariable.
Let's say you enjoy a bit of realism, and are making a skin that's a TV and a remote.
Because we want to move the remote independently of the TV screen (like how people usually want it in real life), we'll make the TV screen be a skin you can't drag and the Remote be another skin. Therefore, we will have a folder structure that will look like this.
Code:
📂 Realistic Interaction↳📂 TV↳↳📄 tvscreen.ini↳📂 Remote↳↳📄 clickclack.ini
The TV skin is all ready, it's just 2 image meters, you have the Screen image meter which is the channels you're looking at, and you have the TV image meter which will be the TV overlay on top of the channels.
The remote skin is all ready too, we have 9 different meters that have LeftMouseUpAction so we can change between the channels, now the question is, how do we switch between channels?
In my approach, in the TV skin I have a variable named ScreenImage, which is used by Screen image meter (ImageName=#ScreenImage#) to show the channels. Let's say the filenames are "channel-0.png" through "channel-9.png", and we also have a file named "noise.png", which is the noise that used to appear between switching channels.
So, let's switch the channels, we are now in the Remote skin file and we have those 9 meters to change the channel. It should be trivially easy, for any button (I'll go with 1 here), logically we want to transform the phrase 'In the Config named "Realistic Interaction\TV", set the variable named ScreenImage to "channel-1.png". ', so let's do just that.
The bang format is [!SetVariable "Variable" "Value" "Config"], so our own bang should be [!SetVariable ScreenImage "channel-1.png" "Realistic Interaction\TV"] and bam, just like that we're done, if we are to go in the About Rainmeter window > Skins tab, you will notice we changed the variable, that's really good.
We're gonna go a bit tangential here, you'll notice the image didn't change, this is because variables are static, so you can either add DynamicVariables=1 to the meter so variables are re-evaluated every Update or use bangs to do the action. To do the thing we said earlier, have noise for a few bits and to actually change the image, our bang is gonna end up looking like
[!SetVariable ScreenImage "noise.png" "Realistic Interaction\TV"][!UpdateMeter "ScreenMeter" "Realistic Interaction\TV"][!Redraw "Realistic Interaction\TV"][!Delay "1000"][!SetVariable ScreenImage "channel-1.png" "Realistic Interaction\TV"][!UpdateMeter "ScreenMeter" "Realistic Interaction\TV"][!Redraw "Realistic Interaction\TV"]
Why did I come up with a TV example when the last time I watched TV in any capacity was in 2012? I have no idea.
We are also not limited to a single skin, we can do it on multiple skins, though you should be careful.
Let's say we're making a complex suite where we want the current temperature to be in multiple places (like in our skins that are replacements of the taskbar, or the top bar, or the start menu), because the heavy sweating wasn't enough already.
Because we know pulling info from the internet might get us restricted if we do too many requests, we'll keep it simple, we'll have a single invisible skin that gets the info periodically once, and then propagate that info to the other skins we want. In my approach, I have went with the same approach as the TV example for the bangs. On the measure that gets the current temperature, we have an OnChangeAction formatted like this:
OnChangeAction=[!SetVariable Temperature "[&MyTemperatureMeasure]" "Best$uite\Taskbar"][!SetVariable Temperature "[&MyTemperatureMeasure]" "Best$uite\Top"][!SetVariable Temperature "[&MyTemperatureMeasure]" "Best$uite\Start"][!Update "Best$uite\Taskbar"][!Update Best$uite\Top"][!Update "Best$uite\Start"]
This should be a pretty safe approach to doing it, however, we could have also used the * Parameter, which acts on all active skins, and we can easily transform all the bangs above into just 2:
OnChangeAction=[!SetVariable Temperature "[&MyTemperatureMeasure]" *][!Update *]
This one is a bit more dangerous because you might update Update=-1 skins and cause unintended behavior by overriding variables in other skins. While you can do whatever you want, I mostly use * only for !ShowFade and !HideFade.
Sending info to and from Rainmeter
External apps to Rainmeter
Alright, now that we know how to send info between skins, knowing that we need to use bangs, it should be easy.
If we go to the bangs page and look at the top, we will see the Command Line Arguments section:
"C:\Program Files\Rainmeter\Rainmeter.exe" !SetVariable "textSize" "12" "illustro\Clock"
Knowing this information that we can use the command line to execute bangs, then it means we can make any app, as long as it's able to open a command line shell, execute a bang in Rainmeter if we want.
That brings me to my next section though.
Read the room
Let's say we are using an API, but that API only has Python libraries and bindings, essentially, the only way to ever get the info we want is through Python.
So let's make that script. ⚒🛠💥🏗. Alright we're done, we managed to get a variable that represents the value we want, this should be very simple, all we do is just use os.popen and then set a variable/option inside Rainmeter, except... what if we don't want to do this, what if we don't want to open a CMD window to do all of this, can't we just write all the info to a JSON file, and read that file instead, if we do this, we also have the advantage that multiple other apps can open (in read mode) the same file.
Well, we obviously can, the WebParser measure was made specifically for this, where it can read files using file:///, this also decouples the Python script from ever including any hardcoded call to Rainmeter, so it can be more easily shared. Why can't we just use a plugin that has direct bindings to Python instead?
So which approach should we go for? The truth is, whatever works best for you, there is no correct answer here, but it's important to think of the context of all of these things when taking into account what to use and when to use.
RunCommand
Getting info in Rainmeter
The RunCommand plugin is powerful, it allows you to execute programs and to return their output... that is, as long as they STDOUT anything after execution.
So let's write something that has an STDOUT, which RunCommand can get. The RunCommand page already has a great example of this, if we are to open Powershell and type the command (Get-CimInstance -ClassName Win32_Processor -Property Name).Name, we will get our CPU name as the output, which is perfect. If we are to copy the Example on the page into a skin and run it, we will see the RunCommand plugin in action doing the job we want, returning the STDOUT result of the command. Again, this extends to anything that when executed will return some form of exit code with the information you want.
Sending info from Rainmeter
This one can be executed by either External Windows Commands or RunCommand too, but you can just execute commands, in which you include your parameters (pick one, any one), so for example let's open a ffplay window with our skin's width and height.
[ffplay -x [#CurrentConfigWidth] -y [#CurrentConfigHeight]]
You can do much more than that though, up to you really.
WindowMessage
An often forgotten feature is the Windows™ Message protocol, which can be used to send and receive messages, it's similar to RunCommand as in, it can only return strings. Rainmeter has a plugin for this, the WindowMessagePlugin plugin.
Getting info in Rainmeter
Welp, as said previously, we can only get strings. The thing is, if you wanna use this measure, the app you're needs to have functions specifically for the Message protocol inside it. The WindowMessage page once again has a great example of how the feature should be used, for WinAmp, where sending a command to the WinAmp window/class, it returns back to Rainmeter the info it requested, you can also command the measure to send Messages to the windows/class, which the measure that listens on a specific WindowMessage can catch.
Sending info from Rainmeter
Wait, apps can get info from Rainmeter using the Message protocol too? The answer is yes, but very limited. Whenever you start Rainmeter, there's a few invisible processes doing some other tasks you might not usually expect, one of these is the RainmeterTrayClass window/class... thingy, which is used for this purpose. At most this feature is able to return stuff like the Skin Path, the Settings Path, the Window ID of a skin window, but nothing more. This should be used for external tools created to work alongside Rainmeter.
https://github.com/rainmeter/rainmeter/blob/master/Library/RainmeterQuery.h
Community Plugins
We should not be limited only by what comes with Rainmeter, we have a whole community that makes stuff (for us).
Let's say we hate Microsoft for making such a convoluted protocol for no reason, let's pick another protocol, like WebSocket! Over on the forums, there are 2 WebSocket plugin (which are also mirrored on GitHub), which one you end up using is up to you, but it should provide to you that indeed there are more ways to get info into Rainmeter. Because we have started with the Python example, there's also the Python plugin, which should work better for talking/integrating it with your skin. I'll end with also posting about PowershellRM, just to show you should not limit yourself to only native functions and branch out there with Plugins.
And that's the end, my brain ran out of juices, I tried to search on the forum if this is a duplicate, but I didn't find something similar to this, the Rainmeter docs don't emphasize the Config and * Paramters too much, it's good that it exists, just dosen't talk for a lot like I did. It dose tell you though on the Getting Started page that "Skins can interact with other skins and applications using special commands", which is good that it's there for people who want to get in, it's attractive.
Statistics: Posted by Jeff — Today, 9:35 pm — Replies 0 — Views 16