Using Lua scripts (Part 05): Making a bar meter and a circle meter - brndnmtthws/conky GitHub Wiki

v: Making a bar meter and a circle meter

Make a cpu indicator bar

We made a line that changes length according to the reading of cpu% from Conky but to make that a bit more interesting we will make a vertical indicator bar to show cpu. The bar will have a colored background and a different colored indicator. It will also be resizeable and placeable through settings.

So where to start?

First we need to think about the order of operations in Lua with cairo. Things drawn lower down in the script will be drawn over the top of things drawn above.

So our colored background will need to be behind the indicator, we need to draw the background first.

We are going to define some settings

  1. where we want the indicator to be
  2. how big we want the indicator
  3. the colors for our background and indicator

So our setting section might look like this

-- SETTINGS FOR CPU INDICATOR BAR
bar_bottom_left_x = 50
bar_bottom_left_y = 200
bar_width= 30
bar_height= 100

-- Set bar background colors, 1, 0, 0, 1 = fully opaque red.
bar_bg_red = 1
bar_bg_green = 0
bar_bg_blue = 0
bar_bg_alpha = 1

-- Set indicator colors, 1, 1, 1, 1 = fully opaque white.
bar_in_red = 1
bar_in_green = 1
bar_in_blue = 1
bar_in_alpha = 1

We want unique names so that there is no chance of names being duplicated elsewhere in the script and overwriting our settings (I usually have my string names tell me something about the information kept in the string). It can be a pain to type long string names over and over again but worse when you run the script and it doesn't work as expected because a string has been overwritten.

Drawing the background is then pretty simple, we will used a filled-in rectangle. The only thing to note is that by asking for the bottom coordinates, when we come to set the height of the rectangle our height will be negative (because we are drawing up!).

We are using the rectangle function, so we don't need to specify an end cap type, and we are using fill so we don't need to specify line join type or line width.

-- 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)

So we have a red rectangle. Now we need to think about making the indicator bar first. We need to get our reading into a string with value = tonumber (conky_parse ("${cpu}")).

We are going to use the string value to affect the height of the indicator bar, but we have a further complication in that we want out to be able to set indicator size in our settings part. We need some math!

If we wanted out indicator to be 100 pixels high we would just be able to substitute cpu reading for height, as cpu will give us numbers between 0 and 100.

But say we wanted our indicator to be 200 pixels high or only 50 pixels high, we need to scale up or down our cpu readings so that the indicator bar grows and shrinks in the right proportions.

We know our maximum value for cpu is going to be 100. We need to divide the height we want by this maximum value to get the scale right.

If we set bar_height = 200 and max_value = 100 then scale = bar_height / max_value.

Scale = 2, so that, for every increase of 1 in cpu % reading, the bar will move an additional 2 pixels up. This means that at a cpu % of 100 the bar will be 200 pixels long, which is what we want.

So indicator_height = scale * value, and this is the number we plug into our rectangle drawing command.

All together for the indicator:

-- Draw indicator. 
cairo_set_source_rgba (cr, bar_in_red, bar_in_green, bar_in_blue,
		bar_in_alpha) -- Set indicator color. 
value = tonumber (conky_parse ("${cpu}"))
max_value = 100
scale = bar_height / max_value
indicator_height = scale * value

cairo_rectangle (cr, bar_bottom_left_x, bar_bottom_left_y, bar_width,
		-indicator_height)

cairo_fill (cr)

And you have a working cpu indicator bar

NOW, what you could do is copy this code chunk over and over again, edit the settings and assign a different Conky object to each instance for as many bars as you want set up some text to label each bar. HOWEVER, there is a better and easier way to get multiple bars: we can turn our indicator bar drawing code into a function -- but more about functions in a later part!

Making a cpu indicator circle meter

I'm picking cpu as my object of choice for no other reason than it is the one that changes the most, so you get to see your handy work in action :).

Making a circle meter is very similar to the bar, except we are using the output of cpu% from Conky to change the angles passed to the arc drawing command.

We will have a ring meter with a background ring, one color and an indicator ring of a different color. we will make the ring sizeable through settings. We will have the ring show 0 cpu at the top and then as cpu increases the indicator bar will increase in size clockwise around the ring until at 100% it makes a complete circle and ends back at the top.

We are going clockwise, so we will use cairo_arc. Unlike with the rectangle, we will be using cairo_stroke to draw our meter, which means caro_set_line_width must be set to give us the width of our indicator lines. Setting the line cap type will have an effect on what the meter looks like but we are not joining any lines so there is no point setting line join type

