Shop Mobile More Submit  Join Login

Similar Deviations
This is a code walkthrough of my "Do I Need a Jacket?" Rainmeter weather skin.  The goal of the skin is to replicate doineedajacket.com in a Rainmeter weather skin, including the ability of the end user to set their own temperature thresholds.  Here's a screenshot of what this skin looks like:



Download Here

The Groundwork

First, lets get the basics out of the way.  We of course need [Rainmeter], [Metadata], and [Variables] sections.

[Rainmeter]
Update=1000
Author=Flying Hyrax | flyinghyrax.deviantart.com
DynamicWindowSize=1
MouseOverAction=!Execute [!ShowMeterGroup buttons][!Redraw]
MouseLeaveAction=!Execute [!HideMeterGroup buttons][!Redraw]

[Metadata]
Name="Do I Need a Jacket?"
Version=1.0
License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0
Information=Tells you if you'll need a jacket, based on current weather information from Yahoo! Weather and threshold values which you can set. | ALL credit for idea due to www.doineedajacket.com | Variables/options consolidated in Settings.txt in skin's root folder.

You can ignore the MouseOver/LeaveAction bits in the [Rainmeter] section for now - that's not important until later.

In order to make it simple for users to edit the options for our skins, and also to make things easy on ourselves if we decide to do more than one version of the skin, we'll put the necessary variables in a separate file using include:

[Variables]
include=#CURRENTPATH#settings.txt

This directs the skin to import the contents of a file called "settings.txt" that is in the same folder as the skin itself.  For reference, here are the contents of that file:

[Variables]
loc=USWA0395
unit=f

hotTemp=80
coldTemp=60
freezeTemp=32

windLow=15
windHigh=30
windExtreme=77

color=200,200,200,200
font1=Segoe UI Light
font2=Helvetica
size1=28
size2=14

Notice the repeat of the [Variables] section header.  This is necessary because when include imports the contents of an external file, those contents are read as if they are the last thing in the skin.  If we left out the [Variables] header in settings.txt, then Rainmeter would think our variables were part of the last measure or meter in our skin.

WebParser Measures

Now, lets get on to the meat of the matter, as it were.  We'll need some WebParser measures to collect the weather data we'll be using from Yahoo! Weather.  I'm not going to explain WebParser and Regular Expressions in this walkthrough, but there are already numerous helpful resources on that.  For instance, this WebParser primer.  Anyway:

[mWeatherData]
Measure=PLUGIN
Plugin=PluginsWebParser.dll
URL=http://weather.yahooapis.com/forecastrss?p=#loc#&u=#unit#
RegExp="(?siU)<yweather:location city="(.*)".*region="(.*)".*country="(.*)".*<yweather:units temperature="(.*)".*speed="(.*)".*speed="(.*)".*<pubDate>(.*)</pubDate>.*<yweather:condition  text="(.*)".*temp="(.*)".*low="(.*)""
UpdateDivider=900
ErrorString="ixnay on the eatherway"
DecodeCharacterReference=1
ForceReload=1
Debug=0

[mCity]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=1

[mRegion]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=2

[mCountry]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=3

[mTempUnit]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=4

[mWindUnit]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=5

[mWind]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=6

[mDate]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=7

[mCondition]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=8

[mTemp]
Measure=Plugin
Plugin=PluginsWebParser.dll
URL=[mWeatherData]
StringIndex=9

Calc Measures

Now we get to the fun stuff.  First, we're going to use a CALC measure to compare the current temperature to the values in "settings.txt".  We'll make four basic ranges that the temperature can be in: Hot, warm, cold, and freezing.  The four ranges are separated by the "hotTemp", "coldTemp", and "freezeTemp" variables.  

[mCalcTemp]
Measure=CALC
Formula=#freezeTemp# > mTemp ? 3 : (#coldTemp# > mTemp ? 2 : (#hotTemp# > mTemp ? 1 : 0))

Here's what we're doing with that formula.  There are three nested If/Then statements there (see here, scroll down to "Conditional Operator").  To paraphrase: "Is mTemp (the current temperature) less then #freezeTemp#?  If yes, then return a value of 3.  If not, then is it less than #coldTemp#?  If yes..."

You get the idea.  Since we're starting at the lowest temperature threshold, we don't have to use both a greater than and a less than comparison to figure out which range the temperature is in.  If it's colder than #freezeTemp#, then it's freezing, and if it's not colder than #freezeTemp# but is colder than #coldTemp#, then it has to be in the "cold" range, and so on.

Let's move on. This skin is going to be text based.  We need a way to show a little text description of just how cold or warm it is, for humor's sake.  We'll use another Calc measure, with some Substitutes:

[mTempSubs]
Measure=CALC
Formula=mCalcTemp
Substitute="3":"darn cold out","2":"chilly out","1":"temperate","0":"positively balmy"

If we only needed this text, than we could just put the "Substitute=..." back inside [mCalcTemp].  However, we need both - we'll use the raw numbers (0-3) later on, so we need a second measure.

Now we're going to do basically the same thing, only comparing the current wind speed to the wind thresholds:

[mCalcWind]
Measure=CALC
Formula=#windLow# > mWind ? 0 : (#windHigh# > mWind ? 1 : (#windExtreme# > mWind ? 2 : (#windExtreme# <= mWind ? 3 : 4)))

[mWindSubs]
Measure=CALC
Formula=mCalcWind
Substitute="0":"not windy","1":"somewhat breezy","2":"very windy","3":"your computer just blew away"

The main difference is that [mCalcWind] has one more nested conditional operator than [mCalcTemp], and the numbers go the other way - zero first progressing to 3 instead of 3 decreasing to zero.  It could be done in either order, but it makes things easier to keep track of later when we actually determine whether or not you need a jacket.  Before we do that, we need one more bunch of conditional operators.

In English, the conjunction used between two phrases depends on the agreement or disagreement of the phrases (English majors everywhere will cringe when they read that description, but that's basically it.)  In this case, we have two phrases - one for the temperature and one for the wind.  Depending on conditions, there are several possibilities:
   1) It's cold and windy
   2) It's cold, but not windy
   3) It's warm, but windy
   4) It's warm and not windy.
Now we just need to express that in a formula:

[mCalcJunction]
Measure=CALC
Formula=(mCalcTemp > 1) && (mCalcWind > 0) ? 1 : ((mCalcTemp > 1) && (mCalcWind <= 0) ? 2 : ((mCalcTemp <= 1) && (mCalcWind > 0) ? 2 : ((mCalcTemp <= 1) && (mCalcWind <= 0) ? 1 : 0)))
Substitute="1":"and","2":"but","0":","

Notice the logical AND operator (&&), just don't confuse it with the English language and I was talking about above.  If the temperature range is greater than 1 (cold or freezing), AND the wind range is greater than 0 (breezy, windy, or a freaking hurricane), then you need a jacket because of both the temperature and the wind.  If that's not the case, then we use our nested conditional operators to move on to the next case.  Each case gets either a 1 (substituted for "and") or a 2 (substituted for "but").

Lastly, we're finally going generate an actual yes or no answer to our original question: do you need a jacket?

[mCalcYN]
Measure=CALC
Formula=(1 < mCalcTemp) || (0 < mCalcWind) ? 1 : 0
Substitute="1":"","0":" don't"

This one is simple; the "||" is a logical OR operator.  If the temperature is in the cold or freezing ranges, OR if the wind is above the "0" range (OR both), then the conditional operator evaluates to true (1), if not, it evaluates to false (0).  

Meters

...then we use a substitute so that we can plug this info into our main string meter:

[Y/N]
Meter=STRING
MeasureName=mCalcYN
FontFace=#font1#
FontSize=#size1#
FontColor=#color#
X=0
Y=0
AntiAlias=1
SolidColor=0,0,0,1
Text="You%1 need a jacket."

The "%1" in the "Text=" line is the output of the [mCalcYN] measure from above, which because of the substitute will be either nothing or the word " don't".  So the string will read either "You need a jacket" or "You don't need a jacket."

The string meter for the secondary text is bit of a beast to look at, but it's pretty simple if you break it down:

[Elaboration]
Meter=STRING
MeasureName=mTempSubs
MeasureName2=mWindSubs
MeasureName3=mCalcJunction
MeasureName4=mCondition
MeasureName5=mTemp
MeasureName6=mTempUnit
MeasureName7=mWind
MeasureName8=mWindUnit
MeasureName9=mCity
MeasureName10=mRegion
MeasureName11=mCountry
MeasureName12=mDate
FontFace=#font2#
FontSize=#size2#
FontColor=#color#
X=5
Y=43
AntiAlias=1
SolidColor=0,0,0,1
Text="It's %1, %3 %2."
ToolTipText="%9 %10 %11#CRLF#%12#CRLF#%4, %5 %6#CRLF#Wind: %7 %8"

