我想在Kivy中创建实时图表。 我该如何做到这一点? 我对Kivy不熟悉。 请帮助我。
定义你的图表
例如:
plot = MeshLinePlot(color=next(colors))
定义图形
例如:
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)
更新图表和更新X轴:
例如:
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]
调用一个时钟
例如:
Clock.schedule_interval(self.update_points, 1/60.)
Clock.schedule_interval(self.update_xaxis, 1/60.)
并添加小部件:
b.add_widget(graph)
我希望我没有忘记任何东西。它可以为您提供使用Kivy Garden的运行图表。
在kivy garden中有一个图形小部件。你可以阅读kivy的文档,了解如何使用garden小部件。
我也在尝试在Kivy中制作实时图表。
我从Youscope开始。您可以在以下YouTube视频中查看youscope的演示 https://www.youtube.com/watch?v=-1E0DpQ_cFo
源代码在这里:https://code.google.com/p/felipesanches/source/browse/trunk/youscope-emu/youscope-emu.py
它是使用Pygame编写的,并使用波音频文件作为输入源,但您也可以使用其他源(例如串行数据或计算曲线)。
Youscope的问题是,我无法从中构建Android APK。我尝试安装了Python适用于Android的子集,但在构建过程中总是出现错误消息。(还没有找出问题所在。)
所以我决定将Youscope代码移植到Kivy,因为使用Buildozer我可以制作Android APKs。(还没有测试构建绘图应用程序,但应该可以。)绘图在kivy中似乎与原始代码一样快,但目前我卡在了重新绘制曲线上。但是我认为绘制应该更快,也许计算点太长时间了。我想我应该检查一个WAV文件作为输入,看看它是否更快。
Kivy的源代码与pygame代码非常相似,但在Kivy中没有while循环的游戏循环。在Kivy中,您使用回调和Clock.schedule_interval(callback,time_in_sec)(请参见http://kivy.org/docs/api-kivy.clock.html)来更新/绘制屏幕。
为了绘图,您需要使用添加到画布的帧缓冲区。请参见http://kivy.org/docs/api-kivy.graphics.fbo.html。
曲线是逐点从左到右绘制的。重新绘制是指我在帧缓冲区上绘制第一条曲线(我正在使用计算出的正弦波),当我到达屏幕的右边缘时,我从左侧开始以新的曲线绘制。
现在仍然有之前绘制的曲线需要清除。您可以在此处重新绘制整个屏幕,但这可能比逐点删除旧线条要慢。
难点在于恢复旧曲线下面的背景颜色。看起来我获取了错误像素的颜色,但我不确定哪里出错了。
使用Framebuffer.get_pixel_color(wx,wy)(需要Kivy 1.8.0)可以获取像素的rgba颜色,但这并没有正常工作。也许这是一个更新问题,但我不确定。
使用黑色像素进行清除(不使用get_pixel_color)可以工作,但这会删除背景网格。
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()
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()