1 --[[PIE CHART WIDGET BY WLOURF v1.21 26 june 2010
3 This widget draw a pie chart or a ring in a conky window.
4 More info on the parameters with pictures on this page :
5 http://u-scripts.blogspot.com/2010/04/pie-chart-widget.html
8 tableV -- table of labels and values {{label,conky_variable,conky_argument,convert to Go-Mo-Ko units (true/false) or unit}, ...}
9 -- this table is mandatory, others parameters are optionals
12 {"cpu 0","cpu","cpu0",100,"%"},
13 {"cpu 1","cpu","cpu1",100,"%"},
15 -- to know disk space, use this line :
16 tableV=read_df(true,true),
18 xc -- x center of the pie, default = conky_window.width/2
19 yc, -- y center of the pie, default = conky_window.height/2
20 radius_int -- internal radius in pixel, default = conky_window.width/6
21 radius_ext -- external radius in pixel, default = conky_window.width/4
22 first_angle -- first angle of the pie (in degrees), default=0
23 last_angle -- last angle of the pie (in degrees), default=360
24 type_arc, -- fill the arc in a linear way (ring) or radial way (pie), values l or r, default=l
25 inverse_l_arc -- inverse arc for rings (true/false), default=false
26 proportional -- display proportional sectors (true/false); default =false
27 gradient_effect -- gradient effect (true/false), default=true
28 line_length -- length for horizontal line (from radius_ext to end of line), default=radius_ext
29 line_thickness -- thickness of line, default=1
30 line_space -- vertical space between two lines, default=10 pixels
31 extend_line -- grow up the line (true/false) if length of text> line_length, default=true
32 nb_decimals -- number of decimals for numbers, default=1
33 show_text -- display text (true/false), default=true
34 font_name -- font, default "Nokia Pure"
35 font_size -- font size, default=12
36 font_color -- font color (for gradient) or nil (for constant color), default = nil
37 font_alpha -- font alpha, default=1
38 txt_offset -- space between text and line, default=1
39 txt_format -- string for formatting text, possibles values are : default = "&l : &v"
41 -- &o for occupied percentage
42 -- &f for free percentage
44 -- &n for free value (non-occupied)
46 -- &p for percentage value of full graph
47 tablebg -- table of tables of colours for background {colors,alpha}
48 tablefg -- table of tables of colours for foreground {colors,alpha}
51 v1.0 10/04/2010 original release
52 v1.1 15/05/2010 the parameters are in a table (pie_settings), only the values in pie_settings.tableV are mandatory
53 added an option to draw values like a ring (type_arc="l")
54 v1.2 26/05/2010 add inverse_l parameter (for type_arc="l")
55 bug fix : line_length problem
56 read_df function improved
57 v1.21 26/06/2010 rename some parameters and write more infos
65 function conky_main_pie()
66 if conky_window==nil then return end
69 -- ------------------PARAMETERS TO SET-----------------------
70 --theses parameters are called many times so I put them into variables
71 local font_name,font_size="Nokia Pure",14
72 local col0,col1,col2=0xFFFFCC,0xCCFF99,0x99FF00
76 local temp = os.date("*t")
78 if hour>12 then hour=hour-12 end
79 local hpc,mpc,spc=hour/12,temp.min/60,temp.sec/60
84 {--CIRCLE 4 : DISK SPACE
85 tableV=read_df(true,true),
98 txt_format="&l free &n",
111 {--CIRCLE 1 : ARCS 1 & 2 INTERNET SPEED
113 {"dl","downspeedf","eth1",1000,"kb/s"},
114 {"ul","upspeedf","eth1",100,"kb/s"},
137 {--CIRCLE 2 : ARC 1 CPU 0
139 {"cpu 0","cpu","cpu 0",100,"%"},
140 --{"cpu 1","cpu","cpu 1",100,"%"},
158 -- {--CIRCLE 2 : ARC 2 CPU 1
160 -- --{"cpu 0","cpu","cpu 0",100,"%"},
161 -- {"cpu 1","cpu","cpu 1",100,"%"},
170 -- inverse_l_arc=true,
184 {--CIRCLE 3 : MEMORY : ram
186 {"mem","memperc","",100,"%"},
197 gradient_effect=true,
210 -------------------END OF PARAMETERS ---------------
212 local w=conky_window.width
213 local h=conky_window.height
214 local cs=cairo_xlib_surface_create(conky_window.display, conky_window.drawable, conky_window.visual, w, h)
217 if tonumber(conky_parse('${updates}'))>5 then
218 for i in pairs(pie_settings) do
219 draw_pie(pie_settings[i])
222 cairo_surface_destroy(cs)
228 function string:split(delimiter)
229 --source for the split function : http://www.wellho.net/resources/ex.php4?item=u108/split
232 local delim_from, delim_to = string.find( self, delimiter, from )
234 table.insert( result, string.sub( self, from , delim_from-1 ) )
236 delim_from, delim_to = string.find( self, delimiter, from )
238 table.insert( result, string.sub( self, from ) )
243 function rgb_to_r_g_b(colour, alpha)
244 return ((colour / 0x10000) % 0x100) / 255., ((colour / 0x100) % 0x100) / 255., (colour % 0x100) / 255., alpha
247 function round(num, idp)
248 local mult = 10^(idp or 0)
249 return math.floor(num * mult + 0.5) / mult
252 function size_to_text(size,nb_dec)
254 if nb_dec<0 then nb_dec=0 end
255 size = tonumber(size)
256 if size>1024*1024*1024 then
257 txt_v=string.format("%."..nb_dec.."f G",size/1024/1024/1024)
258 elseif size>1024*1024 then
259 txt_v=string.format("%."..nb_dec.."f M",size/1024/1024)
260 elseif size>1024 then
261 txt_v=string.format("%."..nb_dec.."f K",size/1024)
263 txt_v=string.format("%."..nb_dec.."f B",size)
268 function text_to_bytes(txt_in)
271 txt_modifier = string.match(txt_in,"%d(%a)")
272 num_bytes = string.gsub(txt_in,"%d(%a)","")
273 if txt_modifier == "G" then
274 num_bytes = num_bytes*1024*1024*1024
275 elseif txt_modifier == "M" then
276 num_bytes = num_bytes*1024*1024
277 elseif txt_modifier == "K" then
278 num_bytes = num_bytes*1024
279 elseif txt_modifier == "B" then
280 num_bytes = num_bytes
287 function read_df(show_media,sort_table)
288 --read output of command df and return arrays of value for files systems
289 --reurn array of table {file syst, "", occupied space , total space , convert to G, M, K ...}
291 local f = io.popen("df")
294 table.insert(results,{"/home/user/MyDocs","",text_to_bytes(conky_parse("${fs_used /home/user/MyDocs}")),text_to_bytes(conky_parse("${fs_size /home/user/MyDocs}")),true})
295 table.insert(results,{"/home","",text_to_bytes(conky_parse("${fs_used /home}")),text_to_bytes(conky_parse("${fs_size /home}")),true})
296 table.insert(results,{"rootfs","",text_to_bytes(conky_parse("${fs_used /}")),text_to_bytes(conky_parse("${fs_size /}")),true})
297 table.insert(results,{"SD","",text_to_bytes(conky_parse("${fs_used /media/mmc1}")),text_to_bytes(conky_parse("${fs_size /media/mmc1}")),true})
298 --table.insert(results,{"/home/user/MyDocs2","","30","50",true})
299 --table.insert(results,{text_to_bytes(conky_parse("${fs_used /home/user/MyDocs}")),"","10","50",true})
301 -- local line = f:read("*l")
302 -- if line == nil then break end
303 -- while string.match(line," ") do
304 -- line=string.gsub(line," "," ")
306 -- local arr_l=string.split(line," ")
307 -- local match = string.match(arr_l[1],"/")
309 -- if string.match(arr_l[1],"/") then
310 -- if not show_media then arr_l[6]=string.gsub(arr_l[6],"/media/","",1) end
311 -- if string.match(arr_l[1],"opt/") then break end
312 -- table.insert(results,{arr_l[6],"",(arr_l[2]-arr_l[4])*1024,arr_l[2]*1024,true})
318 -- if sort_table then
319 -- --how to sort table into table?
322 -- for k=2, #results do
324 -- if tonumber(results[1][3])>tonumber(results[2][3]) then
325 -- local tmpV = results[1]
326 -- results[1] = results[2]
330 -- if tonumber(results[k][3])<tonumber(results[k-1][3]) then
331 -- local tmpV = results[k-1]
332 -- results[k-1] = results[k]
340 return results --array {file syst, occupied space , total space }
346 if t.tableV==nil then
347 print ("No input values ...")
353 if t.xc==nil then t.xc=conky_window.width/2 end
354 if t.yc==nil then t.yc=conky_window.height/2 end
355 if t.int_radius ==nil then t.int_radius =conky_window.width/6 end
356 if t.radius ==nil then t.radius =conky_window.width/4 end
357 if t.first_angle==nil then t.first_angle =0 end
358 if t.last_angle==nil then t.last_angle=360 end
359 if t.proportional==nil then t.proportional=false end
360 if t.tablebg==nil then t.tablebg={{0xFFFFFF,0.5},{0xFFFFFF,0.5}} end
361 if t.tablefg==nil then t.tablefg={{0xFF0000,1},{0x00FF00,1}} end
362 if t.gradient_effect==nil then t.gradient_effect=true end
363 if t.show_text==nil then t.show_text=true end
364 if t.line_lgth==nil then t.line_lgth=t.int_radius end
365 if t.line_space==nil then t.line_space=10 end
366 if t.line_width==nil then t.line_width=1 end
367 if t.extend_line==nil then t.extend_line=true end
368 if t.txt_font==nil then t.txt_font="Nokia Pure" end
369 if t.font_size==nil then t.font_size=12 end
370 --if t.font_color==nil then t.font_color=0xFFFFFF end
371 if t.font_alpha==nil then t.font_alpha = 1 end
372 if t.txt_offset==nil then t.txt_offset = 1 end
373 if t.txt_format==nil then t.txt_format = "&l : &v" end
374 if t.nb_decimals==nil then t.nb_decimals=1 end
375 if t.type_arc==nil then t.type_arc="l" end
376 if t.inverse_l_arc==nil then t.inverse_l_arc=false end
379 local function draw_sector(tablecolor,colorindex,pc,lastAngle,angle,radius,int_radius,gradient_effect,type_arc,inverse_l_arc)
380 --draw a portion of arc
381 radiuspc=(radius-int_radius)*pc+int_radius
384 if type_arc=="l" then
385 val=pc;radiuspc=radius
389 if type_arc=="l" and inverse_l_arc then
393 cairo_rotate(cr,angle0+angle)
395 if gradient_effect then
396 local pat = cairo_pattern_create_radial (0,0, int_radius, 0,0,radius)
397 cairo_pattern_add_color_stop_rgba (pat, 0, rgb_to_r_g_b(tablecolor[colorindex][1],0))
398 cairo_pattern_add_color_stop_rgba (pat, 0.5, rgb_to_r_g_b(tablecolor[colorindex][1],tablecolor[colorindex][2]))
399 cairo_pattern_add_color_stop_rgba (pat, 1, rgb_to_r_g_b(tablecolor[colorindex][1],0))
400 cairo_set_source (cr, pat)
401 cairo_pattern_destroy(pat)
403 cairo_set_source_rgba(cr,rgb_to_r_g_b(tablecolor[colorindex][1],tablecolor[colorindex][2]))
405 cairo_move_to(cr,0,-int_radius)
406 cairo_line_to(cr,0,-radiuspc)
407 cairo_rotate(cr,-math.pi/2)
409 cairo_arc_negative(cr,0,0,radiuspc,0,-angle1)
410 cairo_rotate(cr,-math.pi/2-angle1)
411 cairo_line_to(cr,0,int_radius)
412 cairo_rotate(cr,math.pi/2)
413 cairo_arc(cr,0,0,int_radius,0,angle1)
414 cairo_close_path (cr);
422 cairo_rotate(cr,angle0)
424 if gradient_effect then
425 local pat = cairo_pattern_create_radial (0,0, int_radius, 0,0,radius)
426 cairo_pattern_add_color_stop_rgba (pat, 0, rgb_to_r_g_b(tablecolor[colorindex][1],0))
427 cairo_pattern_add_color_stop_rgba (pat, 0.5, rgb_to_r_g_b(tablecolor[colorindex][1],tablecolor[colorindex][2]))
428 cairo_pattern_add_color_stop_rgba (pat, 1, rgb_to_r_g_b(tablecolor[colorindex][1],0))
429 cairo_set_source (cr, pat)
430 cairo_pattern_destroy(pat)
432 cairo_set_source_rgba(cr,rgb_to_r_g_b(tablecolor[colorindex][1],tablecolor[colorindex][2]))
434 cairo_move_to(cr,0,-int_radius)
435 cairo_line_to(cr,0,-radiuspc)
436 cairo_rotate(cr,-math.pi/2)
438 cairo_arc(cr,0,0,radiuspc,0,angle1)
439 cairo_rotate(cr,angle1-math.pi/2)
440 cairo_line_to(cr,0,int_radius)
441 cairo_rotate(cr,math.pi/2)
442 cairo_arc_negative(cr,0,0,int_radius,0,-angle1)
443 cairo_close_path (cr);
452 function draw_lines(idx,nbArcs,angle,table_colors,idx_color,adjust,line_lgth,length_txt,txt_offset,radius,line_width,line_space,font_color,font_alpha)
455 local x0=radiuspc*math.sin(lastAngle+angle/2)
456 local y0=-radiuspc*math.cos(lastAngle+angle/2)
457 local x1=1.2*radius*math.sin(lastAngle+angle/2)
458 local y1=-1.2*radius*math.cos(lastAngle+angle/2)
468 if x0>0 and x2-x1<length_txt then x2=x1+length_txt end
469 if x0<=0 and x1-x2<length_txt then x2=x1-length_txt end
473 local dY = math.abs(y2-lastPt2[2])
474 if dY < line_space and lastPt2[1]*x1>0 then
476 y2 = line_space+lastPt2[2]
478 y2 = -line_space+lastPt2[2]
480 if (y2>y1 and x0>0) or (y2<y1 and x0<0 ) then
481 --x3 is for vertical segment if needed
484 if x3>0 then x3=x3+txt_offset end
486 Z=intercept({x0,y0},{x1,y1},{0,y2},{1,y2})
491 --remind x2,y2 of first value
492 x2first,y2first = x2,y2
495 if font_color==nil then
496 cairo_set_source_rgba(cr,rgb_to_r_g_b(table_colors[idx_color][1],table_colors[idx_color][2]))
498 local pat = cairo_pattern_create_linear (x2,y2, x0,y0)
499 cairo_pattern_add_color_stop_rgba (pat, 0, rgb_to_r_g_b(font_color,font_alpha))
500 cairo_pattern_add_color_stop_rgba (pat, 1, rgb_to_r_g_b(table_colors[idx_color][1],table_colors[idx_color][2]))
501 cairo_set_source (cr, pat)
502 cairo_pattern_destroy(pat)
506 cairo_move_to(cr,x0,y0)
507 cairo_line_to(cr,x1,y1)
508 cairo_line_to(cr,x2,y2)
510 cairo_line_to(cr,x3,y3)
513 cairo_set_line_width(cr,line_width)
515 --lastAngle=lastAngle+angle
519 function intercept(p11,p12,p21,p22)
520 --calculate interscetion of two lines and return coordinates
521 a1=(p12[2]-p11[2])/(p12[1]-p11[1])
523 a2=(p22[2]-p21[2])/(p22[1]-p21[1])
536 if t.first_angle>=t.last_angle then
537 local tmp_angle=t.last_angle
538 --t.last_angle=t.first_angle
539 --t.first_angle=tmp_angle
540 print ("inversed angles")
543 if t.last_angle-t.first_angle>360 and t.first_angle>0 then
544 t.last_angle=360+t.first_angle
545 print ("reduce angles")
548 if t.last_angle+t.first_angle>360 and t.first_angle<=0 then
549 t.last_angle=360+t.first_angle
550 print ("reduce angles")
553 if t.int_radius<0 then t.int_radius =0 end
554 if t.int_radius>t.radius then
555 local tmp_radius=t.radius
556 t.radius=t.int_radius
557 t.int_radius=tmp_radius
558 print ("inversed angles")
560 if t.int_radius==t.radius then
562 print ("int radius set to 0")
567 cairo_translate(cr,t.xc,t.yc)
568 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND)
569 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND)
572 local anglefull= (t.last_angle-t.first_angle)*math.pi/180
575 fullsize=fullsize+tableV[i][4]
578 local cb,cf,angle=0,0,anglefull/nbArcs
579 lastAngle=t.first_angle*math.pi/180
583 if t.proportional then
584 angle=tableV[i][4]/fullsize*anglefull
588 if cb>#t.tablebg then cb=1 end
589 if cf>#t.tablefg then cf=1 end
591 if tableV[i][2]~="" then
592 str=string.format('${%s %s}',tableV[i][2],tableV[i][3])
598 if value==nil then value=0 end
601 draw_sector(t.tablebg,cb,1,lastAngle,angle,t.radius,t.int_radius,t.gradient_effect,t.type_arc,t.inverse_l_arc)
602 --print (t.tablefg,cf,tableV[i][2],tableV[i][3],lastAngle,angle,t.radius,t.int_radius)
603 --print (cf,tableV[i],tableV[i][2],tableV[i][3])
604 draw_sector(t.tablefg,cf,value/tableV[i][4],lastAngle,angle,t.radius,t.int_radius,t.gradient_effect,t.type_arc,t.inverse_l_arc)
608 local txt_l = tableV[i][1]
609 local txt_opc = round(100*value/tableV[i][4],t.nb_decimals).."%%"
610 local txt_fpc = round(100*(tableV[i][4]-value/tableV[i][4]),t.nb_decimals).."%%"
611 local txt_ov,txt_fv,txt_max
612 if tableV[i][5]==true then
613 txt_ov = size_to_text(value,t.nb_decimals)
614 txt_fv = size_to_text(tableV[i][4]-value,t.nb_decimals)
615 txt_max = size_to_text(tableV[i][4],t.nb_decimals)
617 if tableV[i][5]=="%" then tableV[i][5]="%%" end
618 txt_ov=string.format("%."..t.nb_decimals.."f ",value)..tableV[i][5]
619 txt_fv=string.format("%."..t.nb_decimals.."f",tableV[i][4]-value)..tableV[i][5]
620 txt_max=string.format("%."..t.nb_decimals.."f",tableV[i][4])..tableV[i][5]
622 txt_pc = string.format("%."..t.nb_decimals.."f",100*tableV[i][4]/fullsize).."%%"
623 local txt_out = t.txt_format
624 txt_out = string.gsub(txt_out,"&l",txt_l) --label
625 txt_out = string.gsub(txt_out,"&o",txt_opc)--occ. %
626 txt_out = string.gsub(txt_out,"&f",txt_fpc)--free %
627 txt_out = string.gsub(txt_out,"&v",txt_ov) --occ. value
628 txt_out = string.gsub(txt_out,"&n",txt_fv) --free value
629 txt_out = string.gsub(txt_out,"&m",txt_max)--max
630 txt_out = string.gsub(txt_out,"&p",txt_pc)--percent
632 local te=cairo_text_extents_t:create()
633 cairo_set_font_size(cr,t.font_size)
634 cairo_select_font_face(cr, t.txt_font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL)
635 cairo_text_extents (cr,txt_out,te)
638 lastPt2=draw_lines(i,nbArcs,angle,t.tablefg,cf,t.extend_line,t.line_lgth+t.radius,
639 te.width + te.x_bearing,t.txt_offset,t.radius,t.line_width,t.line_space,t.font_color,t.font_alpha)
642 local yA=lastPt2[2]-t.line_width-t.txt_offset
643 if xA>0 then xA = xA-(te.width + te.x_bearing) end
644 cairo_move_to(cr,xA,yA)
645 cairo_show_text(cr,txt_out)
648 lastAngle=lastAngle+angle
654 --[[END OF PIE CHART WIDGET]]