Thursday, February 11, 2010




























These two graphs both display cpu usage over time. The blue graph came first and then I modified the Lua script to generate the dot chart. I had been thinking of tackling an animated bar chart for a while and decided that I should just give it a go. But it wasn't easy!

Just to conceptualize how it was going to work in the beginning took a while...
I had to think of a way to capture cpu usage information at one particular time, store that information and display it somehow while capturing new information every cycle and linking that all together to get the illusion of a moving chart.

I had (with the help of the crunchbanglinux forum) found a way to implement a timer in lua:

local updates=conky_parse('${updates}')
update_num=tonumber(updates)
local timer=(update_num % 4)+1

Parse conky to get the updates number then using the line above, every time the updates number is divisible cleanly by 4 (ie no remainder) then timer =1 (without the +1 it would equal 0)
The result is that the timer counts 1,2,3,4,1,2,3,4,1,2,3,4 etc etc. This was the basis for the animation.

I started with trying a 4 bar graph and after a little figuring out I got to the point where i could get the bars set and moving across, but when the timer reset so did the bars. The problem was that I could pass information down the script by referencing strings:
num1=number
num2=num1
num3=num2
num4=num3
But to get the table working i needed this:
A->B->C->D->a->b->c->d
B->C->D->a->b->c->d->A
C->D->a->b->c->d->A->B
D->a->b->c->d->A->B->C

So that I get a full cycle, so that the next step I can reset back top the beginning. But going from dABC which existed at the bottom of the script back to the top for ABCD, I had to take the information for ABC (from dABC) and move it up the script so that ABC in ABCD is the same ABC from dABC. That makes sense right?

So I needed a function. Functions are how you move information from the bottom of the script to the top!

Anyway. Look here at the code on the crunchbang forum and you will see what I ended up with. The script is pretty long. The reason for this is that, as you can see from the above example, for a 4 step animation you actually need 8 steps, each step containing 4 bits of information. So for 4 steps you need 32 bits of information.

I ended up with a 10 step graph which needed 200 bits of information. If i wanted a 20 step graph then that is 20x2x20=800 bits of info.

If anyone has an easier way then I would be only too glad to hear it!

The other drawback with the graph drawing function, as I have written it, is that you can only feed it one set of information. Unlike other functions where you can feed it say cpu info and settings and also feed it memperc information and settings and get 2 outputs. This function you cant do that...something just doesn't work. It would be easy enough to copy the function, and change its name, then call the newly named function with different information in the conky display function. However, it is also the case that there arent many other conky outputs that are suited to a graph.

Downspeed and upspeed graphs can be generated by conky, and could be generated from my graph drawing script... but extra steps would need to be taken to adjust for the changing units... B to KiB to MiB.

Maybe I'll find a more compact way of doing this... although running this script, total "resting" cpu usage on my system is around the 2's or 3's so it is hardly intensive even for it's size.

Wednesday, February 10, 2010

Hot on the heels of my custom bars lua comes another script for drawing bars and rings. And after figuring out most of what goes into making a settings table work with cairo in a lua script... I decided to go with another approach.

Instead of setting up a settings table at the beginning of the script, then parsing the table with one function and feeding the parsed information into another function which converts the table values into local strings... I went with defining the values when calling the draw function. It maybe isn't as user friendly as the settings table way but in less than half the lines of my custom bars lua I have created a script that can generate the same bars and also generate ring meters.

Also I think the way that I have set up the code will make it easier to inset these meters into another Lua script. All it requires is that the meter drawing functions are placed above the function to be called in conky. Then you can use the functions with just a couple of setup lines.

Of course the ring meters are taken from londonali1010's ring meter script here. Many thanks to londonali1010 for all her excellent and inspirational work!




I have also enabled the addition of boundary rings for the ring meters. The outer and inner boundary rings can be set values for width, color and alpha individually.

The code in posted in the crunchbanglinux forum conky config thread here.
I have just updated the code to allow much easier use of rotation with the bars. Bars rotate in place and not relative to 0,0 in the new code.
Here is the annotated Lua script for my custom bars.



For simplicity I have put in the settings for only 1 bar.

require "cairo"

