Skip to content

Using Lua scripts (Part 08): Functions

lasers edited this page Jan 4, 2019 · 2 revisions

viii: Turn our code into a function

take the example of the bar indicator with all the features we added and the entire lua script might look something like this

--[[this lua script draws vertical bar indicators]]

require 'cairo'

function conky_main()
  if conky_window == nil then return end
  local cs = cairo_xlib_surface_create(conky_window.display, conky_window.drawable, conky_window.visual, conky_window.width, conky_window.height)
  cr = cairo_create(cs)
  local updates=tonumber(conky_parse('${updates}'))
  if updates>5 then
    --#########################################################################################################
    --SETTINGS FOR INDICATOR BAR
    bar_bottom_left_x= 100
    bar_bottom_left_y= 100
    bar_width= 30
    bar_height= 100
    bar_value=tonumber(conky_parse("${cpu}"))
    bar_max_value=100
    --set bar background colors, 0.5,0.5,0.5,1 = fully opaque grey
    bar_bg_red=0.5
    bar_bg_green=0.5
    bar_bg_blue=0.5
    bar_bg_alpha=1
    --bar border settings
    bar_border=1 --set 1 for border or 0 for no border
    --set border color rgba
    border_red=0
    border_green=1
    border_blue=1
    border_alpha=1
    --set border thickness
    border_width=10
    --color change
    --set value for first color change, low cpu usage to mid cpu usage
    mid_value=50
    --set "low" cpu usage color and alpha, ie bar color below 50% - 0,1,0,1=fully opaque green
    lr,lg,lb,la=0,1,0,1
    --set "mid" cpu usage color, between 50 and 79 - 1,1,0,1=fully opaque yellow
    mr,mg,mb,ma=1,1,0,1
    --set alarm value, this is the value at which bar color will change
    alarm_value=80
    --set alarm bar color, 1,0,0,1 = red fully opaque
    ar,ag,ab,aa=1,0,0,1
    --end of settings
    --draw bar
    --draw background
    cairo_set_source_rgba (cr,bar_bg_red,bar_bg_green,bar_bg_blue,bar_bg_alpha)
    cairo_rectangle (cr,bar_bottom_left_x,bar_bottom_left_y,bar_width,-bar_height)
    cairo_fill (cr)
    --draw indicator
    if bar_value>=alarm_value then --ie if value is greater or equal to 50
      cairo_set_source_rgba (cr,ar,ag,ab,aa)--yellow
    elseif bar_value>=mid_value then --ie if bar_value is greater or equal to 80
      cairo_set_source_rgba (cr,mr,mg,mb,ma)--red
    else
      cairo_set_source_rgba (cr,lr,lg,lb,la)--green
    end
    scale=bar_height/bar_max_value
    indicator_height=scale*bar_value
    cairo_rectangle (cr,bar_bottom_left_x,bar_bottom_left_y,bar_width,-indicator_height)
    cairo_fill (cr)
    --draw border
    cairo_set_source_rgba (cr,border_red,border_green,border_blue,border_alpha)
    cairo_set_line_width (cr,border_width)
    border_bottom_left_x=bar_bottom_left_x-(border_width/2)
    border_bottom_left_y=bar_bottom_left_y+(border_width/2)
    brec_width=bar_width+border_width
    brec_height=bar_height+border_width--remember that we need to make this value negative at some point because we are drawing up
    cairo_rectangle (cr,border_bottom_left_x,border_bottom_left_y,brec_width,-brec_height)
    cairo_stroke (cr)
    --#########################################################################################################
    end-- if updates>5
  cairo_destroy(cr)
  cairo_surface_destroy(cs)
  cr=nil
end-- end main function

we can run the script from conky by adding these lines to the conkyrc

conky.config = {
  lua_load = '/path-to-file/filename.lua',
  lua_draw_hook_post  = 'main',
};

conky.text = [[ ]];

but as it currently stands we only get one indicator bar out of it, currently set to display cpu%

there are plenty of other conky objects that we might want use our indicator bar with.

one thing we could do is copy all the code required to draw the bar, all the settings and all the drawing code, and paste it over and over, duplicating the code within the main lua function for as many bars as you want.

then you would go to each repetition of code, edit the settings (a minimum of setting a different conky object and new coordinates)

there is nothing wrong with this approach, plenty of my script have code repetitions in them and our code isnt all that long (we would have about 60 code lines per bar)...

BUT if our code was longer, and even for code this length, if we wanted lots of bars we would end up with a pretty large main function with settings and drawing code all mixed together. This can make harder work of editing when you want to make a change.

FUNCTIONS are a simple way to execute the same piece of code over and over again without increasing your line count and so keeping your main function free from clutter which makes it easier to navigate and edit.

