line_graph.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. -- @author cedlemo
  2. local setmetatable = setmetatable
  3. local ipairs = ipairs
  4. local math = math
  5. local table = table
  6. local type = type
  7. local string = string
  8. local color = require("gears.color")
  9. local base = require("wibox.widget.base")
  10. local helpers = require("blingbling.helpers")
  11. local superproperties = require('blingbling.superproperties')
  12. ---Graph widget.
  13. --@module blingbling.linegraph
  14. local linegraph = { mt = {} }
  15. local data = setmetatable({}, { __mode = "k" })
  16. ---Fill all the widget (width * height) with this color (default is none ).
  17. --@usage mygraph:set_background_color(string) -->"#rrggbbaa"
  18. --@name set_background_color
  19. --@class function
  20. --@param color a string "#rrggbbaa" or "#rrggbb"
  21. ---Fill the graph area background with this color (default is none).
  22. --@usage mygraph:set_graph_background_color(string) -->"#rrggbbaa"
  23. --@name set_graph_background_color
  24. --@class function
  25. --@param color a string "#rrggbbaa" or "#rrggbb"
  26. ---Set rounded corners for background and graph background.
  27. --@usage mygraph:set_rounded_size(a) -> a in [0,1]
  28. --@name set_rounded_size
  29. --@class function
  30. --@param rounded_size float in [0,1]
  31. ---Define the top and bottom margin for the graph area.
  32. --@usage mygraph:set_v_margin(integer)
  33. --@name set_v_margin
  34. --@class function
  35. --@param margin an integer for top and bottom margin
  36. ---Define the left and right margin for the graph area.
  37. --@usage mygraph:set_h_margin(integer)
  38. --@name set_h_margin
  39. --@class function
  40. --@param margin an integer for left and right margin
  41. ---Define the graph color.
  42. --@usage mygraph:set_graph_color(string) -->"#rrggbbaa"
  43. --@name set_graph_color
  44. --@class function
  45. --@param color a string "#rrggbbaa" or "#rrggbb"
  46. ---Define the graph outline.
  47. --@usage mygraph:set_graph_line_color(string) -->"#rrggbbaa"
  48. --@name set_graph_line_color
  49. --@class function
  50. --@param color a string "#rrggbbaa" or "#rrggbb"
  51. ---Display text on the graph or not.
  52. --@usage mygraph:set_show_text(boolean) --> true or false
  53. --@name set_show_text
  54. --@class function
  55. --@param boolean true or false (default is false)
  56. ---Define the color of the text.
  57. --@usage mygraph:set_text_color(string) -->"#rrggbbaa"
  58. --@name set_text_color
  59. --@class function
  60. --@param color a string "#rrggbbaa" or "#rrggbb" defaul is white
  61. ---Define the background color of the text.
  62. --@usage mygraph:set_text_background_color(string) -->"#rrggbbaa"
  63. --@name set_text_background_color
  64. --@class function
  65. --@param color a string "#rrggbbaa" or "#rrggbb"
  66. ---Define the text's font .
  67. --@usage mygraph:set_font(string)
  68. --@name set_font
  69. --@class function
  70. --@param font a string that contains the font name family and weight
  71. ---Define the text font size.
  72. --@usage mygraph:set_font_size(integer)
  73. --@name set_font_size
  74. --@class function
  75. --@param size the font size
  76. ---Define the template of the text to display.
  77. --@usage mygraph:set_label(string)
  78. --By default the text is : (value_send_to_the_widget *100) .. "%"
  79. --static string: example set_label("CPU usage:") will display "CUP usage:" on the graph
  80. --dynamic string: use $percent in the string example set_label("Load $percent %") will display "Load 10%"
  81. --@name set_label
  82. --@class function
  83. --@param text the text to display
  84. local properties = { "width", "height", "h_margin", "v_margin",
  85. "background_color",
  86. "graph_background_border", "graph_background_color",
  87. "rounded_size", "graph_color", "graph_line_color",
  88. "show_text", "text_color", "font_size", "font",
  89. "text_background_color", "label"
  90. }
  91. function linegraph.draw(graph, wibox, cr, width, height)
  92. local max_value = data[graph].max_value
  93. local values = data[graph].values
  94. -- Set the values we need
  95. local value = data[graph].value
  96. local graph_border_width = 0
  97. if data[graph].graph_background_border then
  98. graph_border_width = 1
  99. end
  100. local v_margin = superproperties.v_margin
  101. if data[graph].v_margin and data[graph].v_margin <= data[graph].height/4 then
  102. v_margin = data[graph].v_margin
  103. end
  104. local h_margin = superproperties.h_margin
  105. if data[graph].h_margin and data[graph].h_margin <= data[graph].width / 3 then
  106. h_margin = data[graph].h_margin
  107. end
  108. local background_color = data[graph].background_color or superproperties.background_color
  109. local rounded_size = data[graph].rounded_size or superproperties.rounded_size
  110. local graph_background_color = data[graph].graph_background_color or superproperties.graph_background_color
  111. local graph_background_border = data[graph].graph_background_border or superproperties.graph_background_border
  112. local graph_color = data[graph].graph_color or superproperties.graph_color
  113. local graph_line_color = data[graph].graph_line_color or superproperties.graph_line_color
  114. local text_color = data[graph].text_color or superproperties.text_color
  115. local text_background_color = data[graph].text_background_color or superproperties.text_background_color
  116. local font_size =data[graph].font_size or superproperties.font_size
  117. local font = data[graph].font or superproperties.font
  118. local line_width = 1
  119. cr:set_line_width(line_width)
  120. cr:set_antialias("subpixel")
  121. helpers.draw_rounded_corners_rectangle( cr,
  122. 0, --x
  123. 0, --y
  124. data[graph].width,
  125. data[graph].height,
  126. background_color,
  127. rounded_size
  128. )
  129. -- Draw the graph background
  130. --if background_border is set, graph background must not be drawn on it
  131. local h_padding = 0
  132. local v_padding = 0
  133. if background_border ~= nil and h_margin < 1 then
  134. h_padding = 1
  135. else
  136. h_padding = h_margin + 1
  137. end
  138. if background_border ~= nil and v_margin < 1 then
  139. v_padding = 1
  140. else
  141. v_padding = v_margin + 1
  142. end
  143. helpers.draw_rounded_corners_rectangle(cr,
  144. h_padding, --x
  145. v_padding, --y
  146. data[graph].width - h_padding,
  147. data[graph].height - v_padding ,
  148. graph_background_color,
  149. rounded_size,
  150. graph_background_border)
  151. helpers.clip_rounded_corners_rectangle(cr,
  152. h_padding, --x
  153. v_padding, --y
  154. data[graph].width - h_padding,
  155. data[graph].height - v_padding,
  156. rounded_size
  157. )
  158. --Drawn the graph
  159. --if graph_background_border is set, graph must not be drawn on it
  160. --find nb values we can draw every column_length px
  161. --if rounded, make sure that graph don't begin or end outside background
  162. --check for the less value between hight and height to calculate the space for rounded size:
  163. local column_length = 6
  164. if data[graph].height > data[graph].width then
  165. less_value = data[graph].width/2
  166. else
  167. less_value = data[graph].height/2
  168. end
  169. max_column=math.ceil((data[graph].width - (2*h_padding +2*(rounded_size * less_value)))/column_length)
  170. --Check if the table graph values is empty / not initialized
  171. --if next(data[graph].values) == nil then
  172. if #data[graph].values == 0 or #data[graph].values ~= max_column then
  173. -- initialize graph_values with empty values:
  174. data[graph].values={}
  175. for i=1,max_column do
  176. --the following line feed the graph with random value if you uncomment it and comment the line after it
  177. --data[graph].values[i]=math.random(0,100) / 100
  178. data[graph].values[i]=0
  179. end
  180. end
  181. --Fill the graph
  182. x=data[graph].width -(h_padding + rounded_size * less_value)
  183. y=data[graph].height-(v_padding)
  184. cr:new_path()
  185. cr:move_to(x,y)
  186. cr:line_to(x,y)
  187. for i=1,max_column do
  188. y_range=data[graph].height - (2 * v_margin)
  189. y= data[graph].height - (v_padding + ((data[graph].values[i]) * y_range))
  190. cr:line_to(x,y)
  191. x=x-column_length
  192. end
  193. y=data[graph].height - (v_padding )
  194. cr:line_to(x + column_length ,y)
  195. cr:line_to(width - h_padding,data[graph].height - (v_padding ))
  196. cr:close_path()
  197. r,g,b,a=helpers.hexadecimal_to_rgba_percent(graph_color)
  198. cr:set_source_rgba(r, g, b, a)
  199. cr:fill()
  200. --Draw the graph line
  201. r,g,b,a=helpers.hexadecimal_to_rgba_percent(graph_line_color)
  202. cr:set_source_rgba(r, g, b,a)
  203. x=data[graph].width - (h_padding + rounded_size * less_value)
  204. y=data[graph].height-(v_padding)
  205. cr:new_path()
  206. cr:move_to(x,y )
  207. cr:line_to(x,y )
  208. for i=1,max_column do
  209. y_range=data[graph].height - (2 * h_margin + 1)
  210. y= data[graph].height - (v_margin + ((data[graph].values[i]) * y_range))
  211. cr:line_to(x,y )
  212. x=x-column_length
  213. end
  214. x=x + column_length
  215. y=data[graph].height - (v_padding )
  216. cr:line_to(x ,y )
  217. cr:stroke()
  218. if data[graph].show_text == true then
  219. --Draw Text and it's background
  220. cr:set_font_size(font_size)
  221. if type(font) == "string" then
  222. cr:select_font_face(font,nil,nil)
  223. elseif type(font) == "table" then
  224. cr:select_font_face(font.family or "Sans", font.slang or "normal", font.weight or "normal")
  225. end
  226. local value = data[graph].values[1] * 100
  227. if data[graph].label then
  228. text=string.gsub(data[graph].label,"$percent", value)
  229. else
  230. text=value .. "%"
  231. end
  232. helpers.draw_text_and_background(cr,
  233. text,
  234. h_padding + rounded_size * less_value,
  235. data[graph].height/2 ,
  236. text_background_color,
  237. text_color,
  238. false,
  239. true,
  240. false,
  241. false)
  242. end
  243. end
  244. function linegraph.fit(graph, width, height)
  245. return data[graph].width, data[graph].height
  246. end
  247. ---Add a value to the graph.
  248. --For compatibility between old and new awesome widget, add_value can be replaced by set_value
  249. --@usage mygraph:add_value(a) or mygraph:set_value(a)
  250. --@param graph the graph
  251. --@param value The value between 0 and 1.
  252. --@param group The stack color group index.
  253. local function add_value(graph, value, group)
  254. if not graph then return end
  255. local value = value or 0
  256. local values = data[graph].values
  257. if string.find(value, "nan") then
  258. value=0
  259. end
  260. local values = data[graph].values
  261. table.remove(values, #values)
  262. table.insert(values,1,value)
  263. graph:emit_signal("widget::updated")
  264. return graph
  265. end
  266. ---Set the graph height.
  267. --@param height The height to set.
  268. function linegraph:set_height( height)
  269. if height >= 5 then
  270. data[self].height = height
  271. self:emit_signal("widget::updated")
  272. end
  273. return self
  274. end
  275. ---Set the graph width.
  276. --@param width The width to set.
  277. function linegraph:set_width( width)
  278. if width >= 5 then
  279. data[self].width = width
  280. self:emit_signal("widget::updated")
  281. end
  282. return self
  283. end
  284. -- Build properties function
  285. for _, prop in ipairs(properties) do
  286. if not linegraph["set_" .. prop] then
  287. linegraph["set_" .. prop] = function(graph, value)
  288. data[graph][prop] = value
  289. graph:emit_signal("widget::updated")
  290. return graph
  291. end
  292. end
  293. end
  294. ---Create a graph widget.
  295. --@param args Standard widget() arguments. You should add width and height
  296. --key to set graph geometry.
  297. --@return A graph widget.
  298. function linegraph.new(args)
  299. local args = args or {}
  300. args.width = args.width or 100
  301. args.height = args.height or 20
  302. if args.width < 5 or args.height < 5 then return end
  303. local graph = base.make_widget()
  304. data[graph] = {}
  305. for _, v in ipairs(properties) do
  306. data[graph][v] = args[v]
  307. end
  308. data[graph].values = {}
  309. -- Set methods
  310. graph.set_value = add_value
  311. graph.add_value = add_value
  312. graph.draw = linegraph.draw
  313. graph.fit = linegraph.fit
  314. for _, prop in ipairs(properties) do
  315. graph["set_" .. prop] = linegraph["set_" .. prop]
  316. end
  317. return graph
  318. end
  319. function linegraph.mt:__call(...)
  320. return linegraph.new(...)
  321. end
  322. return setmetatable(linegraph, linegraph.mt)