1import display_driver 2import lvgl as lv 3 4# A class is used to keep track of the series list because later we 5# need to draw to the series in the reverse order to which they were initialised. 6class StackedAreaChart: 7 def __init__(self): 8 self.obj = None 9 self.series_list = [None, None, None] 10 11stacked_area_chart = StackedAreaChart() 12 13# 14# Callback which draws the blocks of colour under the lines 15# 16def draw_event_cb(e): 17 18 obj = e.get_target() 19 cont_a = lv.area_t() 20 obj.get_coords(cont_a) 21 22 #Add the faded area before the lines are drawn 23 dsc = e.get_draw_part_dsc() 24 if dsc.part == lv.PART.ITEMS: 25 if not dsc.p1 or not dsc.p2: 26 return 27 28 # Add a line mask that keeps the area below the line 29 line_mask_param = lv.draw_mask_line_param_t() 30 line_mask_param.points_init(dsc.p1.x, dsc.p1.y, dsc.p2.x, dsc.p2.y, lv.DRAW_MASK_LINE_SIDE.BOTTOM) 31 line_mask_id = lv.draw_mask_add(line_mask_param, None) 32 33 #Draw a rectangle that will be affected by the mask 34 draw_rect_dsc = lv.draw_rect_dsc_t() 35 draw_rect_dsc.init() 36 draw_rect_dsc.bg_opa = lv.OPA.COVER 37 draw_rect_dsc.bg_color = dsc.line_dsc.color 38 39 a = lv.area_t() 40 a.x1 = dsc.p1.x 41 a.x2 = dsc.p2.x 42 a.y1 = min(dsc.p1.y, dsc.p2.y) 43 a.y2 = cont_a.y2 - 13 # -13 cuts off where the rectangle draws over the chart margin. Without this an area of 0 doesn't look like 0 44 dsc.draw_ctx.rect(draw_rect_dsc, a) 45 46 # Remove the mask 47 lv.draw_mask_free_param(line_mask_param) 48 lv.draw_mask_remove_id(line_mask_id) 49 50 51# 52# Helper function to round a fixed point number 53# 54def round_fixed_point(n, shift): 55 # Create a bitmask to isolates the decimal part of the fixed point number 56 mask = 1 57 for bit_pos in range(shift): 58 mask = (mask << 1) + 1 59 60 decimal_part = n & mask 61 62 # Get 0.5 as fixed point 63 rounding_boundary = 1 << (shift - 1) 64 65 # Return either the integer part of n or the integer part + 1 66 if decimal_part < rounding_boundary: 67 return (n & ~mask) 68 return ((n >> shift) + 1) << shift 69 70 71# 72# Stacked area chart 73# 74def lv_example_chart_8(): 75 76 #Create a stacked_area_chart.obj 77 stacked_area_chart.obj = lv.chart(lv.scr_act()) 78 stacked_area_chart.obj.set_size(200, 150) 79 stacked_area_chart.obj.center() 80 stacked_area_chart.obj.set_type( lv.chart.TYPE.LINE) 81 stacked_area_chart.obj.set_div_line_count(5, 7) 82 stacked_area_chart.obj.add_event_cb( draw_event_cb, lv.EVENT.DRAW_PART_BEGIN, None) 83 84 # Set range to 0 to 100 for percentages. Draw ticks 85 stacked_area_chart.obj.set_range(lv.chart.AXIS.PRIMARY_Y,0,100) 86 stacked_area_chart.obj.set_axis_tick(lv.chart.AXIS.PRIMARY_Y, 3, 0, 5, 1, True, 30) 87 88 #Set point size to 0 so the lines are smooth 89 stacked_area_chart.obj.set_style_size(0, lv.PART.INDICATOR) 90 91 # Add some data series 92 stacked_area_chart.series_list[0] = stacked_area_chart.obj.add_series(lv.palette_main(lv.PALETTE.RED), lv.chart.AXIS.PRIMARY_Y) 93 stacked_area_chart.series_list[1] = stacked_area_chart.obj.add_series(lv.palette_main(lv.PALETTE.BLUE), lv.chart.AXIS.PRIMARY_Y) 94 stacked_area_chart.series_list[2] = stacked_area_chart.obj.add_series(lv.palette_main(lv.PALETTE.GREEN), lv.chart.AXIS.PRIMARY_Y) 95 96 for point in range(10): 97 # Make some random data 98 vals = [lv.rand(10, 20), lv.rand(20, 30), lv.rand(20, 30)] 99 100 fixed_point_shift = 5 101 total = vals[0] + vals[1] + vals[2] 102 draw_heights = [0, 0, 0] 103 int_sum = 0 104 decimal_sum = 0 105 106 # Fixed point cascade rounding ensures percentages add to 100 107 for series_index in range(3): 108 decimal_sum += int(((vals[series_index] * 100) << fixed_point_shift) // total) 109 int_sum += int((vals[series_index] * 100) / total) 110 111 modifier = (round_fixed_point(decimal_sum, fixed_point_shift) >> fixed_point_shift) - int_sum 112 113 # The draw heights are equal to the percentage of the total each value is + the cumulative sum of the previous percentages. 114 # The accumulation is how the values get "stacked" 115 draw_heights[series_index] = int(int_sum + modifier) 116 117 # Draw to the series in the reverse order to which they were initialised. 118 # Without this the higher values will draw on top of the lower ones. 119 # This is because the Z-height of a series matches the order it was initialised 120 stacked_area_chart.obj.set_next_value( stacked_area_chart.series_list[3 - series_index - 1], draw_heights[series_index]) 121 122 stacked_area_chart.obj.refresh() 123 124lv_example_chart_8() 125