So first we have the table below.
The table has a format like this:
table={{A1=1,B1=2,C1=3,D1=4},{A2=1,B2=2,C2=3,D2=4},{A3=1,B3=2,C3=3,D3=4}}
but for ease of use and so that the settings can be edited easily the table is split up line by line as follows:
BUT you still have to have all of the brackets (I'm not sure that they have to be curly brackets, but they certainly work.) and all of the commas or you will get an error.
table = {
{
I have used comments throughout the table so that each setting is easily identified. The comments do not interrupt the format of the table.
--conky object for output
co='cpu',
--rectangle width
rw=150,
--rectangle height
rh=50,
--NOTE about width and height. For a bordered rectangle with line width lw (specified below)
--the border is drawn 1/2 lw inside and 1/2 lw outside the boundary of the rectangle, so that
--final width=rw+lw and final height=rh+lw. I did not automate this below as it is possible
--to specify a line width (lw) greater than either width (rw) or height (rh).
--eg if you want a rectangle 200 pixels long and a border line width of 8, then rw=100-8=92
Above is a note about how to set up the heights and widths. If something isn't obvious to anyone who might use the script it's nice to explain things,
--background rectangle color set
bg_red=1,
bg_green=0,
bg_blue=0,
--background rectangle alpha
rab=0.5,
--indicator rectangle color set
in_red=0,
in_green=1,
in_blue=0,
--indicator rectangle alpha
rai=1,
--border line width
lw=40,
--border line color set
l_red=1,
l_green=0,
l_blue=1,
--border line alpha
la=0.2,
--position, x and y
rx=200,
ry=150,
--set rotation in degrees (will require x and y to be reconfigured)
rot=0
},
}
So we have all of the variables set. Each variable will be passed into the various functions and applied to the bars when they are drawn.

There are 2 functions and one sub function in this script. From looking at my last annotated script a function is written and then called below, and then when it is called information is passed up to the function. Here, to see how the information is passed I'll look at the second function first. This is the function that we will be calling in conky:
function conky_draw_shape()
this is the name of the function
local updates=conky_parse('${updates}')
update_num=tonumber(updates)
if update_num > 5 then
if conky_window==nil then return end
local w=conky_window.width
local h=conky_window.height
local cs=cairo_xlib_surface_create(conky_window.display, conky_window.drawable, conky_window.visual, w, h)
cr=cairo_create(cs)
The above should be familiar. It sets the 5 cycle delay and the surface onto which cairo will draw. Next comes a local function. This function called "parse" which takes the table entry "co" (the conky object whose output will be displayed by the bar) and performs the conky_parse command on it.
local function parse(cr,pt)
Here is the local function name and the strings that will be set when the function is used below.
local str=''
local value=0
str=string.format('${%s}',pt['co'])
The pt part is the important part (note that there is nothing important about the term pt, it is just the name of a string and could be anything. Perhaps pt stands for parse table). pt is one of the strings that is set by the parse function from information fed to the function when it is called. Also you should remember 'co' as one of the variables we set in the settings table.
str=conky_parse(str)
value=tonumber(str)
The function is parsing 'co' the conky object, running that through the tonumber command and then storing the information in a string named "value"
if value == nil then value = 0 end
draw_table(cr,pt,value)
The line above is calling the first function in the script, and feeding the function 3 things: cr, pt and value. As we saw the "value" string is generated above and it is that string that will be passed to the draw_table function.
end
Here is the end of the local function. Below is the remaining commands of the "global" function called "draw_shape"
for i in pairs(table) do

parse(cr, table[i])
These 2 lines is where the "magic" happens :) These lines read the contents of the table, and pass each set of variables to the parse function. In this case the letter "i" is extremely important and cannot be substituted. Perhaps "i" stands for "instance" or "index", I don't know exactly. So basically what is being done is that for every set of data ie {A1=1,B1=2,C1=3,D1=4} in the settings table the "parse" function is being called and fed the cr string as well as every string that the set contains ( A1, B1, C1, D1).

As you can see the parse function is being sent 2 things cr and table[i]. Then as we saw the parse function (local function parse(cr,pt)) takes cr and stores it as cr and takes the output of table[i] and stores it as pt. Then in the parse function we see pt['co'] which is the same as "table['co']". So the table is being searched for the string co, and the value of co is being formatted (str=string.format('${%s}',pt['co'])) and stored in the string called "str".