before we go about editing our lua script to turn the bar code into a function, i'll do my best to explain what functions are all about!

if you look at other pre made lua scripts out there you will see that there are almost always other functions in the script in addition to the main function that is called in the conkyrc.

Functions can be as simple or as complex as you need the function to be. for use in conky, other functions are written outside of the main function either above or below.

due to the way lua scripts work, writing other functions below the main function isnt a problem.

From a programming point of view it would be more "proper" to have your other functions above the main. BUT many times you want to enter settings into the lua script, and more often than not these settings will go into the main function which is one reason why you may not want to bury your main function below the other functions.

all your other functions do not need the conky setup lines that the main function needs, you will be calling these other function from within the main function, so image that whenever you call a function, the script is simple substituting the function call with the code it contains, so that in essence the code exists within the main function

so all you need for other functions is to

  • start the function
  • give it a name
  • set out what the function is going to do
  • end the function

here is an example of a simple function

function multiply(number)
  return number*2
end

this function accepts a number and returns the result of that number multiplied by 2

(for the sake of brevity i wont write out all the setup code for the main function just remember you need all the elements i described in part 1 of this how to for the main function if you want lua to display stuff in conky)

so how do we use the function i just wrote? lets say that we put it under the main function we would have a script like so

function conky_main()
  *setup lines*
  --#######################
  result=multiply(16)
  print (result)
  --#######################
  *close out lines
end-- main function

function multiply(number)
  return number*2
end

the round brackets are very important here this is how we send information to our other functions

i am using the function inside main like this result=multiply(16) So I am sending the multiply function the number 16 inside the curved brackets

The multiply function takes the number 16 and sets a string called "number" to that value function multiply(number) so hopefully you can see the relationship between what is written between curved brackets when sending information to functions

This string could be called anything i wanted (as long as it didn't start with a number or have spaces) there is nothing special about calling it "number" it could just as easily be "baboons" but as ive said elsewhere i think its a good idea for the string name to reflect the contents of that string :)

the function takes the value held in "number", multiplies it by 2 and returns the result in this case result=multiply(16) the result returned by the multiply function is assigned to a string called "result" print(result) --> 32

A function doesn't have to return a value, they can be used for as many different purposes as you can think of

SO lets work on getting our bar drawing code into its own function

-think of what we are going to call the function, lets call it indicator_bar

so we can start our new function, and we will go and start the function below our main function we might as well put our "end" in there too, and comment it so we dont forget what its for :)

function indicator_bar()
  --############################
  --insert function code
  --############################
end--of bar_indicator function

now we need to think about what code are going to put into our function?

this is one reason why i wrote my initial code so that there was a separate settings part where we assigned all the input values to strings and a separate drawing part where we manipulated our strings and plugged them into the commands that are responsible for getting the actual bar graphics onto the screen.

the point of making the function is that we want to be able to send our function different sets of settings and have the function output the graphics for each set

SO we will retain the setup portion for each bar within the main conky function and put the bar drawing code into the indicator_bar.

we can just go ahead and cut the code out of the main conky function and paste it into the indicator_bar function

once separated, the code would look something like this:

function conky_main()
  *setup lines*
  --#######################
  --SETTINGS FOR INDICATOR BAR
  bar_bottom_left_x= 100
  bar_bottom_left_y= 100
  bar_width= 30
  bar_height= 100
  bar_value=tonumber(conky_parse("${cpu}"))
  bar_max_value=100
  --set bar background colors, 0.5,0.5,0.5,1 = fully opaque grey
  bar_bg_red=0.5
  bar_bg_green=0.5
  bar_bg_blue=0.5
  bar_bg_alpha=1
  --bar border settings
  bar_border=1 --set 1 for border or 0 for no border
  --set border color rgba
  border_red=0
  border_green=1
  border_blue=1
  border_alpha=1
  --set border thickness
  border_width=10
  --color change
  --set value for first color change, low cpu usage to mid cpu usage
  mid_value=50
  --set "low" cpu usage color and alpha, ie bar color below 50% - 0,1,0,1=fully opaque green
  lr,lg,lb,la=0,1,0,1
  --set "mid" cpu usage color, between 50 and 79 - 1,1,0,1=fully opaque yellow
  mr,mg,mb,ma=1,1,0,1
  --set alarm value, this is the value at which bar color will change
  alarm_value=80
  --set alarm bar color, 1,0,0,1 = red fully opaque
  ar,ag,ab,aa=1,0,0,1
  --end of settings
  --#######################
  *close out lines
end-- main function


