我一直在尝试创建一种在应用程序中使用的仪表和针小部件。理想情况下,我会使用它在应用程序运行时持续显示某个变量的演变。
我已经成功地使用最新的 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 一起工作。
在代码顶部导入此对象。
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 必须在主线程中进行图形更新。