So for the data set "table= {A1=1,B1=2,C1=3,D1=4}"
table['A1']=1
table['B1']=2
table['C1']=3
table['D1']=4

and as in the parse function table[i] = pt[i] so that

table['A1']=1=pt['A1']
table['B1']=2=pt['B1']
table['C1']=3=pt['C1']
table['D1']=4=pt['D1']

I hope that all makes sense.
So to recap...
  • function draw_shape is generating the strings: cr and table[i]
  • function parse is being fed cr (but not doing anything with it) and table[i] and renaming table[i] as the string pt.
  • parse is looking through the string "pt" for the table setting 'co' then parsing and formatting the output and storing it as the string "value".
parse is then feeding the strings "cr", "pt" and the new string "value" up to the function settings_table.
end
end
end

Now we are going back to the point after the settings table (confused yet? :) )
Here we have the first function in the Lua script and we have already seen where the strings cr, pt and value are being fed from.
function draw_table(cr, pt, value)
local width=pt['rw']
local height=pt['rh']
local bgalpha=pt['rab']
local indalpha=pt['rai']
local across=pt['rx']
local down=pt['ry']
local lwide=pt['lw']
local lalpha=pt['la']
local bgcolr=pt['bg_red']
local bgcolg=pt['bg_green']
local bgcolb=pt['bg_blue']
local incolr=pt['in_red']
local incolg=pt['in_green']
local incolb=pt['in_blue']
local lcolr=pt['l_red']
local lcolg=pt['l_green']
local lcolb=pt['l_blue']
local rotate=pt['rot']
Above are a number of lines that are searching the table information (stored in the string pt) for the various bits and pieces of information. Then when the specific bit of information is found, that information is being set as a local string. For example:
local width=pt['rw']
string pt is being searched for the table setting "rw" (rectangle width) and storing the value of this setting in the local string "width".
--indicator calculation
local inum=(((width-lwide)/100)*value)
The above calculation allows the bars to be resized while maintainig the correct proportions of the indicator line.
--set initial rotation
cairo_rotate (cr, rotate*math.pi/180)
Here we are applying the local string rotate (initially set in the settings table as "rot") and performing the rotation, if any.
--background bar
cairo_rectangle (cr, (across+(lwide/2)), (down+(lwide/2)), (width-lwide), (height-lwide))
cairo_set_source_rgba (cr, bgcolr, bgcolg, bgcolb, bgalpha);
cairo_fill (cr)
Above we are drawing the background bar using the local strings we set above. The calculations in the code are to account for the way that the outline of rhe rectangle is drawn. This was explained towards the top of the script.
--indicator bar
cairo_rectangle (cr, (across+(lwide/2)), (down+(lwide/2)), inum, (height-lwide))
cairo_set_source_rgba (cr, incolr, incolg, incolb, indalpha);
cairo_fill (cr)
Above we generate the indicator bar.
--border line
cairo_set_line_width (cr, lwide);
cairo_rectangle (cr, across, down, width, height)
cairo_set_source_rgba (cr, lcolr, lcolg, lcolb, lalpha);
cairo_stroke (cr)
And above we draw the outline, if any.
--resets rotation
cairo_rotate (cr, (rotate*-1)*math.pi/180)
Then we reset rotation.
end
Below you can look through the functions that have been described above, but in the correct order.
function conky_draw_shape()
local updates=conky_parse('${updates}')
update_num=tonumber(updates)
if update_num > 5 then
if conky_window==nil then return end
local w=conky_window.width
local h=conky_window.height
local cs=cairo_xlib_surface_create(conky_window.display, conky_window.drawable, conky_window.visual, w, h)
cr=cairo_create(cs)
local function parse(cr,pt)
local str=''
local value=0
str=string.format('${%s}',pt['co'])
str=conky_parse(str)
value=tonumber(str)
if value == nil then value = 0 end
draw_table(cr,pt,value)
end
for i in pairs(table) do
parse(cr, table[i])
end
end
end
That's the end.
Here is an approach that achieves the same result as a settings table, in that you can achieve multiple outputs from a single instance of a function. But I think that this next approach is easier to implement and considerable shorter code wise. And here is my first take at explaining the use of arrays in a Lua script.