First you need to think about what kind of setting you want.

A reminder of how we draw an arc: cairo_arc (cr, center_x, center_y, radius, start_angle, end_angle).

-- SETTINGS
-- rings size
ring_center_x = 200
ring_center_y = 200
ring_radius = 50
ring_width = 20

-- Colors.

-- Set background colors, 1, 0, 0, 1 = fully opaque red.
ring_bg_red = 1
ring_bg_green = 0
ring_bg_blue = 0
ring_bg_alpha = 1

-- Set indicator colors, 1, 1, 1, 1 = fully opaque white.
ring_in_red = 1
ring_in_green = 1
ring_in_blue = 1
ring_in_alpha = 1

-- Indicator value settings.
value = conky_parse ("${cpu}")
max_value = 100

Just as with the rectangle, go ahead and draw the background ring like so.

-- Draw background.
cairo_set_line_width (cr, ring_width)
cairo_set_source_rgba (cr, ring_bg_red, ring_bg_green, ring_bg_blue,
		ring_bg_alpha)
cairo_arc (cr, ring_center_x, ring_center_y, ring_radius, 0, 2 * math.pi)
cairo_stroke (cr)

NOTE: about line_width. Line width when we are drawing a circle or arc works the same way as a straight line. Half of the line width is drawn to one side of the base line, and the other drawn to the other side. This means if you set a radius of 50 pixels and a line width of 20 pixels, then the distance from the centre point of the circle to the inner edge of the line will only be 40 pixels and it will be 60 pixels to the outer edge of the circle.

Now that we have a red background circle we have to work out how to apply the cpu% number held in the string "value" to make the indicator line move.

In this case it is the arc's end angle that will be changing relative to cpu%.

Again we need a bit of math to get the indicator arc moving in the right proportion.

degrees = 360
scale = degrees / max_value -- ie for every 1% increase in cpu% 
                            -- the arc should move an additional
                             -- 3.6 degrees.
end_angle = value * scale

Or I could just write end_angle = value * (360 / max_value). Now we can plug everything into the arc drawing command like so:

-- Draw indicator.
cairo_set_line_width (cr, ring_width)
end_angle = value * (360 / max_value)
cairo_set_source_rgba (cr, ring_in_red, ring_in_green, ring_in_blue,
		ring_in_alpha)
cairo_arc (cr, ring_center_x, ring_center_y, ring_radius, 0,
		(end_angle - 90) * (math.pi / 180))
cairo_stroke (cr)

HUH??? If you try the above code you will notice that something doesn't quite seem right. I thought I would put this in to take a look at what to do when things go wrong :D.

The first thing I tried was adding a print line to the code like this:

-- Draw indicator.
cairo_set_line_width (cr, ring_width)
end_angle = value * (360 / max_value)
print (end_angle)
cairo_set_source_rgba (cr, ring_in_red, ring_in_green, ring_in_blue,
		ring_in_alpha)
cairo_arc (cr, ring_center_x, ring_center_y, ring_radius, 0,
		(end_angle - 90) * (math.pi / 180))
cairo_stroke (cr)

Was I calculating something wrong?? So I took a look at the terminal and watched what end_angle was being calculated as. I also went to the conky.conf and put in ${cpu} in conky.text = [ ... ](/brndnmtthws/conky/wiki/-...-) so I could watch cpu at the same time (I could have just put print (value, end_angle) and gotten both value in the terminal I suppose).

But it looked as if the calculations were giving me the correct result, so it must be something in the cairo_arc command itself that was going wrong (cairo_arc (cr, ring_center_x, ring_center_y, ring_radius, 0, (end_angle - 90) * (math.pi / 180))).

It was the start angle, which I had left at 0. I hadn't compensated for the arc command quirk so actually my 0 line was 90 degrees further around my circle than I thought it was!

A quick modification later:

cairo_set_line_width (cr, ring_width)
end_angle = value * (360 / max_value)
print (end_angle)
cairo_set_source_rgba (cr, ring_in_red, ring_in_green, ring_in_blue,
		ring_in_alpha)
cairo_arc (cr, ring_center_x, ring_center_y, ring_radius,(-90) *
		(math.pi / 180), (end_angle - 90) * (math.pi / 180))
cairo_stroke (cr)

And we have a circle meter.