在kivy(python)中刷新图表时出现问题

问题描述 投票:0回答:1

我一直在尝试创建一种在应用程序中使用的仪表和针小部件。理想情况下,我会使用它在应用程序运行时持续显示某个变量的演变。

我已经成功地使用最新的 kivy 和 kivy-garden 以及 matplotlib 版本 3.5.0 使其在 Windows 中工作。我不确定之间的版本,但使用最新版本的 matplotlib 该应用程序将无法运行,从而引发FigureCanvasKivyAgg 错误。

编写的类有一个方法,可用于用另一个值更新针的位置。 “应用程序”有两个按钮。 “一”只是生成一个随机数并更新针的位置。 “循环”做同样的事情,但是循环一定次数。

我遇到的问题是循环功能不会连续改变针的位置。我可以看到它工作,因为值被打印到控制台上,但是程序在循环运行时冻结,只显示循环生成的最后一个值。

有什么办法可以解决这个问题吗?

这是代码。首先是小部件的类,然后是测试应用程序。

import kivy
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg

import matplotlib
matplotlib.use('module://kivy.garden.matplotlib.backend_kivy')
import matplotlib.pyplot as plt
from math import pi
matplotlib.use('svg')

####################################################################################################
###                                          Dial class                                          ###
####################################################################################################

class Dial(BoxLayout):
    def __init__(self, **kwargs):
        super(Dial, self).__init__(**kwargs)

        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(projection="polar")

        self.arco_total = pi
        self.segs = [
            0,
            pi*0.1,
            pi*0.2,
            pi*0.3,
            pi*0.4,
            pi*0.5,
            pi*0.6,
            pi*0.7,
            pi*0.8,
            pi*0.9,
            pi
            ] 

        self.labels=[1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0]

        self.ax.bar(
            x=self.segs[:-1],
            width=self.arco_total/(len(self.segs)-1),
            height=0.05,
            bottom=2,
            color="black",
            align="edge",
            linewidth=0.5,
            edgecolor="white")

        for loc, valor in zip(self.segs, self.labels):
            self.ax.annotate(valor, xy=(loc, 2.05), ha="right" if valor<0.5 else "left")

        self.value=0
        self.needle = self.ax.annotate(
            " ",
            xytext=(0,0),
            xy=((pi*(1-self.value)), 2.0),
            arrowprops=dict(
                arrowstyle="wedge",
                color="black",
                # mutation_scale=25,
                shrinkA=0
            ),
            bbox=dict(
                boxstyle="circle",
                facecolor="black",
                linewidth=1.0
            ),
            fontsize=15,
            color="black",
            ha="center"
        )

        self.ax.set_axis_off()
        self.ax.set_thetamin(0)
        self.ax.set_thetamax(180)
        self.ax.figure.tight_layout()

        self.graph = FigureCanvasKivyAgg(self.fig)

        self.add_widget(self.graph)

    
    def actualizar_valor(self, valor):
        self.needle.xy=[pi*(1-valor), 2.0]
        self.graph.draw()


####################################################################################################
###                                           Test app                                           ###
####################################################################################################

import kivy
from kivy.app import App

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
import random
import time as tm



class main(App):
    def __init__(self, **kwargs):
        super(main, self).__init__(**kwargs)

        self.dial = Dial()

        self.button1 = Button(
            text="One",
        )
        self.button1.bind(on_press = self.one)

        self.button2 = Button(
            text="Loop",
        )
        self.button2.bind(on_press = self.loop)

        #Distribución de los widgets
        self.layout_1 = BoxLayout(size_hint=(1, 0.8))
        self.layout_1.add_widget(self.dial)
        self.layout_2 = BoxLayout(orientation='horizontal', size_hint=(1, 0.2))
        self.layout_2.add_widget(self.button1)
        self.layout_2.add_widget(self.button2)
        self.layout = BoxLayout(orientation='vertical')
        self.layout.add_widget(self.layout_1)
        self.layout.add_widget(self.layout_2)

    def build(self):
        return self.layout
    
    def one(self, evento):
        # for i in range(5):
        val = random.uniform(0,1)
        # print(val)
        self.dial.actualizar_valor(val)
        # tm.sleep(1.0)
    

    def loop(self, evento):
        for i in range(5):
            val = random.uniform(0,1)
            print(val)
            self.dial.actualizar_valor(val)
            tm.sleep(0.5)
    

if __name__ == "__main__":
    main().run()

欢迎任何帮助/建议。谢谢

我在 tkinter 上遇到了类似的问题,我设法通过将“更新函数”放在单独的线程上来解决它。但我无法让它与 kivy 一起工作。

python-3.x matplotlib kivy
1个回答
0
投票

在代码顶部导入此对象。

from kivy.clock import mainthread

用此代码替换循环函数。

@mainthread
def update_dial(self, the_value):
    self.dial.actualizar_valor(the_value)

def run_thread(self):
    for i in range(5):
        val = random.uniform(0, 1)
        print(val)
        self.update_dial(val)
        tm.sleep(0.5)

def loop(self, evento):
    print(evento)
    threading.Thread(target=self.run_thread, daemon=True).start()

你需要释放主线程来运行 Kivy 的东西。抱歉,我没有比这更好的解释,我只知道修改使针移动并在按下按钮后直接释放 GUI。

需要 @mainthread 装饰器,因为 Kivy 必须在主线程中进行图形更新。

© www.soinside.com 2019 - 2024. All rights reserved.