Tuesday, February 9, 2010

I thought it was time that I tried to get to grips with calling multiple instances of a function via a settings table. I tried it out with something simple at first, and paying close attention to the ring meters script by londonali1010 as a reference here is the end result:



A script that generates indicator bars. While not groundbreaking I thought it might add to the tools of the conky maker :)

They are fully configurable and you can change:
  • which output to display
  • background color and alpha
  • indicator color and alpha
  • border color and alpha
  • size
  • position
  • rotation
via a settings table

Go here for the code. I shall probably be making some modifications in the future.
I shall also post an annotated copy of the lua script and do my best to explain how the whole thing works
Tips for writing Lua scripts and getting them working in conky

Some of these will be very basic.

1. Always launch your conkyrc through the terminal. You will get feedback about errors here that are invaluable for fixing them. More on this later

2. Setup a folder where you are going to keep all of your conky configs and create a conky_start shell script
mine contains the following:

#!/bin/bash
conky -c ~/.conky/.conkyrc_tabletest

You need to make this script executable. I do this through my file manager as I'm not familiar with the terminal commands to do this.

This script will launch the conky I have saved as conkyrc_tabletest in the folder ".conky" in my home directory. Obviously you muct have created a conky before you can try and launch it!

Then launch the conky in the terminal as follows:

$ /home/mcdowall/.conky/conky_start

I have my regular conky config launch on startup, but I always launch a config I'm working on by editing this conky_start file. Just make sure that if you have a conky (or more than 1) running on your system that the conky you are about to launch will not interfere. My everyday conky is set to top_right... so I set a conky I'm working on to open elsewhere.

3. You are going to have to stop and restart the conkyrc *alot*

a command like:

$ killall conky

should do the trick. If your conky quits on you and just disappears always perform the above command. Don't just start it up again or you will get multiple instances on conky running and things will start to slow down.

4. Regarding Lua scripts in general, use the print function to troubleshoot when something isn't working right.

for example I have the following code:

cpunum=conky_parse ('${cpu}')
cpu=tonumber(cpunumber)

but for some reason things aren't working as expected. So put in the print command as so:

cpunum=conky_parse ('${cpu}')
cpu=tonumber(cpunumber)
print (cpu)

If the above code is correct then you will get the output of cpu printed only in the terminal window. Of course if there is something wrong above this point that has broken the script, you won't get anything printed out.

The above code is probably too simple to go wrong, but when you are trying more complicated things, like string editing with gsub or the string:split function or trying to work out some tricky mathematics, the print command is invaluable.

5. Put enough comments into the script so you know whats going on.
When you go back to a script and want to edit something, it's much easier if you have divided your script and functions into descriptive sections with comments. -- before text makes the text a comment.

6. Try and use descriptive names to your strings.
As above, when you come back to look at a script, a string names cpu_num gives you a good clue about what the string is about... while something more arbitrary like "a18" doesn't.

Terminal Output

Sometimes the terminal output is very helpful, sometimes it's not. I would say the most common thing I see in the terminal is this:

Conky: llua_do_call: function conky_draw_shape execution failed: attempt to call a nil value

This happens particularly because I don't have enough end's in my scripts. I should get into the habit of writing my scripts out properly like this:

function
if x==y then
...................return z
..............else
...................return a
.............end
end
(ignore the ... thats just so the format is preserved!)
But I don't.

Another thing about the above error is that if you go to your Lua script and edit the script so that you think you have corrected the error, many times when you save the Lua script the terminal just keeps on repeating the error making you think that it isn't fixed. When you get the
attempt to call a nil value error it is a good idea to issue the killall conky command. Then restart the conkyrc.

But before you restart, scroll back through the error messages
and look at the lines just after the conkyrc was launched... this is where the terminal will tell you why it's giving the errors. For example:

mcdowall@mcdowall-desktop:~$ /home/mcdowall/.conky/conky_start
Conky: llua_load: /home/mcdowall/lua/table.lua:176: 'end' expected (to close 'function' at line 151) near ''
Conky: forked to background, pid is 16955
mcdowall@mcdowall-desktop:~$
Conky: desktop window (87) is root window
Conky: window type - desktop
Conky: drawing to created window (0x2c00001)
Conky: drawing to double buffer
Conky: llua_do_call: function conky_draw_shape execution failed: attempt to call a nil value

and we see:
Conky: llua_load: /home/mcdowall/lua/table.lua:176: 'end' expected (to close 'function' at line 151) near '

eof = end of function which means that indeed there is a missing end ( 'end' expected).

Go to the line in question (here it's telling us that the function which begins on line 151 should have an end on line 176) and fix the problem and try again.

mcdowall@mcdowall-desktop:~$ /home/mcdowall/.conky/conky_start
Conky: llua_load: /home/mcdowall/lua/table.lua:92: '}' expected (to close '{' at line 76) near 'in_blue'

this error is because of a missing comma on line 76.

So the terminal is invaluable!

Sometimes the helpful part of the error gets repeated in the terminal, and usually when this happens, fixing the problem in the Lua script and saving the script are enough to get past it.
I was reading through the crunchbanglinux forum and there was a thread here that began as a question about vector graphics formats but went onto become a discussion of guitar amp schematics. I looked at the linked schematics and I immediately saw the potential to use the scematic, or at least part of it, for the basis of a conky setup!

And here it is...


I recreated the schematic with cairo and then placed the various graphs bars and text outputs around it. I used the conkyrc to generate the upspeed and downspeed graphs and the memory usage bar. Everything else was made in lua. I took alot of the code from my full screen conky setup here. In the screen.lua I had already worked on formatting many of the conky object outputs, and I needed the same approach here so that I could be as faithful to the original schematic as possible.

The lua script is rather long so it's probably best that I link to my crunchbang forum post that contains the code here.

Sunday, February 7, 2010


Here is an annotated copy of my screen.lua

First off, this Lua uses more than 1 function. I can still only call a single function in conky, but in that function (called conky_draw_it below) I can use other functions.

This first 2 functions are simple enough, but show an important principal of using functions. What these functions do is to add zeros to numbers. So that percentages are always 3 digits so that when cpu usage is 5 % then 005 is displayed. Similarly time in hours, for example, is always 2 digits so that 2am is displayed as 02. This is important otherwise the changing number of digits will move everything around or overlap.
function addzero100(num)
The (num) part is important. This is a string that will be set later when the function is used. Here is an example of how this function will be called "cairo_show_text (cr, (addzero100(swap)));" As you can see the place of (num) has been taken by (swap) when the function is used. The information stored in the string "swap" is given to the addzero100 function and is stored as "num". Then the function operates on the information stored in "num" and returns the result in the place of (addzero100(swap)). So if the information is "swap" was 8 (ie 8% of swap was in used) then 008 will be displayed.
if tonumber(num)
return "00" .. num
So if a number can have 3 digits, but has only a 1 digit value, 2 zeros are added.
elseif tonumber(num)
return "0" .. num
Otherwise if it has a 2 digit value 1 zero is added
else
return num
Otherwise if it has a 3 digit value nothing is added. The function below shold be self explanatory. :)
end
end
function addzero10(num)
if tonumber(num)
return "0" .. num
else
return num
end
end