The first three measures are used in "Text=" and come from our the temperature range substitution, wind range substitution, and the measure we used to find the right conjunction depending on the weather conditions.  Measures 4 through 12 are all for the "ToolTipText=".  This way, the user can mouse over the string and see the actual current weather data, the location that Yahoo! weather associates with the weather location code in "settings.txt," and a timestamp of the last time the weather feed data was updated.

Lastly, we're going to make three little buttons that show up when you mouse over the skin.  These image meters will open "settings.txt," refresh the skin, and open the readme file:

[Settings]
Meter=IMAGE
ImageName=set.png
SolidColor=0,0,0,1
X=0
Y=0
W=24
PreserveAspectRatio=1
AntiAlias=1
ImageTint=#color#
Group=buttons
Hidden=1
LeftMouseUpAction=!Execute ["#CURRENTPATH#settings.txt"]
ToolTipText="Open settings.txt"

[Refresh]
Meter=IMAGE
ImageName=fresh.png
SolidColor=0,0,0,1
X=5R
Y=r
W=24
PreserveAspectRatio=1
AntiAlias=1
ImageTint=#color#
Group=buttons
Hidden=1
LeftMouseUpAction=!Refresh
ToolTipText="Refresh skin"

[Help]
Meter=IMAGE
ImageName=help.png
SolidColor=0,0,0,1
X=5R
Y=r
W=24
PreserveAspectRatio=1
AntiAlias=1
ImageTint=#color#
Group=buttons
Hidden=1
LeftMouseUpAction=!Execute ["#CURRENTPATH#info.rtf"]
ToolTipText="Open ReadMe"

This was what those Mouse Actions in the [Rainmeter] section were for.  You can see that all three image meters are part of the "buttons" group and are hidden by default; but because of those Mouse Actions they will show whenever the user's mouse goes inside the skin's window.


And that's pretty much all there is to it.  As an example skin this might not be the best choice, because the skin needs several other files to work with this code: the settings.txt file, the images for the buttons, etc.  However, I though the discussion of the Calc Formulas at least might be helpful to someone, and it was fun and good practice to write this out.

- fh
Add a Comment:
 
No comments have been added yet.

Youtube CSS!

Journal Entry: Thu Sep 15, 2011, 11:49 PM
Get the CSS!
YouTube CSS by Pakaku


Oh wow, after only one day, plus a few hours afterwards tweaking the thing, I'm going to release this CSS. It's just too good to keep from everyone :faint:

It's a pretty straightforward CSS, with one special feature, which is the Youtube video that's at the top of every journal. At the moment of writing this journal, it's still a Subscriber-only feature. But if you're still interested in downloading the CSS and using it, here's how you'd edit the video that's shown:

  1. Install CSS
  2. Find the video you want to show
  3. Copy the video's code
  4. Start a new journal entry and click 'Edit skin'
  5. Paste the code you copied as an embedded link (Click here to learn how to embed a video)
  6. Click Preview to make sure everything worked okay


If you're feeling adventrous, and want a video on your page in style, why not try out the CSS?

Add a Comment:
 
No comments have been added yet.

The Elite Elect #88

Journal Entry: Sun Nov 16, 2014, 10:28 AM
ProfileGalleryFavouritesFriends


"The Elite Elect 88"

GREATEST BLESSING by mentalraymaya
had to get the train by OliverJules SP5 by neverblind ...venezia XXVIII... by roblfc1892
dark by MartaSyrko ...budapest XXXIV... by roblfc1892 *** by Tarasov
Insomnia by Mrs-White from nowhere to elsewhere II by Tom-Ripley 56f6bc9bc5207685585f4e1bc7eb5ee0 by kuvars
Sleep attacks by Blakk-mamba Vital Spaces by Blakk-mambaAngry Schoolgirl by PatrickMonnier
 november rain by JoanLlado Psycho: Victim #6 by Miguel-Santos The Gravediggers by DougNZ
Saturday Shopping Crawd by RaeymaekersP Tokyo!! by Relin27 ny2014_032 by br53199
Pobiti Kamani by thenSir Burning Horizon by o0oLUXo0o Under the Table by LAlight
<da:thumb id="491936479"/> 1009 by Nigrita Guichard Lake II : Spectacular Dawn by MaximeCourty
Padarn Tree by CharmingPhotography Umbrella by ildiko-neer It's a foggy world today.........XV by Betuwefotograaf
 
Untitled by jonniedee Hands and heads by xbastex Untitled by jonniedee
The Happening by mldzz Violence Is Funky by bliXX-a March Of The Pigs by bliXX-a
Instant 3315 by SUDOR Untitled by jonniedee a lady and two trams by onurrus
*** by Tarasov Jefferson Market Branch by steeber LIGHT AND DARKNESS by mirpiphotography
Crane House No.3 by wulfman65 Where do we go? by Trashins Birds in a Cage by Draken413o
Ignore This Shit! by bliXX-a Old Soviet steam locomotive. by fly10 Delta Machine by bliXX-a
A quiet moment by erynlasgalenphotoart
Tirol Tour-24 by wulfman65

