I want to create a real time graph in kivy. How can i achieve that? I m new to kivy. Please help me.
5 Answers
define your plot
e.g.
plot = MeshLinePlot(color=next(colors))
define graph
e.g.
graph = Graph(
xlabel='Iteration',
ylabel='Value',
x_ticks_minor=1,
x_ticks_major=5,
y_ticks_major=1,
y_grid_label=True,
x_grid_label=True,
padding=5,
xlog=False,
ylog=False,
x_grid=True,
y_grid=True,
ymin=0,
ymax=11,
**graph_theme)
update graph and update x axis:
e.g.
def update_xaxis(self,*args):
global graph
global cnt
graph.xmin = cnt - 50
graph.xmax = cnt
def update_points(self, *args):
global i
global MYLIST
global cnt
#self.plot.points = [(i,i)]
self.plot.points = [z for z in MYLIST]
call a Clock
e.g.
Clock.schedule_interval(self.update_points, 1/60.)
Clock.schedule_interval(self.update_xaxis, 1/60.)
and add the widget:
b.add_widget(graph)
I hope I have not forgotten anything. It gives you running graph with kivy Garden.
There is a graph widget in the kivy garden. You can read about using garden widgets in kivy's documentation.

- 29,124
- 4
- 48
- 60
-
does it support histograms? – João Abrantes Apr 15 '15 at 12:06
-
2I don't think so. We may have a gsoc project in matplotlib support this year though. – inclement Apr 15 '15 at 12:34
I'm also trying to do a real time graph in Kivy.
Youscope
I started with Youscope. A demo of youscope you can see in the following youtube video https://www.youtube.com/watch?v=-1E0DpQ_cFo
And the source code is here: https://code.google.com/p/felipesanches/source/browse/trunk/youscope-emu/youscope-emu.py
It's written with Pygame and uses a wave audio file as input source, but you could also use other sources (e.g. serial data or a calculated curve).
The problem with Youscope is that I'm not able to build an APK for Android from it. I've tried to install the python subset for android but I get always error messages at building. (Not figured out what's wrong.)
So I decided to port the Youscope code to Kivy because with Buildozer I can make Android APKs. (Not tested to build the graphing app yet, but that should work.)
Youscope with Kivy
The drawing seems to run in kivy as fast as the original code but at the moment I'm stuck at redrawing the curve. But I think drawing should be faster maybe calculating the points is taking too long. I think I should check a WAV-file as input and if it's faster.
Clock.schedule_intervall (Kivy) vs. game loop (Pygame)
The source for Kivy is pretty simillar to the pygame code but in Kivy is no game loop with a while loop. In Kivy you're using callbacks with Clock.schedule_intervall(callback, time_in_sec) (see http://kivy.org/docs/api-kivy.clock.html) for updating/drawing the screen.
Use framebuffer for drawing
For drawing you need to use a framebuffer that is added to the canvas. see http://kivy.org/docs/api-kivy.graphics.fbo.html
The curve is drawn from left to right point by point. With redrawing I mean that I draw the first curve (I'm using a calculated sine wave) on the framebuffer and after I reached the right edge of the screen, I start to draw from the left again with the new curve.
And now there's still the previously drawn curve that needs to be cleared. You could redraw the whole screen here but that's probably slower than removing the old line point by point.
The difficulty here is to restore the background color that's underneath the old curve. It looks like I'm getting the color of the wrong pixel, but I'm not sure what's wrong.
Get_pixel_color() for refreshing the screen
With Framebuffer.get_pixel_color(wx,wy) (Kivy 1.8.0 required) you can get the color of a pixel in rgba and that's not working properly. Maybe it's an updating issue but I'm not sure.
Clearing with a black pixel (with-out get_pixel_color) is working but that removes the background grid.
Here's the code I wrote, needing a trend curve.
class TrendCurve(BoxLayout):
def __init__(self, **kwargs):
super(TrendCurve, self).__init__(**kwargs)
#self size and position
self.size = (1000, 500)
self.pos = (60,1)#((Window.width / 2) - ((self.size[0] / 2) - 80) , (Window.height / 2) - (self.size[1] / 2))
self.text = ""
self.number_labels = {}
#This is the point where the trend starts
self.point_zero = (self.pos[0] + 10, self.pos[1] + 10)
self.point_zero_x = self.pos[0] + 10
self.point_zero_y = self.pos[1] + 10
#Points for drawing the line around the rectangle
#"border line"
self.x1 = self.pos[0] - 50
self.y1 = self.pos[1]
self.x2 = self.pos[0] - 50
self.y2 = self.pos[1] + self.size[1]
self.x3 = self.pos[0] + self.size[0]
self.y3 = self.y2
self.x4 = self.x3
self.y4 = self.pos[1]
self.x5 = self.pos[0] - 50
self.y5 = self.y4
self.box_points = [self.x1, self.y1, self.x2, self.y2, self.x3, self.y3, self.x4, self.y4, self.x5, self.y5]
#Trend line
self.trend_points = []
#Trend starts at point zero
self.trend_points = [self.point_zero_x, self.point_zero_y]
#Variable for setting resolution of points and numbers
self.resolution = 10
#Lines for x and y on the trend.
self.xline_points = [self.pos[0] + 10, self.pos[1] + 10, self.pos[0] + 10, (self.pos[1] + self.size[1] - 10)]
self.yline_points = [self.pos[0] + 10, self.pos[1] + 10, (self.pos[0] + self.size[0] - 10), self.pos[1] + 10]
self.pointlinesx = {}
self.pointlinesy = {}
self.r = 0
self.g = 1
self.b = 0
#This is the resolution for how far forward we go for each update that comes.
self.x_update = 1
#This is to be rendered before
with self.canvas.before:
Color(0.4, 0.4, 0.4, 1)
self.rectangle = Rectangle(size=self.size, pos=self.pos)
self.left_addon_rectangle = Rectangle(size=(50, self.size[1]), pos=(self.pos[0] - 50, self.pos[1]))
#This is the main canvas
with self.canvas:
Color(0.2, 0.2, 0.2)
self.box = Line(points=self.box_points, width=1)
Color(1, 1, 1)
self.xline = Line(points=self.xline_points)
self.yline = Line(points=self.yline_points)
#These are the small lines for value_y, changing color as it goes upwards
#red gets more powerful and green gets less powerful
for i in range(0, self.size[1] - self.resolution, self.resolution):
if self.r < 1:
self.r += 0.03
if self.g > 0:
self.g -= 0.04
Color(self.r,self.g, 0)
if i >= 20:
self.pointlinesx[i] = Line(points=(self.point_zero_x - 3, self.point_zero_y + i, self.point_zero_x + 3, self.point_zero_y + i), width=0.8)
self.number_labels[i] = Label(size=(50, 20),font_size= 8, pos=(self.point_zero_x - 40, (self.point_zero_y + i) - 10), text=str(0 + i))
self.top_label = Label(text=self.text, size=(100, 50), pos=(self.center[0] - 50, self.center[1] + (self.size[1] / 2) - 50))
self.ms_label = Label(text="ms", size=(100,50), font_size= 11, pos=(self.point_zero_x - 90, self.point_zero_y + (self.size[1] / 2) - 25))
#These are the small lines for value_x, only white colored.
Color(1,1,1)
for i in range(0, self.size[0], 20):
if i >= 20:
self.pointlinesy[i] = Line(points=(self.point_zero_x + i, self.point_zero_y - 3, self.point_zero_x + i, self.point_zero_y + 3), width=0.8)
#This is to be rendered after
with self.canvas.after:
Color(0.3,0.6,1)
self.trend = Line(points=self.trend_points, width=0.8)
def add_points_test(self, dt):
new_num = randint(50, 200)
self.add_point(new_num)
def update(self):
self.trend.points = self.trend_points
def add_point(self, y):
try:
y = int(y)
except ValueError:
pass
if type(y) == int:
#The x is updated x pixels forth at a time
x = self.trend_points[len(self.trend_points) - 2] + self.x_update
self.trend_points.append(x)
#y must be between max and min
if y < 500 > 0:
self.trend_points.append(self.point_zero_y + y)
if y > 500:
self.trend_points.append(500)
if y < 0:
self.trend_points.append(0)
if x > (self.rectangle.size[0] - 10):
new_point_list = []
count = 0
for i in self.trend_points:
if (count % 2) != 1:
i -= self.x_update
new_point_list.append(i)
count += 1
del (new_point_list[0])
del (new_point_list[1])
new_point_list[0] = self.point_zero_x + 20
self.trend_points = new_point_list
self.update()

- 22,092
- 39
- 79
- 102
This is my workaround solution for the same. The code is not very clean but this will give you a gist of how to handle a real-time graphing in Kivy using Matplotlib.
app.py
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.floatlayout import FloatLayout
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import random
import threading
import time
from kivy.clock import Clock
x = [1,2,3,4,5]
y = [5,12,6,24,29]
def data():
data.a = None
data.fig = None
data()
plt.plot(x,y)
plt.ylabel("Y axis")
plt.xlabel("X axis")
def gen_rand_int(intput):
return random.randint(20,50)
data.fig = Figure(figsize=(5,4), dpi=100)
data.a = data.fig.add_subplot(111)
data.a.plot([1,2,3,4,5])
run_thread = True
def animate():
while run_thread:
data.a.clear()
n_list = list(map(gen_rand_int, [0]*5))
data.a.plot(n_list)
time.sleep(0.5)
print("animate")
calcThread = threading.Thread(target=animate)
calcThread.start()
class View(FloatLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.box = self.ids.box
canvas = FigureCanvasKivyAgg(data.fig)
self.box.add_widget(canvas)
Clock.schedule_interval(self.timer, 1)
def timer(self, dt):
canvas = FigureCanvasKivyAgg(data.fig)
self.box.clear_widgets()
self.box.add_widget(canvas)
print("timer")
def save_it(self):
print("button clicked")
class MainApp(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "BlueGray"
Builder.load_file('view.kv')
return View()
try:
MainApp().run()
except:
run_thread = False
print("Keyboard interrupt")
view.kv
<View>
BoxLayout:
id: box
size_hint_y: 0.8
pos_hint: {"top": 1}
BoxLayout:
size_hint_y: .2
TextInput:
id: namer
multiline: False
Button:
text: "Save It!!"
on_release: root.save_it()

- 111
- 1
- 3