Here is another function. I found this here. It performs a very useful function in that it takes a string and splits it up into bits and puts the bits into a table. I'm a little hazy about how it works, but it works.
function string:split(delimiter)
local result = { }
local from = 1
local delim_from, delim_to = string.find( self, delimiter, from )
while delim_from do
table.insert( result, string.sub( self, from , delim_from-1 ) )
from = delim_to + 1
delim_from, delim_to = string.find( self, delimiter, from )
end
table.insert( result, string.sub( self, from ) )
return result
end
Ok, here we get to the function that contains all the stuff that I want displayed. This function will be using the functions above. Note that you cant use a function until it has been written... meaning that the function you are going to use must be above the function in which you want to use it.
function conky_draw_it()
local updates=conky_parse('${updates}')
update_num=tonumber(updates)
if update_num > 5 then
if conky_window==nil then return end
local w=conky_window.width
local h=conky_window.height
local cs=cairo_xlib_surface_create(conky_window.display, conky_window.drawable, conky_window.visual, w, h)
cr=cairo_create(cs)
The above sets the delay and the "canvas".
--sets font, font size and color
cairo_select_font_face (cr, "White Rabbit", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, 200);
cairo_set_source_rgba (cr, 1, 1, 1, 1);
In this case I initially wanted to use the same font, font size and color for all the text so I set that all up above.
--this section generates numbers only uptime ddhhmm
This section takes the output of the conky object ${uptime} which would normally be, for example, "1d 3h 25m 12s" and turns it into "010325" (no seconds)
local uptime=conky_parse("${uptime}")
utdcalc=string.gsub(uptime, "[%d ]", "")
string.gsub was anew Lua command for me. The above takes the string "uptime" finds all numerical digits and spaces and replaces them with nothing... ie it deletes them. This is what is being looked for: "[%d ]". %d means any digit, and when placed in [] it finds digits and spaces. It would be different if the search was for "%d ". Without the [] it is now searching for a digit followed by a space only. The end result is 1d 3h 25m 12s -> dhms. This will be used in an if command below.
utime=string.split(uptime, "%a ")
Above I'm using the string split function as defined earlier in the script. As you can see, I am feeding the split string function 2 things... a string (uptime) and a search object "%a ". %a matches any letter. I am searching for a letter followed by a space. So it takes "1d 3h 25m 12s" and turns it into:
1
3
25
12
so that:
utime[1] = 1 (days)
utime[2] = 3 (hours)
utime[3] = 25 (minutes)
utime[4]= 12 (seconds)
if utdcalc=="hms" then
If uptime is less than 24 hours there will be no days in the output and therefore the following will be set as strings...
utd="00"
uth=addzero10(utime[1])
utm=addzero10(utime[2])
Also here we are using the addzero10 function, so that 3 (hours) -> 03
else
If there is a day component to uptime then "utdcalc" will not be "hms" (it will be dhms), so instead the following strings will be set.
utd=addzero10(utime[1])
uth=addzero10(utime[2])
utm=addzero10(utime[3])
end
Kernel, Down speed and Up speed uses a similar process as used for uptime; a combination of string.gsub and string.split.
--this section prints number only kernel information
local kernel1=conky_parse("${kernel}")
kernel=string.gsub(kernel1, "[%a .-]", "")
--this section formats downspeed
local downspeed=conky_parse("${downspeed}")
downnum=string.gsub(downspeed, "[%a ]", "")
dwnspcalc=string.split(downnum, "%p")
dwnumunit1=string.gsub(downspeed, "[%p%d]", "")
dwnumunit=string.gsub(dwnumunit1, "iB", "")
if dwnumunit=="M" then
dspmb=string.gsub(downnum, ".$", "")
dsnum=(math.random(5,20))/100
Here is a feature of the conky that I implimented later on. math.random(5,20) generates a random number between 5 and 20 every cycle. I used this number divided by 100 in the line below to adjust the aplha of the text cairo displays.
cairo_set_source_rgba (cr, 1, 1, 1, dsnum);
.................................................^ here is the random number string dsnum
cairo_move_to (cr, 784, 800);
cairo_show_text (cr, (dspmb) .. (dwnumunit));
else
dsnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, dsnum);
cairo_move_to (cr, 784, 800);
cairo_show_text (cr, (addzero100(dwnspcalc[1]) .. dwnumunit));
end
--this section formats upspeed
local upspeed=conky_parse("${upspeed}")
upnum=string.gsub(upspeed, "[%a ]", "")
upspcalc=string.split(upnum, "%p")
upumunit1=string.gsub(upspeed, "[%p%d]", "")
upumunit=string.gsub(upumunit1, "iB", "")
Below is the section that formats the name of the top process, as measured by cpu usage so that only the first 4 characters of the name are displayed, as I only had space for 4 characters in the conky. This one gave me some trouble as when you use the string:split function, the search criteria used to delineate the splits is deleted when the string is split. But I discovered a string.gsub trick below that solved the problem.
--this section formats top process
local topproc1=conky_parse("${top name 1}")
topproc=string.gsub(topproc1, "[%p]", "")
First I removed all the punctuation characters. %p matches all punctuation.
top1=string.upper(topproc)
Then I put all the text into uppercase
top2=string.gsub(top1, "%a", "%1-")
Abpve is the gsub trick... it matches all letters (%a) and puts a hyphen (-) between them. So that CONKY becomes C-O-N-K-Y-
top=string.split(top2, "%p")
I was then able to split the string based on punctuation and turn the resulted split into a table.