On a personal note,would like to take this opportunity to thank each and everyone who
went out of their way to come here and wish me a Happy Birthday back on the 1st.
I never got round to replying to you all individually and that`s very unlike me
so apologies at the same time.
Big thanks too for these very welcomed and appreciated Birthday dedications from
these great DA friends here!


:iconjacac:  Postcard from China 20 by JACAC Come On Brain, Work by EarthHart :iconearthhart:
The Emptiness by k-i-mm-i-e Life in the back streets by RezzanATAKOL
:iconk-i-mm-i-e: :iconrezzanatakol:

Can`t link Kimmie`s icon for some reason
:o
k-i-mm-i-e.deviantart.com/

:iconthe-yard-collective:
For Lovers of Street Photography:
Add a Comment:
 
No comments have been added yet.

The dA PRO Camera Bag is back with loads of improvements! Now with a larger access flap for your camera, more space for the tools you need, better padding all around, and of course a larger pocket to hold your ever-important mobile phone. It's all built for quick and easy access, designed by photographers for photographers.

Fits SLR/DSLR camera w/ lens attached and up to four extra lenses.

Perfectly sized pocket for iPhone/MP3 player– conveniently located on the front of the shoulder strap.

In the rain, pull the weatherproof cover from the hidden bottom compartment and protect your gear.

Memory card storage system with additional utility pouches for all the little stuff photographers usually lose!

Add a Comment:
 
No comments have been added yet.

Welcome back! If you haven't done so already, have a read of Getting the most out of your CSS Journals Part One. Part two deals with Dressing up your mood boxes, adding extra lines to your Journal title, and will touch upon dynamic images in your journal, amongst other things. So without further delay, let's begin!

Mood boxes

"Mood boxes", or the little box that shows what you're listening to etc, is now available to everyone. Subscribers and non subscribers alike. It would seem that there's not a whole lot you can do with these, asides from change the colours and such. But something a few people don't know, is that you can move your mood box anywhere you like, and do so much more than just colourising it. In this example, we'll show you how to shift your mood box to the right, just like in This example.

CSS:

.journalbox .list {
position:absolute;
right:0px;
bottom:0px;
width:25%;
}


And that's all you need! When you look at your mood box, it will be 25% wide, and sitting on the bottom-right hand corner of your journal! Please note that this will look shocking on smaller journal entries, or some journal views (ie. on the userpage, full-view etc.). Now you can stretch out your box to wherever you wish, put half on one side, half on the other, hide bits of your mood box. It's all up to your imagination!

(A tip: If you find you can't rid of the mood box, because there is no "remove mood" feature, try this code:

.journalbox .list {
display:none;
}

to rid of the mood box altogether)

Journal Taglines

Ever wish you could add a small bit of text underneath your journal title, like you can on the major blogging systems out there (Think Wordpress and such)? It's really easy with CSS. Just add the following to your "Header" section of your journal, and your CSS section:

HTML:

It's all in a day's work!


CSS:

.journal-subtext {
top:30px;
position:absolute;
}

.journalbox .journaltop {
font-size: 0px;
}


Just remember to tweak "30" to whatever value you wish, depending on where your .journaltop h2 header is placed, and if you want to keep the date / time, leave out the font-size:0px; And there you have it! It'll now show your sub-text (or personal message) below your journal title. This can be done with images, CSS menus (as per last week's example) or anything else dA supports. It's only limited by your imagination!

Dynamic Images

Imagine if you could use your dA journal to tell people what TV show you were watching at that very second, or if you could display your computer's uptime, temperature and the last program you ran, or even display news headlines, images from your gallery, new posts on your forums, or so much more. Impossible you say? No way! Thanks to the magic of GD

For those in the know, or people who have used Last.fm, AudioScrobbler, or any other services like that, GD / Dynamic Images should be nothing new. For the rest of you, GD is a PHP library that lets us draw text on a picture, or draw a completely new picture from scratch. The output, can be a standard JPG, GIF or PNG file, which dA is more than happy to support. The trick comes from writing a PHP script that returns an image instead of text. Now I won't go into PHP and GD and the like. That's wayyyy beyond the scope of our article. Instead I'll give you an example. Check out my Userpage, and look at my webcam. That is a dynamically created image. Every time it's viewed, it grabs my base image (the green), adds text to it, depending on what's in the database, and shoots it off as a JPG picture. Our browser sees the JPG data, and displays it as such.

With a bit of manipulation, you can write a script that reads your computer's uptime, shows off some images, even parse a website to get images for displaying. It's limited by PHP's engine, and your imagination!

Have a read of PHPFreak's GD Tutorial for a starter on GD. Some PHP knowledge is required here ;D


That's all for this week. Remember, if you have any suggestions, note them to Grayda for inclusion in the next part of Getting the most out of your CSS Journals. Thanks for reading, :+fav: if you get some use out of this, and keep watching the skies!

Previous Weeks
Getting the most out of your CSS Journals Part One
Add a Comment:
 
No comments have been added yet.

[ended] Omnimo 6 Release +$100 Contest

Journal Entry: Wed Oct 16, 2013, 12:30 PM
+ $100 Desktop Contest

Ladies and Gentlemen

After almost 18 months, we're finally proud to announce
The Grand release of Omnimo 6.0.
The biggest
Most important
And likely, the last major update
In the lifecycle of this project.



Life's circumstances have a tendency to change rapidly, and indeed, both of us (me and Xyrfo) had experienced rather significant changes which kept us from activelly working on this project. We started it as we were teenagers, but we've grown up and our new lives have begun. Perhaps Omnimo is a tribute to this transition; a hobbyist project that had become more serious with every release. We put our faith in the hope that our efforts were not in vain, and that the long wait had been worth it.

In our previous releases, we hesitated making the changelog out of fear that we won't cover everything that we've worked on. Incidentally, it's also the case for Omnimo 6.0 - we've improved it so much, I don't even know where to begin describing it. Instead, I'll leave it up to you to see and experience the new features.

In the next several weeks, I will try to produce a series of Informational bulletins that will showcase some of the features, both obvious and hidden - revolving around this new version.

TL;DR: It's here

Meanwhile, to celebrate the Grand Release,
we're officially announcing a contest:

CONTEST OVERVIEW

We like to see how people use Omnimo, and even more so the interesting ideas people come up with. There had been similar contests before which were quite fun. This time we have fantastic prizes!
RULES:


» Omnimo must be used as the centerpiece
» Do not use any age inappropriate material on your desktop
» You may enter up to 3 screenshots
» Screenshots must be preferably PNG and submitted to Customization>Screenshots>Windows
» To enter, post a comment with the link or thumbnail to your Deviation in the comments section of this journal
» Do not use multiple DeviantArt accounts to get more entries

JUDGING & PRIZES


Your desktops will be judged both by us and by the community. There won't be a specific standard of measurement for quality or originality, so pretty much everyone has a chance of winning.

The best desktops may be subject to a voting / poll, for 3 days after the contest is over. Winners will be announced after 2 weeks and 3 days from now, prizes to be paid in cash by PayPal transfer (can be negotiated if you don't have PayPal), we'll be spending a whooping $100 out of our lunch money for the prizes:



Let the games begin, may the odds be ever in your favor.
And remember: participation matters.



View entries so far


Add a Comment:
 
No comments have been added yet.

Foobar7 by Markkoenig

    I've been asked to write a tutorial on skinning foobar2000, because many f2k users encounter difficulties modifying skins or simply using them. I'm not an expert on the subject, I've started by installing a few skins and trying to understand how that worked ; I still think it's the best way to get a good handle on it, though a long and sometimes frustrating way. I invite you to try and see by yourself, to keep in mind that errors come all along and to share any advice, question, difficulty or anything you want to say, may you be new or expert. I also recommend to read this good tutorial (though it's partly based on outdated PanelsUI), maybe this less developed one, some of the very useful HydrogenAudio's forums and the few links I'll drop here.

This first part intends to be an overview of the whole thing ; a second part made by slowboyfast will guide you through a step-by-step on how to create graphic elements only with foobar2000's drawing functions ; and a third part by FlipOut69 will propose detailed presentations of some customization functions. If you don't want to go too far into skinning and are okay with the standard interface, the first link hereabove might satisfy you, specially CUI's specific options. Now if you're ready, let's hop in! :)



The tutorial

Part 1 by Markkoenig :iconmarkkoenig: : The Basics

Part 2 by slowboyfast :iconslowboyfast: : Composition & Graphics

Part 3 by FlipOut69 :iconflipout69: : Creating Panel Switchers



Recommandations


  • Always keep your components up-to-date, unless it may cause compatibility issues.
  • When installing a skin, check the version of foobar it's been designed for. Avoid installing old skins on current foobar versions and vice versa.
  • Backup your files if you're not sure of what you're doing ; export or copy your working scripts when you're about to take risks.
  • I personnally recommend to install skins in fresh foobar2000 installations to avoid compatibility issues between different versions of a component (WSH Panel Mod in particular). That being said, if you've checked the components and their version and there's no conflict, you sure can install several skins on a single foobar2000. Beware the updating of components, which can disable a part of or the entire skin ; however it doesn't happen a lot 'cause AFAIK no component is currently under development.
  • I mostly use portable installation instead of normal installation, for it's easier to install skins into and you can multiply your foobar2000s as much as you need, knowing that there will be no conflict of any sort (no need to update anything or to have two skins aside). Just copy the core files (database etc.) from an installation to the other to keep your library and media player settings.
  • If you're good at coding and you're not too ambitious, you can stick with foobar2000's code to create graphic elements. Otherwise, you can decide to use Photoshop or Gimp or any other program to create images that you'll assemble using Panel Stack Splitter.



Useful links




Installation


Installing a downloaded skin depends on :

  • Its complexity. The use of certain components might require new folders, system files, etc. Look out for instructions on skins' description.
  • The type of foobar2000 installation.
    • On a portable installation, just throw everything in the installation's folder.
    • On a standard installation, the typical process is to put the skin and the component in foobar2000's main directory, whereas the configuration files go to C:\Users\[you]\Documents\AppData [hiddenfolder]\Roaming\foobar2000\configuration. Some components manually installed may be situated in ...Roaming\foobar2000\user-components folder. Again, it's rarely that simple.

Wether you want to install or create a skin, remember that the common use is to create a "skins" folder in foobar2000's directory, where one puts the skin files.



Default UI


Foobar2000 comes with a default interface (DUI) that is easy to customize but very limited. It works perfectly and is very fast, but its features are quite limited to changing fonts, colors, stock elements, their position and their size. You access its parameters the first time you run foobar2000, and later either in Preferences/Display/Default UI or in [toolbar]/View/Layout/Enable Layout editing mode. In Prefs/Display/Default UI/Playlist View, you can add custom columns and grouping schemes using foobar2000's Title Formatting. Using the View/Layout/Enable layout editing mode command (under CUI, it's View/Layout/Live editing), you'll be able to choose and move the UI elements within the player's window. All the elements are on the same layer, placed side-by-side ; to put several elements at the same place, you have to add Tab Stacks (Enable layout editing mode, then right-click on the UI element and "Add splitter"/"Add panel").



Columns UI + Panel Stack Splitter


To really enhance your foobar2000 UI's experience, you have to use the Columns UI component, better combined with Panel Stack Splitter. All by itself, Columns UI won't offer many more possibilities. There used to be a few other components but they are outdated and no longer supported (Panels UI for example).

To edit in Prefs/Display/Columns UI, CUI allows you to organize the elements more freely using splitters to divide the window's space. There are six tabs in CUI's preferences page :

  • The Main tab is where you import and export .fcl files and edit the window title string.
  • The Layout tab is where you edit most of the graphic UI stuff, that's where we're gonna spend a load of time.
  • The Status bar tab allows you to edit the bottom status bar which displays technical information. It's rather often deactivated anyway.
  • The Notification area is also self-explanatory, it controls (optional) tooltip and tray icon options.
  • The Filter tab is only useful when you use the Filter tabs element.
  • And the Artwork tab is useless, for we use much more efficient artwork components or methods.

PSS allows the interface to be organized by panels rather than horizontal or vertical splitters as in DUI and CUI alone, and according to a hierarchical pattern : one or a group of elements can be assembled on one part, which can be displayed alongside or submitted to others. This way, you can have different parts in your skin behaving differently. And it is able to run scripts by itself. Instead of stand-alone elements separated by visible frames, PSS can combine multiple elements on one panel, with or without frame. The elements on a panel can thus behave differently than the panel itself, managed by Columns UI. Though it may seem a bit messy, it offers way more possibilities than the tabs system of DUI.

The Layout

So let's talk about this Layout tab. First thing is to erase whatever element is here by default (NG Playlist normally) and to insert an horizontal or vertical splitter as a base, or a PSS (right click/insert). Then all you have to do is adding elements and/or panels according to what you need and to the way you want to organize your skin. Let's look at this example below. The upper Splitter in the Layout tab is the main splitter, it's the general "background" of the skin, though it's displayed first in the list it's the lowest of all ; but every submitted element is listed from top to bottom, as it is on the actual skin. Remember that any element called in a PSS script is overpassed by the element(s) this PSS is running. The hierarchy is to be set in this same Layout tab, by right-clicking on the elements and selecting "Move up/Move down" ; the presets menu allows you to store several organizations in the same .fcl. Technically, one can create multiple and totally different skins and store them in a single .fcl file. The panels and elements can be edited by selecting the main Panel Stack Splitter and clicking "Configure" (you could also directly select the panel or element you want to edit, but I often observed that it causes the hierarchally upper element to reset).

Here on Monolite Flat, you can see that the elements are organized one above the other. The waveform seekbar is the lowest component above the first/main Panel Stack Splitter, which runs the prev/play/pause/next buttons (that's why you don't see them in the list, they're in its script) ; then there is another PSS above, running the Library Tree, then the ELPlaylist which is submitted to the main PSS, etc. until the toolbar buttons, the upper elements of the skin. See the second screenshot, it should be clearer.
3 by Markkoenig
The WSH elements are gathered on one panel (the black one) and though there are 3 of them, two other components just nearby and 3 buttons on the first PSS, it all seems united and consistent.

2 by Markkoenig
So the first important thing to think about when one is designing or modifying a skin is its organization. The overall structure is ruled by "layers" assembled in hierarchy, in which you add and dispose panels of various sizes, in which you place one or several UI elements. To add panels or UI elements, right-click on a splitter in the Layout tab and choose "Change splitter type" or "Insert panel".

The Coordinates

Once you click "Configure", the PanelList tab shows the current UI elements (PSS or components) that are submitted to (or managed by, if you want) the PSS you are currently editing. The elements are listed from top to bottom, meaning the first one is on the top layer, the last one on the bottom layer ; you have to take this into account if one panel or element is supposed to cover the other(s). Tick "forced layout" to allow panels and elements oversizing each others and other tricks. You can use TF to edit values, which would be much more practical. For example, coordinates set by numbers are okay to set an element's position (x,y), but it may be wiser to use a TF value like %_width% (returns the windows' width) if the window must be resizable. Choose one or the other, depending on the case. Ticking "ignore panel size limits" allows you to arrange elements out of their panel's limits. "Hide panel on startup" should be used when you have a button to make the element appear. Be careful when editing those, quite often you don't see the element you just placed, cause it's out of range or hidden under another element.

4 by Markkoenig

Everytime you are done with editing one of these dialog boxes, hit "Apply". You will notice that when you hit "OK", you can't edit hierarchally previous elements : the dialog box is empty. You have to validate the whole modification by hitting "OK" down to "Apply" on the Layout tab before you can go back editing PSS and elements.

Colours and Fonts

Just like in the DUI options page, you can edit the colours and fonts of both stock foobar2000 UI and some third-party elements ; assuming you haven't set them already (using scripts for example). Some third-party components allow you to access their UI preferences both by right-clicking in it and by using this menu.

5 by Markkoenig



Scripts


The other important aspect of f2k skinning is the use of scripts in order to customize how a UI element works and/or is displayed. Scripts rely on a syntax called Title Formatting (TF) that I recommend you learn to handle (see the links above). Multiple display options and stock components of foobar2000 use TF : the window title, the status bar, the notification area, the columns playlist, the metadata display, etc. ; as well as several components among which Panel Stack Splitter, ELPlaylist/NG Playlist, LibraryTree, etc. TF may be quite easy, on the other hand it is limited in its possibilities. If you are ambitious, you might prefer switching to JavaScript through WSH Panel Mod (see below).

NG Playlist

Its display options are to be found in Prefs/Columns UI (or DUI)/Playlist View or by right-clicking on one of its column titles/Preferences.

ELPlaylist

Its display options can be found in the Prefs/Columns UI/Layout tab or by right-clicking anywhere in it/Settings (if you do so, remember to save your skin in .fcl or any other modification applied will reset ELPlaylist to its last saved settings, due to some sort of priority conflict). ELPlaylist's modifications are divided into two sections :

  • what is true for the entire element : Style, Field definition, Behaviour and Misc tabs ;
  • what is true for one script at a time (ELPlaylist can host multiple scripts, i.e. multiple custom library displays, which you can switch between) : Script and Grouping tabs.

Now let's take the tabs one by one. Remember there's a manual that explains everything (but in a very poor english). And notice that the scripts are run from top to bottom : for example, if you want to display a text string in front of a background image, make sure the image is called before the string ($imageabs... and later $drawstring). Again, taking a look into other people's work is very useful.

  • The Script tab is where you... write scripts. Track list refers to the library track list of your music : this is where you display informations like title, length, track number, etc. Group header is also self-explanatory. As for the track list, you can display a hell lot of things in many ways using forms, colors, pictures, etc. Those two sections are updated everytime there is a track change or an action from the user (adding files, refreshing, etc.). Per Second is updated every second (+ go to Behaviour tab and tick "update every second") ; you can make a progress bar or display sliding informations, etc. Popup (Background) and Popup (Track) can be used to display info within a popup when the cursor stays over a track ; this option's settings are in the Misc tab.
6 by Markkoenig
  • The Style tab gathers the main display options for the entire UI element, as well as a few ones that can impact the track list. You can choose a custom default font, or directly edit it the scripts ; you can use multiple fonts and if you edit fonts in scripts, they will overpass this custom default font. You can edit odd and even items background to have an "iTunes look", but you can do it in the scripts as well. "Enable Visual Style" was designed for Vista and doesn't seem to be of any use. "Pseudo-Transparent" background however can be interesting, but we'll come back later on the topic of transparency.
7 by Markkoenig
  • The Grouping tab sets the way your playlist viewer is organized. Group by allows you to name and store several grouping formats (such as Artist/Album, Albums only, tracklist sorted by artist, title, length, year, etc.) that you can access in the context menu ("Group by"). These options use TF. Group format refers to the way tracks are gathered, Sort format to the way tracks and/or groups are listed. Row height is the height of a single track line. Playlist Filter allows you to filter the library when using this particular grouping format ; i.e. you can have a grouping format called "Genre" where the tracks are filtered by genras. Finally, Associated titleformat script name allows you to link one particular grouping format to one particular display script (assuming you have given your script a name in Script tab/"Titleformat Script Presets"), that will automatically be activated when you activate this grouping format.
8 by Markkoenig
  • The Field definition tab stores values that you don't want to repeat everytime it's needed in your scripts. Instead of writing a color value like 245-145-93-125 or a long path twenty times in your script, you can store them here and use the $get function.
9 by Markkoenig
  • The Behaviour tab allows you to edit what happens when you interact with ELPlaylist.
  • The Misc tab refers to the popup feature.

Panel Stack Splitter

It works just the same way with scripts. When you are in a Splitter Settings dialog box, there are four tabs (see above for PanelList tab).

  • The Script tab allows you to use TF, just like in ELPlaylist. "PerTrack" will react on track change, "PerSecond" will be updated every second. This is where you can create text strings, buttons, display info, display images (graphic UI elements, background, artist pic, covers�), etc. ; see TF links for the possibilities. There is no complexity limit to your design, but keep in mind that the more elements the stock UI engine of foobar2000 has to create, the slower it will start and run. If you plan on creating a heavy and very complex skin, it can be wiser to switch to JScript.
  • The Behaviour tab rules the behaviour of the panel.
  • Global Variables is the equivalent of "Field definition".

You can submit as many PSS as you want to each others. If you select a component in the PanelList and hit "Configure", it will open its Preferences window ; sometimes this feature isn't available.

WSH Panel Mod

It's an extremely powerful component that simply host scripts in JScript or VBScript language. Almost anything is possible with it, far beyond TF's possibilities : you probably have seen some of Br3tt's work already. What's hard here is that you have to know JScript, or, as I'm doing so far, you can copy other people's scripts and modify them as far as you understand. A few samples are included in the component's package though. WSH Panel Mod is quite the key to an impressive skin.



Syntax basics


You probably won't write the syntax fluently right from the start, specially if it's your first contact with coding. It takes some time and -let's be honest- some endeavour to memorize sentences/functions, to avoid forgetting stuff and to get a good handle on the syntax' possibilities. Basically, the syntax used here is divided into two categories : values/field references and functions. A value or field reference is an information (size, position, color, opacity, metadata, count, etc.), a function is an action that is processed whether once-and-for-all (for example when starting foobar2000), at a specified moment (on track change...) or when you call it (by clicking on a button...). So generally speaking, a script is no more than a "find information, then call the action" process developped and multiplied. Thus, when you want to write a function, always make sure you bring the required information, or you might confront a crash or a big nothing and want to bang your head till you find the error. So we're gonna see the basics of understanding and organizing a script. Let's take a few examples.

We are in the Script tab of the first PSS. On this very simple script, I called a few graphic elements, some drawn, the others based on pictures ; some static, others dynamic.
  • In yellow is a basic information provider, the directory of the images used in this script. The $puts function allows you to store an information/value under a name, so that it's easier to call all along the script. Here I named 'skin_path' the whole information that is the path, '%ps_foobar2000_path%\skin... etc.' ; so below, in the blue zone, I just need the $get function to call this value and then finish the path of the picture : $get(skin_path)\play.png.
  • In the pink zone I drew the background of the toolbar and the slight grey border beneath. As the script runs from top to bottom, I drew the light grey background first ; then I added a gradient, and then the border. As you can see, I use both numerical and dynamic values. The dynamic values, may they be numerical values like here or metadata (like track tags), are called with %...% (whereas functions are called by $...). Here I wanted to set a background that fits the window's width even after resizing, so I called the value %_width%, which is relative and constantly updated.
  • In the blue zone, I drew the playback buttons. You can see a very common use of the $if function (or $if statement) which allows you to set various actions depending on the state of a value or a component : it works like $if(thisvaluereturnstrue, dothisaction, otherwisedothat). This is where the syntax begins to be more complex. This function here rules the play/pause button, which won't react the same way whether there's an ongoing playback or not. To avoid drawing two simple buttons side-by-side, I have to use $if to call alternatively two different buttons at the same place : if playback is going, "pause" button is displayed, if not, "play" button is displayed.
11 by Markkoenig

Now let's have a look at the ELPlaylist scripts, on another skin. CUI, PSS and ELP (and some more) use the same syntax, but there are function that don't work on some panels and/or some components for obvious reasons of inconsistency. By the way, as you can see on the screenshots below, I often tidy my script up so it's more convenient to read.
  • So first, in yellow, are the stored values I already told you about. I could have put them in the Field definition tab, but I like to have them constantly under my eyes. Your call. As you can see, I'm using the $if function into the $puts function : that means that the stored value called 'text_color', for example, will change whether the track is playing or not, is selected or not. This way, if you just want the text color to change according to the track state, doing this will prevent you from typing "$if(%el_isplaying%, then all the $drawstring, and if not, use this other string", which is very much longer : instead, you type one string and only the stored value changes once. The important thing here is to keep in mind that most of the functions can be combined and articulated to obtain complex results (or simpler ones for the rest of the script). You can also notice the ')))' at the end of the 4th line : the more functions you combine, the more parenthesis you'll have to remember.
  • In the "BG" zone, I set the "iTunes look" (/!\ not talking about the overall dark background : that background comes in a more global canvas. Here the ELP's background is transparent). To obtain the odd/even background effect, I used the $ifequal and $mod functions to activate the odd background when the result of [index number of the item in the group] divided on base 2 = 1, i.e. every other track ; instead of using fixed values like $if(%el_item_index%,$or(1,3,5,7, etc.)).
  • Same trick in the blue zone to add rectangle layers when the item is selected or playing.
  • In the green zone are listed the metadata to display on the item row. The $font function lets you set the font, size and type you want (N.B. : the $drawstring function only works with TrueType fonts ; otherwise, use the GDI renderer with $drawtext/$drawtextex, that can use both OpenType and TrueType). Then I add one string per data, but you can display as many metadata as you want on a single string.
  • Same thing with the indicator, for which I used a special font.
  • For the rating indicator (invisible here), I used $pad and $repeat according to the rating metadata to display the right number of characters side-by-side.
  • Eventually, I told ELPlaylist to add a shadow effect (gradient) on the item which index number equals the total number of items in the group, i.e. the last item of the group.
12 by Markkoenig
13 by Markkoenig
So here are some examples on how to organize a script and combine functions on complex syntaxes. Now let's sum up the main and important rules.
  • '//' disables the line.
  • Be careful when combining functions, you need to have the correct number of '(' and ')', and generally speaking watch your typing, any missing coma will disable or mess up the whole rest of the script. When you write a sketch, use tabulations instead of straight lines to see clearly what you're doing.
  • It's better to plan what you want to do before doing it, in order to decrease the risk of errors and sterile wanderings.
  • Always prefer relative and dynamic values over fixed/numerical values when you can use both.
  • Think of every data you need before calling the function.
  • The script is run from top to bottom, so organize it wisely.
  • Create or modify one thing at a time so as to quickly find the bug.
  • When you combine several functions, remember that the deepest function will be called first, and the other(s) above in this processing order. If you write $if($add($div($get(..., f2k will first get the value, then divide, then add and finally check if it returns true or false. Sometimes it's not the case : a single $if is very simple, for the process will be 'if[value]=true, then apply this, otherwise apply this'. So you have to think in terms of priorities.
  • If you can't find a function to do straightly something you need, there's almost always a way to go round.
  • Write the script in its appropriate component/tab.
  • Simplier is always better.


Transparency


Transparency in skinning is a very nice feature, sometimes a pain to handle though. There are various options to get transparency :

  • In Columns UI "Main" tab, the box "Use transparency" applies to the whole window. I used to use it with DUI, black background and white font, I could have something... but that's pretty much all.
  • In the Splitter Settings/Behaviour tab, the box "Pseudo Transparency" allows a background set in the Script tab to appear through an element. It won't let you see a component placed under.
  • Some components have a transparency feature (ELPlaylist, Lyrics Show Panel 3, Biography View, Channel Spectrum...) which may work different ways.

To sum up with, the default transparency feature lets the panel background appear through the component ; I guess that's why they're often labeled "pseudo transparency". It may be a bit difficult to build complex transparency-based skins with the default feature, you may have to find tricks. Otherwise, there are two external means :

  • Again, JScript can apply transparency (almost) anywhere.
  • The other and easier means to obtain transparency is the UI_Hacks component, which hasn't been approved by foobar2000 creators and is often flagged as buggy and/or Trojan (because of its name). I personally never had any problem with it. It provides true transparency, so that you won't see any background but your Windows desk or any program running behind. You will find its option in the Prefs/Default UI/Main Window tab. From here you can :
    • Modify or disable the window frame.
    • Modify the window's behaviour, lock it, disable its resizing or edit a specified size.
10 by Markkoenig
  • And what's interesting here, add various transparency modes. "Disable" disables Windows' Aero effect (provided you're using it). "Glass Frame" replaces Windows' default frame with one that you can edit with the values below. I used this mode in Lux to have a selective blur. "Sheet of glass" seems to activate a selective and blurred alpha transparency based on the colors' weight : a dark or solid color won't be affected whereas a light color will be affected by the transparency. Apart from these modes, the box "Alpha transparency" allows you to apply transparency to the whole window : selecting "Disable" and ticking "Alpha transparency" will make the window totally transparent but will leave the frame solid, unticking it and choosing "Glass Frame" will leave the background solid, etc. Don't forget that these transparency features depend on Aero's availability. If you're using visual styles that disable Aero, you might obtain slightly different results. Moreover, some fonts and antialiasing/ClearType don't like alpha transparency, so you might sometimes experience ugly renderings.
Snap19 by Markkoenig



    Well I guess that's it for now. Hoping you'll have a better understanding of the core of foobar2000 skinning, before reading the next parts. Feel free to comment and share, and to ask any question ; I'm open to suggestions and corrections/addenda. Let's do some skins! :)
Hey Deviants!

Here is the first part of a tutorial made by ~slowboyfast, ~FlipOut69 and myself. It's still a work in progress for one good reason, it might grow bigger and bigger with your comments, questions and suggestions. I really hope this first part will satisfy you, so leave comments! :eager: Now available as a downloadable .png : link
Show
Add a Comment:
 
No comments have been added yet.

If you are unfamiliar with the binary (base 2) numeral system, you may want to do some reading here before going through the rest of this post.

The aim of this skin is to create a binary-coded decimal clock in Rainmeter.  To keep this short, we'll just do the hours and minutes (especially since the code for seconds is virtually identical to the minutes anyway).



Download Full Skin Here
(package includes several variants - we'll be going through "Decimal_HM.ini")

The Groundwork


To begin, we'll need [Rainmeter], [Metadata], and [Variables] sections.  Once again, we'll use include to put the variables in an external file (called Appearance.txt) so the end user can change settings easily.

[Rainmeter]
Author=Flying Hyrax
Version=2001000

[Metadata]
Name=Binary Clock - Encoded Decimal - Hours and Minutes
Version=1.0
Information=
License=Creative Commons Attribution-Non Commercial-Share Alike 3.0

[Variables]
include=#CURRENTPATH#Appearance.txt

(Here's the contents of Appearance.txt, for reference:)

[Variables]

Hour0=200,200,200,100
Hour1=250,250,250,200

Min0=200,200,200,100
Min1=250,250,250,200

Sec0=200,200,200,100
Sec1=250,250,250,200

Radius=6
Space=12

Next, back in the main file, we need Time Measures to return the "raw" time information that we will be converting into binary:

[mHour]
Measure=TIME
Format=%H

[mMin]
Measure=TIME
Format=%M

Math!


Now we get to the fun stuff.  For this version of the binary clock, the different place values of the decimal time each have their own column.  In other words, we'll need to convert the tens place of the hour and one's place of the hour into binary separately.  Lets say it's 1 o'clock PM (13 in the 24-hour representation) - we'll need a way to separate the "1" and the "3" in 13 from each other.  To do this, the solution I came up with is to use some Calc Measures and the FRAC and TRUNC functions.  Both functions take a number as their input. FRAC returns the fractional part (i.e. FRAC(1.234) returns .234) while TRUNC returns the whole number (TRUNC(4.321) returns 4).  Here's what we do:

[mHourTens]
Measure=CALC
Formula=(TRUNC(mHour * 0.1))

[mHourOnes]
Measure=CALC
Formula=(10 * (FRAC(mHour * 0.1)))

[mMinTens]
Measure=CALC
Formula=(TRUNC(mMin * 0.1))

[mMinOnes]
Measure=CALC
Formula=(10 * (FRAC(mMin * 0.1)))

Here's what's going on there, using 13:27 as an example time.  [mHourTens] multiplies 13 by 0.1, giving 1.3, then uses TRUNC to isolate the 1.  [mHourOnes] multiplies 13 by 0.1 (gives 1.3), isolates the fractional part (0.3), then multiplies by 10 (returns 3).  [mMinTens] and [mMinOnes] do the same thing, but with the numerals in 27.  Using these Calcs, we now have the individual digits that make up the current time: 1, 3, 2, and 7.

More math!


Now we're going to start converting those numbers into binary.  I'm sure there are several ways to do this, but for this particular skin this is the approach I took, and I think it worked rather well.  Using this method, you get one measure for each of the place values of the binary numbers, each of which returns either a 1 or a 0.  These raw numbers can then be used in a variety of different meter types.  

The algorithm I used is described here, under "Short division by two with remainder."  Read that section to better understand what I'm doing in these next sections, but the TL;DR is that to convert a number from decimal to binary, you divide by two, and the remainder becomes the next least-significant bit (the next place to the left, starting at the radix point).  The quotient of the division operation is again divided by two leaving a remainder of 1 or 0; repeat until the quotient is zero.  For example, here are the Calc measures to convert the decimal ten's place into binary values:

[mHourTensBin1]
Measure=CALC
Formula=(mHourTens >= 1) ? (mHourTens % 2) : 0
DynamicVariables=1

[mHourTensBin2]
Measure=CALC
Formula=(mHourTens >= 2) ? ((TRUNC(mHourTens / 2)) % 2) : 0
DynamicVariables=1

Here's the rundown.  In [mHourTenBin1], we first check if the ten's place of the decimal hour ("1" from our example above; the result of [mHourTens]) is greater than or equal to 1 using a Conditional Operator (see here also).  If the input is at least 1, we use the Modulo operation (%) to do the remainder division.

Modulo divides one number by another number, but returns the remainder of the division operation, not the quotient.  For instance, (5  % 2) would return 1, and (7 % 4) would return 3.  Especially, note that 1 % 2 returns 1, because 1 / 2 in remainder division is 0 remainder 1.  (That's pretty much the principle that makes this skin work).

[mHourTensBin2] is where things start to get fun.  Now that we've evaluated the 2^0 place using [mHourTensBin1], we need to evaluate the next least significant bit - in this case, 2^1.  Recall from here that we do this by performing remainder division on the whole integer answer from our first operation.  So what [mHourTensBin2] does, after checking that the input value is greater than or equal to 2, is divide the input by 2, TRUNC the answer to give us the whole integer, then use the Modulo operation on that.  If you are confused by my mediocre writing, it helps to use actual numbers as examples.  For instance, "2" (the ten's place if our hour is between 20 and 23):

2/2=1.0
TRUNC(1.0)=1
1%2=1

Alright then.  Next, the decimal one's place.  For the decimal ten's place there are only three possible options (0, 1, or 2), which can be represented by 2 binary places (2^0 and 2^1).  But the one's place has 10 possible options (the digits 0-9), which needs 4 binary places (2^0, 2^1, 2^2, 2^3).

[mHourOnesBin1]
Measure=CALC
Formula=(mHourOnes >= 1) ? (mHourOnes % 2) : 0
DynamicVariables=1

[mHourOnesBin2]
Measure=CALC
Formula=(mHourOnes >= 2) ? ((TRUNC(mHourOnes / 2)) % 2) : 0
DynamicVariables=1

[mHourOnesBin4]
Measure=CALC
Formula=(mHourOnes >= 4) ? ((TRUNC((TRUNC(mHourOnes / 2)) / 2)) % 2) : 0
DynamicVariables=1

[mHourOnesBin8]
Measure=CALC
Formula=(mHourOnes >= 8) ? ((TRUNC((TRUNC((TRUNC(mHourOnes / 2)) / 2)) / 2)) % 2) : 0
DynamicVariables=1

This is the same process we used for the ten's place, only expanded to include up to the binary 8's (2^3) place.  The higher the place value, the longer the formula necessary to evaluate the binary value of that place, because you must do one TRUNC(n/2) for every previous place.  Hence in [mHourOneBin8] there are 3 TRUNC functions, to account for the binary 4's, 2's, and 1's places.  For instance, if [mHourOnes] was returning 9, then the process that [mHourOnesBin8] would go through would look something like this:

9/2=4.5
TRUNC(4 .5)=4
4/2=2
TRUNC(2)=2
2/2=1
TRUNC(1)=1
1 % 2 = 1

Just a bit more math...


Next, we need to do the same thing, only for the minutes.  The ten's place for the minutes has 6 possible values (0-5), meaning we need 3 binary places; while the one's place has 10 options (0-9), so we need 4 binary places for that.

[mMinTensBin1]
Measure=CALC
Formula=(mMinTens >= 1) ? (mMinTens % 2) : 0
DynamicVariables=1

[mMinTensBin2]
Measure=CALC
Formula=(mMinTens >= 2) ? ((TRUNC(mMinTens / 2)) % 2) : 0
DynamicVariables=1

[mMinTensBin4]
Measure=CALC
Formula=(mMinTens >= 4) ? ((TRUNC((TRUNC(mMinTens / 2)) / 2)) % 2) : 0
DynamicVariables=1

[mMinOnesBin1]
Measure=CALC
Formula=(mMinOnes >= 1) ? (mMinOnes % 2) : 0
DynamicVariables=1

[mMinOnesBin2]
Measure=CALC
Formula=(mMinOnes >= 2) ? ((TRUNC(mMinOnes / 2)) % 2) : 0
DynamicVariables=1

[mMinOnesBin4]
Measure=CALC
Formula=(mMinOnes >= 4) ? ((TRUNC((TRUNC(mMinOnes / 2)) / 2)) % 2) : 0
DynamicVariables=1

[mMinOnesBin8]
Measure=CALC
Formula=(mMinOnes >= 8) ? ((TRUNC((TRUNC((TRUNC(mMinOnes / 2)) / 2)) / 2)) % 2) : 0
DynamicVariables=1

...and that's all the binary conversion stuff.  To do seconds, the formulas would be the same as for the minutes - only changing all the measure names.

Auto Scaling


Now assuming that you are still reading and that your brain isn't fried (mine sure was when I was writing this), we can move on to appearance stuff.  We'll be using Roundline meters to display our binary "1s" and "0s," and it would be nice if the end user could just change some numbers in our Appearance.txt file to make the skin whatever size they wanted.  Unfortunately, you cannot use a formula directly in a Roundline meter, so we can't just do some math in each meter to make the various pieces scale to each other.  What we can do is put the math in some more Calc measures, then plug those measures into our meters using Dynamic Variables.

[mHourTensYPos]
Measure=CALC
Formula=((#Radius# * 4) + (#Space# * 3))
DynamicVariables=1

[mMinSecYPos]
Measure=CALC
Formula=((#Radius# * 2)+(#Space# * 2))
DynamicVariables=1

[mMinSecXPos]
Measure=CALC
Formula=(#Space# * 1.5)
DynamicVariables=1

[mSizeCalc]
Measure=CALC
Formula=(#Radius# * 2)

[mAlways1]
Measure=FREEDISKSPACE
Total=1
MinValue=0
MaxValue=1
UpdateDivider=86400

Here's a quick diagram showing where these formulas are coming from ([mHourTensYPos], specifically):



[mAlways1] is a little "cheat" measure that we plug in as the MeasureName for all of our Roundline meters - by using it, all our Roundlines will appear as solid dots.

Meter Styles


To accomplish the "lit" vs "unlit" effect for the binary dots, I decided to use Meter Styles and Dynamic Variables.  Here's the code:

[sHour0]
LineColor=#Hour0#

[sHour1]
LineColor=#Hour1#

[sMin0]
LineColor=#Min0#

[sMin1]
LineColor=#Min1#

[sCircle]
LineWidth=1
LineLength=#Radius#
LineStart=0
StartAngle=0
RotationAngle=6.28
Solid=1
AntiAlias=1
MeasureName=mAlways1
X=r
Y=#Space#R
W=[mSizeCalc]
H=[mSizeCalc]
DynamicVariables=1

[sCircle] contains all the settings that are common to every single Roundline meter, so that we don't have to repeat them a bunch of times.  (You'll see soon how short this makes all of the Meters.)  The other Styles are used with the output of the various measures (which will be either a 1 or 0) to change which color the various meters will be, by plugging the output of a Calc measure into the "MeterStyle=" line of the matching meter as a Dynamic Variable...

Meters


...like this:

[HourTens2]
Meter=ROUNDLINE
MeterStyle=sCircle | sHour[mHourTensBin2]
X=#Space#
Y=[mHourTensYPos]

[HourTens1]
Meter=ROUNDLINE
MeterStyle=sCircle | sHour[mHourTensBin1]

[HourOnes8]
Meter=ROUNDLINE
MeterStyle=sCircle | sHour[mHourOnesBin8]
X=#Space#R
Y=#Space#

[HourOnes4]
Meter=ROUNDLINE
MeterStyle=sCircle | sHour[mHourOnesBin4]

[HourOnes2]
Meter=ROUNDLINE
MeterStyle=sCircle | sHour[mHourOnesBin2]

[HourOnes1]
Meter=ROUNDLINE
MeterStyle=sCircle | sHour[mHourOnesBin1]

[MinuteTens4]
Meter=ROUNDLINE
MeterStyle=sCircle | sMin[mMinTensBin4]
X=[mMinSecXPos]R
Y=[mMinSecYPos]

[MinuteTens2]
Meter=ROUNDLINE
MeterStyle=sCircle | sMin[mMinTensBin2]

[MinuteTens1]
Meter=ROUNDLINE
MeterStyle=sCircle | sMin[mMinTensBin1]

[MinuteOnes8]
Meter=ROUNDLINE
MeterStyle=sCircle | sMin[mMinOnesBin8]
X=#Space#R
Y=#Space#

[MinuteOnes4]
Meter=ROUNDLINE
MeterStyle=sCircle | sMin[mMinOnesBin4]

[MinutesOnes2]
Meter=ROUNDLINE
MeterStyle=sCircle | sMin[mMinOnesBin2]

[MinuteOnes1]
Meter=ROUNDLINE
MeterStyle=sCircle | sMin[mMinOnesBin1]

[spacer]
Meter=IMAGE
SolidColor=0,0,0,1
X=R
Y=R
W=#Space#
H=#Space#

As you can see, all the Roundline meters share sCircle, but depending on whether or not the meter is for the hours or minutes, and whether or not the Measure for the appropriate binary place returns a 1 or 0, the second style is either sHour0, sHour1, sMin0, or sMin1; and this second style contains the LineColor for the Roundline meter.  The last meter is just an empty box with the dimensions of our #Space# variable, so that when the skin snaps to the edges of the screen or other skins, there is a gap between the circles and the edge.

That's pretty much it.  The difficulty with this skin is definitely the Calc measures used to change the Time measures into individual binary digits.  Except for that, the rest of the skin is fairly straightforward.

-fh
Add a Comment:
 
No comments have been added yet.

To accomplish scaling in meters (Bar, Bitmap, Button, Histogram, Rotator), where W and H are used to set the size of the "meter", not the size of the "image", you can use a "universal" TransformationMatrix formula (Formula is by Xanci):
TransformationMatrix = (1 * #Scaler#); 0; 0; (1 * #Scaler#); ((1 - #Scaler#) * [#CURRENTSECTION#: X]); ((1 - #Scaler#) * [#CURRENTSECTION#: Y])


Adding this to your meter, and controlling a new #Scaler# variable, you can easily scale these meters to any desired size.

Here are 3 skins that demonstrate using TransformationMatrix to scale Rotator meters.
Rotator Resize by Eclectic-Tech

One uses modified coding from Rotate an Image Around its Center Tip by Smurfier and JSMorley in the Rainmeter Documentation.

The second one uses a color wheel rotating around the center.

And the third one demonstrates scaling a dial image; any image that rotates from a central point.

All files are commented to help explain how it works, and how to use it with your images.

RotatorResizeRect.ini

[Rainmeter]
Update = 50
Dynamicwindowsize = 1
LeftMouseDoubleClickAction = [!WriteKeyValue Variables Scaler 1][!Refresh]

[Variables]
Scaler = 1
ImageW = 128
ImageH = 64
MaxDiameter = (SQRT(#ImageW# ** 2 + #ImageH# ** 2))
ImageSW = (#ImageW# * #Scaler#)
ImageSH = (#ImageH# * #Scaler#)
; The rectangle image size is 128x64 pixels
; To use with a different image, change the ImageW & ImageH 'numbers' to match your image size

[mScalerUp]
Measure = Calc
Formula = (#Scaler# + 0.01 > 2.0)? 2.0 : (#Scaler# + 0.01)
DynamicVariables = 1
UpdateDivider = -1
; Measures the next largest size; limits it to 2x

[mScalerDn]
Measure = Calc
Formula = (#Scaler# - 0.01 < 0.1)? 0.1 : (#Scaler# - 0.01)
DynamicVariables = 1
UpdateDivider = -1
; Measures the next smallest size; limits it to .1x

[MeasureRotate]
Measure = Calc
Formula = (MeasureRotate % 360) + 1
MaxValue = 360
; Controls rotation

[MeterBG]
Meter = Image
SolidColor = 0,0,0,128
W = (#MaxDiameter# * #Scaler#)
H = (#MaxDiameter# * #Scaler#)
MouseScrollUpAction = [!WriteKeyValue Variables Scaler ([mScalerUp])][!Refresh #CurrentConfig#]
MouseScrollDownAction = [!WriteKeyValue Variables Scaler ([mScalerDn])][!Refresh #CurrentConfig#]

[MeterRotate]
Meter = Rotator
MeasureName = MeasureRotate
ImageName = #@#images\rectangle.png
X = ([MeterBG:W] / 2)
Y = ([MeterBG:H] / 2)
OffsetX = (#ImageW# / 2)
OffsetY = (#ImageH# / 2)
TransformationMatrix = (1 * #Scaler#) ; 0 ; 0 ; (1 * #Scaler#) ; ((1 - #Scaler#) * [#CURRENTSECTION#: X]) ; ((1 - #Scaler#) * [#CURRENTSECTION#: Y])
DynamicVariables = 1

[MeterScaleText]
Meter = String
X = ([MeterBG:W] / 2)
Y = ([MeterBG:W] / 2)
FontColor = 255,255,255
StringAlign = CenterCenter
StringEffect = Shadow
Text=Scale: #Scaler#


RotatorResizeColorwheel.ini

[Rainmeter]
Update = 50
DynamicWindowSize = 1
LeftMouseDoubleClickAction = [!WriteKeyValue Variables Scaler 1][!Refresh]

[Variables]
Scaler = 1
ImageW = 128
ImageH = 128
MaxDiameter = (SQRT(#ImageW# ** 2 + #ImageH# ** 2))
ImageSW = (#ImageW# * #Scaler#)
ImageSH = (#ImageH# * #Scaler#)
; The color wheel image size is 128x128 pixels
; To use with a different image, change the ImageW & ImageH 'numbers' to match your image size

[mScalerUp]
Measure = Calc
Formula = (#Scaler# + 0.01 > 2.0)? 2.0 : (#Scaler# + 0.01)
DynamicVariables = 1
UpdateDivider = -1
; Measures the next largest size; limits it to 2x

[mScalerDn]
Measure = Calc
Formula = (#Scaler# - 0.01 < 0.1)? 0.1 : (#Scaler# - 0.01)
DynamicVariables = 1
UpdateDivider = -1
; Measures the next smallest size; limits it to .1x

[MeasureRotate]
Measure = Calc
Formula = (MeasureRotate % 360) + 1
MaxValue = 360
; Controls rotation

[MeterBG]
Meter = Image
SolidColor = 0,0,0,128
W = (#MaxDiameter# * #Scaler#)
H = (#MaxDiameter# * #Scaler#)
MouseScrollUpAction = [!WriteKeyValue Variables Scaler ([mScalerUp])][!Refresh #CurrentConfig#]
MouseScrollDownAction = [!WriteKeyValue Variables Scaler ([mScalerDn])][!Refresh #CurrentConfig#]

[MeterRotate]
Meter = Rotator
MeasureName = MeasureRotate
ImageName = #@#images\colorwheel.png
X = ([MeterBG:W] / 2)
Y = ([MeterBG:H] / 2)
OffsetX = (#ImageW# / 2)
OffsetY = (#ImageH# / 2)
TransformationMatrix = (1 * #Scaler#); 0; 0; (1 * #Scaler#); ((1 - #Scaler#) * [#CURRENTSECTION#: X]); ((1 - #Scaler#) * [#CURRENTSECTION#: Y])
DynamicVariables = 1

[MeterScaleText]
Meter = String
X = ([MeterBG:W] / 2)
Y = ([MeterBG:W] / 2)
FontColor = 255,255,255
StringAlign = CenterCenter
StringEffect = Shadow
Text = Scale: #Scaler#



Extra Credit - Scaling Dials

What if you want to scale a 'dial' image; one that rotates outward from a constant center point, like a clock hand?

Well that is only slightly different, because the dial image is 1/2 of the total image size, and the offset never changes.

Here is coding for a radar dial. (Included in the download above)

RotatorResizeDial.ini

[Rainmeter]
Update = 50
Dynamicwindowsize = 1
LeftMouseDoubleClickAction = [!WriteKeyValue Variables Scaler 1][!Refresh]

[Variables]
Scaler = 1
ImageW = 61
ImageH = 55
MaxDiameter = ((SQRT(#ImageW# ** 2 + #ImageH# ** 2)) * 2)
ImageSW = (#ImageW# * #Scaler#)
ImageSH = (#ImageH# * #Scaler#)
; The radar dial image size is 61x55 pixels BUT
; We must multiply the 'Maxdiameter' by 2 because the dial image is actually 1/2 the 'scanned' area
; To use with a different image, change the ImageW & ImageH 'numbers' to match your image size

[mScalerUp]
Measure = Calc
Formula = (#Scaler# + 0.01 > 2.0)? 2.0 : (#Scaler# + 0.01)
DynamicVariables = 1
UpdateDivider = -1
; Measures the next largest size; limits it to 2x

[mScalerDn]
Measure = Calc
Formula = (#Scaler# - 0.01 < 0.1)? 0.1 : (#Scaler# - 0.01)
DynamicVariables = 1
UpdateDivider = -1
; Measures the next smallest size; limits it to .1x

[MeasureRotate]
Measure = Calc
Formula = (MeasureRotate % 360) + 1
MaxValue = 360
; Controls rotation

[MeterBG]
Meter = Image
SolidColor = 0,0,0,128
W = (#MaxDiameter# * #Scaler#)
H = (#MaxDiameter# * #Scaler#)
MouseScrollUpAction = [!WriteKeyValue Variables Scaler ([mScalerUp])][!Refresh #CurrentConfig#]
MouseScrollDownAction = [!WriteKeyValue Variables Scaler ([mScalerDn])][!Refresh #CurrentConfig#]

[RadarFade]
Meter = Rotator
MeasureName = MeasureRotate
ImageName = "#@#Images\RadarFade125"
X = ([MeterBG:W] / 2)
Y = ([MeterBG:H] / 2)
OffsetX = 0
OffsetY = 0
TransformationMatrix = (1 * #Scaler#); 0; 0; (1 * #Scaler#); ((1 - #Scaler#) *  [#CURRENTSECTION#: X]); ((1 - #Scaler#) * [#CURRENTSECTION#: Y])

[MeterScaleText]
Meter = String
X = ([MeterBG:W] / 2)
Y = ([MeterBG:W] / 2)
FontColor = 255,255,255
StringAlign = CenterCenter
StringEffect = Shadow
Text = Scale: #Scaler#


If you do not download the demo skin above, you will need to create 3 images: Rectangle.png (128x64 pixels),  colorwheel.png (128x128 pixels), & a radarfade.png(61x55 pixels) and save them in the "@ Resources\Images" folder, in the same folder where you saved the above codes, to see the action.
Add a Comment:
 
No comments have been added yet.

Rainmeter Workshop - 1

Journal Entry: Tue Feb 9, 2010, 2:10 PM
RAINMETER WORKSHOP


When I first started using Rainmeter I scoured Deviant Art for skins that fit what I was looking for. I didn’t find any. So I ended up dissecting skins, reading the help file, and discovered that it’s not nearly as complicated as it looks.

The goal of this series is to show how easy it is to make your own skins. I’ll be showing simple skins and code and talking about everything from basic skin creation to some of the more complicated things you can do with Rainmeter.

MAKING A NOTES SKIN




The skin I’ll be demonstrating is probably the simplest notes skin you can create. The skin is fundamentally made by combining different blocks of code. Each block as its own unique name. Each block of code is either a Measure or a Meter. Measures can be thought as a piece of code that stores information, and a Meter is one that displays something. The skin also shows the use of several different features, all of which I’m going to explain. These features are MeterStyle and position formulas. All skins are made up of two different types of blocks, meters and measures.

To make this code work, open up Notepad or similar program (I use Notepad++) and save the code in a file named Notes.ini in the Rainmeter Skins folder in your Documents. In the same folder you saved
Notes.ini
in, save a file named
notes.txt
. Write something in the text file. “Hello World” would be good. It helps to have file extensions turned on.

[Rainmeter]-In this section you can define the Update rate, Author, and few other things that I never use.

[Rainmeter]
Author=Varelse
Update=1000


[Variables]
This section contains all the variables I’ll be using in the skin. WIDTH and HEIGHT are the dimension of the background image. NOTESPATH tells Rainmeter what text file to read the notes out of. The next several define the size, font, and color of the text displayed. TITLE is the title of the skin, obviously.

[Variables]
WIDTH=180
HEIGHT=200
NOTESPATH=#CURRENTPATH#notes.txt
FONT=Trebuchet MS
TEXTCOLOR=255,255,255
FONTSIZE=10
FONTSIZE2=8
TITLE=NOTES


[MeasureNOTES]
This is the piece of code that tells Rainmeter to display the notes. As you can see, it references NOTESPATH. To reference a variable you have to enclose the variable name with #VariableName#. FileFilter tells it to look for a text file.

[MeasureNOTES]
Measure= Plugin
Plugin= PluginsQuotePlugin.dll
PathName=#NOTESPATH#
Subfolders=1
FileFilter=*.txt
Separator=¶


[BackGround]
-The box that displays behind the text. SolidColor defines the color, in this case black, and transparency of the color. H and W are the height and width.

[BackGround]
Meter=Image
X=0
Y=0
W=#WIDTH#
H=#HEIGHT#
SolidColor=0,0,0,100


[TextStyle]
-This is style section. It isn’t a meter or a measure. It’s just a section of code for other meters to reference. This style defines several parameters that are common to the Title and the Notes. This makes coding easier by being able to reference a block of code instead of having identical lines of code in different meters.

[TextStyle]
FontColor=#TEXTCOLOR#
StringStyle=Bold
AntiAlias=1
FontFace=#FONT#
FontSize=#FONTSIZE#
StringAlign=CENTER


[Title]
-Displays the title. Simple enough. It references [TextStyle], so [Title] has all the attributes that it does. Every meter needs an X and Y position. What’s interesting about this is how the X dimension is set. X= (#WIDTH#/2) means that the Title will always be centered relative to the width of the skin. Of course, the position could have been any other number or formula. If you are using a position formula, it has to be in parenthesis, otherwise it won’t work. And formulas can be very complex if you want them to be.

[Title]
Meter=String
MeterStyle=TextStyle
X= (#WIDTH#/2)
Y=10
Text=#TITLE#
LeftMouseDownAction=!Execute ["#NOTESPATH#"]


[NoteText]
-Display what [MeasureNotes] reads from the text file. Since we want the text to be within the background we need to set the Height and Width dimensions of the meter. Y=5R which means it is five pixels below the meter above it. I went with a 5 pixel border, so the Width becomes (#WIDTH#-10) and X=5. 5 pixels on the left and 5 on the right, so subtract 10 from the width of the background meter. Height wasn’t so easy. I basically had to guess and fiddle with it till I got what I wanted.

[NotesText]
Meter=String
MeasureName=MeasureNOTES
MeterStyle=TextStyle
X=5
Y=5R
H= (#HEIGHT#-40)
W= (#WIDTH#-10)
StringAlign=LEFT
FontSize=#FONTSIZE2#
ClipString=1



Questions? Comments? Ideas for new tutorials? Leave a comment.


For further reading and clarification on the options below each section, measure, and meter read the Manual. For more on skin creation read Rainmeter 101.

:iconvarelse42:
Varelse42 reports
CSS originally by alder-sketch
modified by bendenfield
visit the Rainmeter homepage
Add a Comment:
 
No comments have been added yet.