There are a lot of good charting packages available for IoT and Rasperry Pi projects.
The PySimpleGUI python libary stands out in its ability to have the same code for both a local GUI and a Web interface. PySimpleGUI isn’t focused as a charting package but it has the canvas and graph elements that allow you to create real time bar charts and real time trend charts.
Getting Started with Graph Elements
For some background on PySimpleGUI see: PySimpleGUI – quick and easy interfaces
The graph element can have different co-ordinate orientations, for example the center can be (0,0) or the bottom left can be (0,0). A graph element is created with the syntax:
Graph(canvas_size, graph_bottom_left, graph_top_right …)
An example with 2 different co-ordinate orientations would be:
# A basic PySimpleGUI graph example import PySimpleGUI as sg bcols = ['blue','red','green'] myfont = "Ariel 18" gtop = sg.Graph((200,200), (0,0),(200,200),background_color="white") gcenter = sg.Graph((200,200), (-100,-100), (100,100),background_color="white") layout = [[sg.Text('Graph: 0,0 at bottom left',font=myfont)], [gtop], [sg.Text('Graph: 0,0 at center',font=myfont)], [gcenter], [sg.Exit()]] window = sg.Window('Graph Example', layout) # Write text and lines event, values = window.read(timeout=0) gtop.draw_text(text="(0,0)", location=(0,0)) gcenter.draw_text(text="(0,0)", location=(0,0)) gtop.draw_text(text="(50,50)", location=(50,50)) gcenter.draw_text(text="(50,50)", location=(50,50)) gtop.draw_line((0,0),(50,50)) gcenter.draw_line((0,0),(50,50)) # Wait for a key to exit window.read() window.close()
Bar Charts
Bar charts can be created using the graph.draw_rectangle() method. Below is an example that takes a command line input to toggle between a tkinter local interface and a web interface. This example has 3 input points that are scanned every 2 seconds.
import sys import random # Pass any command line argument for Web use if len(sys.argv) > 1: # if there is use the Web Interface import PySimpleGUIWeb as sg mode = "web" mysize = (20,2) else: # default uses the tkinter GUI import PySimpleGUI as sg mode = "tkinter" mysize = (12,1) BAR_WIDTH = 150 BAR_SPACING = 175 EDGE_OFFSET = 3 GRAPH_SIZE = (500,500) DATA_SIZE = (500,500) bcols = ['blue','red','green'] myfont = "Ariel 18" #update with your ip myip = '192.168.0.107' graph = sg.Graph(GRAPH_SIZE, (0,0), DATA_SIZE) layout = [[sg.Text('Pi Sensor Values',font=myfont)], [graph], [sg.Text('PI Tag 1',text_color=bcols[0],font=myfont,size= mysize ), sg.Text('PI Tag 2',text_color=bcols[1],font=myfont,size= mysize ), sg.Text('PI Tag 3',text_color=bcols[2],font=myfont,size= mysize)], [sg.Exit()]] if mode == "web": window = sg.Window('Real Time Charts', layout,web_ip=myip, web_port = 8888, web_start_browser=False) else: window = sg.Window('Real Time Charts', layout) while True: event, values = window.read(timeout=2000) if event in (None, 'Exit'): break graph.erase() for i in range(3): # Random value are used. Add interface to Pi sensors here: graph_value = random.randint(0, 400) graph.draw_rectangle(top_left=(i * BAR_SPACING + EDGE_OFFSET, graph_value), bottom_right=(i * BAR_SPACING + EDGE_OFFSET + BAR_WIDTH, 0), fill_color=bcols[i]) <span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>graph.draw_text(text=str(graph_value), location=(i*BAR_SPACING+EDGE_OFFSET+15, graph_value+10),color=bcols[i],font=myfont) window.close()
The presentation between the tkinter and Web interface is almost identical, but not 100% some tweeking on text sizing is required.
Real Time Trend Charts
It is important to note that the PySimpleGUIWeb is still in development so there may be some compatibility issues when trying to toggle between the tkinter and Web versions.
Below is an example that will create a realtime chart.
#!/usr/bin/env python import array from datetime import datetime import sys import random # Pass any command line argument for Web use if len(sys.argv) > 1: # if there is use the Web Interface import PySimpleGUIWeb as sg mode = "web" mysize = (20,2) else: # default uses the tkinter GUI import PySimpleGUI as sg mode = "tkinter" mysize = (12,1) from threading import Thread STEP_SIZE = 1 # can adjust for more data saved than shown SAMPLES = 100 # number of point shown on the chart SAMPLE_MAX = 100 # high limit of data points CANVAS_SIZE = (1000, 600) LABEL_SIZE = (1000,20) # create an array of time and data value pt_values = [] pt_times = [] for i in range(SAMPLES+1): pt_values.append("") pt_times.append("") def main(): timebar = sg.Graph(LABEL_SIZE, (0, 0),(SAMPLES, 20), background_color='white', key='times') graph = sg.Graph(CANVAS_SIZE, (0, 0), (SAMPLES, SAMPLE_MAX), background_color='black', key='graph') layout = [ [sg.Quit(button_color=('white', 'red')),sg.Button(button_text="Print Log", button_color=('white', 'green'),key="log"), sg.Text(' ', key='output')], [graph], [timebar], ] if mode == 'web': window = sg.Window('Pi Trend Chart', layout, web_ip='192.168.0.107', web_port = 8888, web_start_browser=False) else: window = sg.Window('Pi Trend Chart', layout) graph = window['graph'] output = window['output'] i = 0 prev_x, prev_y = 0, 0 while True: # the Event Loop event, values = window.read(timeout=1000) if event in ('Quit', None): # always give ths user a way out break if event in ('log'): # print the recorded time/data arrays print("\nReal Time Data\n") for j in range(SAMPLES+1): if pt_times[j] == "": #only print updated info break print (pt_times[j], pt_values[j]) # Get data point and time data_pt = random.randint(0, 100) now = datetime.now() now_time = now.strftime("%H:%M:%S") # update the point arrays pt_values[i] = data_pt pt_times[i] = str(now_time) window['output'].update(data_pt) if data_pt > SAMPLE_MAX: data_pt = SAMPLE_MAX new_x, new_y = i, data_pt if i >= SAMPLES: # shift graph over if full of data graph.move(-STEP_SIZE, 0) prev_x = prev_x - STEP_SIZE # shift the array data points for i in range(SAMPLES): pt_values[i] = pt_values[i+1] pt_times[i] = pt_times[i+1] graph.draw_line((prev_x, prev_y), (new_x, new_y), color='red') prev_x, prev_y = new_x, new_y i += STEP_SIZE if i < SAMPLES else 0 timebar.erase() # add a scrolling time value time_x = i timebar.draw_text(text=str(now_time), location=((time_x - 2),7) ) # add some extra times if i >= SAMPLES: timebar.draw_text(text=pt_times[int(SAMPLES * 0.25)], location=((int(time_x * 0.25) - 2),7) ) timebar.draw_text(text=pt_times[int(SAMPLES * 0.5)], location=((int(time_x * 0.5) - 2),7) ) timebar.draw_text(text=pt_times[int(SAMPLES * 0.75)], location=((int(time_x *0.75) - 2),7) ) if i > 10: timebar.draw_text(text=pt_times[1], location=( 2,7) ) window.close() if __name__ == '__main__': main()
Final Comment
If you are looking at doing some charting and you want to have both a local and a web interface then PySimpleGUI and PySimpleGUIWeb will be something that you should take a look at.