Time outputs below use Lua os.date rather than conky_parse('${time}')
--this section formats time outputs
--hours
hrs=os.date("%H")
--minutes
min=os.date("%M")
--seconds
sec=os.date("%S")
--year
yr=os.date("%y")
--month
mnt=os.date("%m")
--day
day=os.date("%d")

Below are all the common conky outputs to parse
--this sectiion formats common conky outputs
--cpu
local cpunum=conky_parse("${cpu}")
cpu=tonumber(cpunum)
--mem
local memnum=conky_parse("${memperc}")
mem=tonumber(memnum)
--hdd
local hddnum=conky_parse("${fs_used_perc /}")
hdd=tonumber(hddnum)
--swap
local swapnum=conky_parse("${swapperc}")
swap=tonumber(swapnum)

Below is the section that displays all of the above pieces through cairo.
--prints text
--uptime
utmnum=(math.random(5,20))/100
Above is the random alpha generator in use again.
cairo_set_source_rgba (cr, 1, 1, 1, utmnum);
cairo_move_to (cr, 261, 480);
cairo_show_text (cr, (utd .. uth .. utm))
--kernel
knum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, knum);
cairo_move_to (cr, 0, 800);
cairo_show_text (cr, (kernel))
--upspeed
upsdnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, upsdnum);
cairo_move_to (cr, 261, 320);
cairo_show_text (cr, (addzero100(upspcalc[1]) .. upumunit));
--top process
tpnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, tpnum);
cairo_move_to (cr, 0, 640);
cairo_show_text (cr, (top[1]) .. (top[2]) .. (top[3]) .. (top[4]));
Above is how to print the first 4 characters from the name of the top process. In cairo a space followed by 2 periods and then another space, such as above, basically means print next to. So that if the top process is conky, top[1]=C, top[2]=O, top[3]=N and top[4]=K and cairo is told to display this as "CONK"
--time hours
thnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, thnum);
cairo_move_to (cr, 0, 160);
cairo_show_text (cr, (hrs));
--swapperc
swnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, swnum);
cairo_move_to (cr, 261, 160);
cairo_show_text (cr, (addzero100(swap)));
--month
monnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, monnum);
cairo_move_to (cr, 653, 160);
cairo_show_text (cr, (mnt));
--cpu
cnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, cnum);
cairo_move_to (cr, 915, 160);
cairo_show_text (cr, (addzero100(cpu)));
--time minutes
cairo_set_source_rgba (cr, 1, 1, 1, thnum);
cairo_move_to (cr, 0, 320);
cairo_show_text (cr, (min));
--day
cairo_set_source_rgba (cr, 1, 1, 1, monnum);
cairo_move_to (cr, 784, 320);
cairo_show_text (cr, (day));
--year 20
yrnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, yrnum);
cairo_move_to (cr, 1043, 320);
cairo_show_text (cr, "20");
--time seconds
cairo_set_source_rgba (cr, 1, 1, 1, thnum);
cairo_move_to (cr, 0, 480);
cairo_show_text (cr, (sec));
--year 10
cairo_set_source_rgba (cr, 1, 1, 1, yrnum);
cairo_move_to (cr, 1043, 480);
cairo_show_text (cr, (yr));
--hdd used
hdnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, hdnum);
cairo_move_to (cr, 522, 640);
cairo_show_text (cr, (addzero100(hdd)));
--memory
mnum=(math.random(5,20))/100
cairo_set_source_rgba (cr, 1, 1, 1, mnum);
cairo_move_to (cr, 915, 640);
cairo_show_text (cr, (addzero100(mem)));
ebd
end

And that is that Lua!
For more information you can go here and look at another annotated script that deals with the use of a settings table and uses multiple functions.