function indicator_bar()
  --############################
  --draw bar
  --draw background
  cairo_set_source_rgba(cr,bar_bg_red,bar_bg_green,bar_bg_blue,bar_bg_alpha)
  cairo_rectangle (cr,bar_bottom_left_x,bar_bottom_left_y,bar_width,-bar_height)
  cairo_fill (cr)
  --draw indicator
  if bar_value>=alarm_value then --ie if value is greater or equal to 50
    cairo_set_source_rgba (cr,ar,ag,ab,aa)--yellow
  elseif bar_value>=mid_value then --ie if bar_value is greater or equal to 80
    cairo_set_source_rgba (cr,mr,mg,mb,ma)--red
  else
    cairo_set_source_rgba (cr,lr,lg,lb,la)--green
  end
  scale=bar_height/bar_max_value
  indicator_height=scale*bar_value
  cairo_rectangle (cr,bar_bottom_left_x,bar_bottom_left_y,bar_width,-indicator_height)
  cairo_fill (cr)
  --draw border
  cairo_set_source_rgba (cr,border_red,border_green,border_blue,border_alpha)
  cairo_set_line_width (cr,border_width)
  border_bottom_left_x=bar_bottom_left_x-(border_width/2)
  border_bottom_left_y=bar_bottom_left_y+(border_width/2)
  brec_width=bar_width+border_width
  brec_height=bar_height+border_width--remember that we need to make this value negative at some point because we are drawing up
  cairo_rectangle (cr,border_bottom_left_x,border_bottom_left_y,brec_width,-brec_height)
  cairo_stroke (cr)
  --############################
end--of bar_indicator function

the next thing is to think about how we get the settings information inside the conky_main function to the bar drawing code inside the indicator_bar function we do this using curly brackets

--we call the function in the main function like so
indicator_bar(information to send to the function)

--and we need to set up the function to accept the information
function indicator_bar(information that was sent to the function)

there are a couple of ways to ago about this (as always) but for this example we will be sending and receiving strings

so what strings does the drawing code in indicator_bar need? the same strings that we set in the settings part,

we can write them in one line between our curved brackets separating each string from the next by a comma these are the strings that indicator_bar needs to receive to work properly so we can write the following:

function indicator_bar(bar_bottom_left_x,bar_bottom_left_y,bar_width,bar_height,bar_value,bar_max_value,bar_bg_red,bar_bg_green,bar_bg_blue,bar_bg_alpha,bar_border,border_red,border_green,border_blue,border_alpha,border_width,mid_value,lr,lg,lb,la,mr,mg,mb,ma,alarm_value,ar,ag,ab,aa)

that is our indicator_bar function finished.

NOTE the first code line in this function is a color setup line cairo_set_source_rgba(cr,bar_bg_red,bar_bg_green,bar_bg_blue,bar_bg_alpha) and the first string this line needs is bar_bg_red in our function the code takes the value for bar_bg_red directly from the string of the same name found in the curved brackets following the function name

the same goes for all the other string values that the code needs

so when we call the function inside conky_main, these are the things that we need to send

indicator_bar(bar_bottom_left_x,bar_bottom_left_y,bar_width,bar_height,bar_value,bar_max_value,bar_bg_red,bar_bg_green,bar_bg_blue,bar_bg_alpha,bar_border,border_red,border_green,border_blue,border_alpha,border_width,mid_value,lr,lg,lb,la,mr,mg,mb,ma,alarm_value,ar,ag,ab,aa)

NOTE when sending the information in the function call above there are several ways we can go about it

  • we can input our values directly into the function call like so indicator_bar(100,100,30,100,tonumber(conky_parse("${cpu}")),100,0.5,0.5,0.5,1,1,0,1,1,1,10,50,0,1,0,1,1,1,0,1,80,1,0,0,1) we send this to indicator_bar

it takes the first string in its "string list" (the list inside the curly brackets following the functions name) and sets that string to the value of the first thing in the "received list" which is the list of values received from the function call bar_bottom_left_x=100 then it sets each subsequent string in its "string list" to each subsequent value the "received list" next, 2nd entry in "string list" = 2nd entry in "received list" bar_bottom_left_y=100 next, 3rd entry in "string list" = 3rd entry in "received list" bar_width=30

SO every string in the functions "string list" requires a matching value in the list of things the function received from the function call

if we miss out a value (or put in an extra value) between () in the function call, the function just keeps on assigning vales in the order they are received leading to strings being assigned the wrong values

if we send 9 values in the function call but there are 10 strings to be set in the functions "string list" then the 10th string will have no value and be set to nil

trying to perform operations on strings that have a value of nil is a sure way to an error

i'll continue with function in a next part, i think i have already over-explained everything :D

You can’t perform that action at this time.