如何在Kivy中创建实时图表?

16

我想在Kivy中创建实时图表。 我该如何做到这一点? 我对Kivy不熟悉。 请帮助我。

5个回答

11

定义你的图表

例如:

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的运行图表。


8

kivy garden中有一个图形小部件。你可以阅读kivy的文档,了解如何使用garden小部件。


它支持直方图吗? - João Abrantes
2
我不这么认为。不过,我们今年或许会在Matplotlib支持方面有一个GSoC项目。 - inclement

4

我也在尝试在Kivy中制作实时图表。

Youscope

我从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的Youscope

绘图在kivy中似乎与原始代码一样快,但目前我卡在了重新绘制曲线上。但是我认为绘制应该更快,也许计算点太长时间了。我想我应该检查一个WAV文件作为输入,看看它是否更快。

Clock.schedule_interval(Kivy)vs. game loop(Pygame)

Kivy的源代码与pygame代码非常相似,但在Kivy中没有while循环的游戏循环。在Kivy中,您使用回调和Clock.schedule_interval(callback,time_in_sec)(请参见http://kivy.org/docs/api-kivy.clock.html)来更新/绘制屏幕。

使用framebuffer进行绘制

为了绘图,您需要使用添加到画布的帧缓冲区。请参见http://kivy.org/docs/api-kivy.graphics.fbo.html

曲线是逐点从左到右绘制的。重新绘制是指我在帧缓冲区上绘制第一条曲线(我正在使用计算出的正弦波),当我到达屏幕的右边缘时,我从左侧开始以新的曲线绘制。

现在仍然有之前绘制的曲线需要清除。您可以在此处重新绘制整个屏幕,但这可能比逐点删除旧线条要慢。

难点在于恢复旧曲线下面的背景颜色。看起来我获取了错误像素的颜色,但我不确定哪里出错了。

使用Get_pixel_color()刷新屏幕

使用Framebuffer.get_pixel_color(wx,wy)(需要Kivy 1.8.0)可以获取像素的rgba颜色,但这并没有正常工作。也许这是一个更新问题,但我不确定。

使用黑色像素进行清除(不使用get_pixel_color)可以工作,但这会删除背景网格。


0
这是我写的代码,需要一个趋势曲线。
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()

0
这是我对同样问题的解决方法。代码不是很干净,但这会让你了解如何在Kivy中使用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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接