在 Kivy 中程序 运行 时如何重绘 canvas?

how to redraw canvas while the program is running In Kivy?

我希望程序根据用户输入的两个值的比率重画线

我试过这个算法: 我有两个变量 pulse_lengthdistance_length。 these two points的坐标依比例=(distance_length/(distance_length+pulse_length)*540)+40

当我调用 TextInput 对象的 on_text_validate 事件时,我将输入的值写入变量并重绘 canvas。重绘canvas我用了self.canvas.ask_update(),但是不行

这里是简化的程序代码:


# input variables
pulse_length = 499
distance_length = 999

# calculations
ratio = (distance_length / (distance_length + pulse_length) * 540) + 40

class Line(Widget):
    def __init__(self, **kwargs):
        super(Line, self).__init__(**kwargs)
        with self.canvas:
            kg.Line(
                points=(
                    20, 350,
                    40, 350,
                    40, 240,
                    ratio, 240,
                    ratio, 350,
                    560, 350,
                    560, 240,
                    620, 240),

            # Other instructions ...

            Callback(self.update)
    def update(self, inst):
        ratio = (distance_length / (distance_length + pulse_length) * 540) + 40
        self.canvas.ask_update()

class TextInputGrid(Widget):
    def __init__(self, **kwargs):
        super(TextInputGrid, self).__init__(**kwargs)

        self.inputlayout = GridLayout(cols=1, size=(100, 30))
        self.inputlayout.textfield = TextInput(multiline=False)
        self.inputlayout.add_widget(self.inputlayout.textfield)

class MyFloatLayout(FloatLayout):
    def __init__(self, **kwargs):
        super(FloatLayout, self).__init__(**kwargs)

        # Drawn line widget
        self.add_widget(Line())

        # TextInput 1
        self.textinput1 = TextInputGrid()
        self.textinput1.inputlayout.textfield.bind(on_text_validate=self.enter_pulse)

        # TextInput 2
        self.textinput2 = TextInputGrid()
        self.textinput2.inputlayout.textfield.bind(on_text_validate=self.enter_distance)

        self.add_widget(self.textinput1.inputlayout)
        self.add_widget(self.textinput2.inputlayout)

    def enter_pulse(self, instance):
        global pulse_length
        pulse_length = int(self.textinput1.inputlayout.textfield.text)

    def enter_distance(self, instance):
        global distance_length
        distance_length = int(self.textinput2.inputlayout.textfield.text)


class GeneratorApp(App):
    def build(self):
        return MyFloatLayout()


if __name__ == '__main__':
    GeneratorApp().run()

也许我不知道 ask_update() 是如何工作的?如果是,请解释如何重绘 canvas。或者如果算法不正确,请告诉我如何实现这样的程序。

我发现 kgKivy.graphics

当您在 Canvas 指令中使用 ratio 时,它使用 ratio 的当前值,并且不记得该值来自何处。因此,更改 ratio 的当前值不会更改 Line。为了更新 Line,您需要重新绘制它。这是对您的代码的修改,它删除了旧的 Line 并绘制了一个新版本:

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
import kivy.graphics as kg

# input variables
pulse_length = 499
distance_length = 999

class Line(Widget):
    def __init__(self, **kwargs):
        ratio = (distance_length / (distance_length + pulse_length) * 540) + 40
        super(Line, self).__init__(**kwargs)
        with self.canvas:
            self.line = kg.Line(
                points=(
                    20, 350,
                    40, 350,
                    40, 240,
                    ratio, 240,
                    ratio, 350,
                    560, 350,
                    560, 240,
                    620, 240))

            # Other instructions ...

            #Callback(self.update)
    def update(self):
        ratio = (distance_length / (distance_length + pulse_length) * 540) + 40
        self.canvas.remove(self.line)
        with self.canvas:
            self.line = kg.Line(
                points=(
                    20, 350,
                    40, 350,
                    40, 240,
                    ratio, 240,
                    ratio, 350,
                    560, 350,
                    560, 240,
                    620, 240))

class TextInputGrid(Widget):
    def __init__(self, **kwargs):
        super(TextInputGrid, self).__init__(**kwargs)

        self.inputlayout = GridLayout(cols=1, size=(100, 30))
        self.inputlayout.textfield = TextInput(multiline=False)
        self.inputlayout.add_widget(self.inputlayout.textfield)

class MyFloatLayout(FloatLayout):
    def __init__(self, **kwargs):
        super(FloatLayout, self).__init__(**kwargs)

        # Drawn line widget
        self.line = Line()
        self.add_widget(self.line)

        # TextInput 1
        self.textinput1 = TextInputGrid(pos=(0,0))
        self.textinput1.inputlayout.textfield.bind(on_text_validate=self.enter_pulse)

        # TextInput 2
        self.textinput2 = TextInputGrid(pos=(100,100))
        self.textinput2.inputlayout.textfield.bind(on_text_validate=self.enter_distance)

        self.add_widget(self.textinput1.inputlayout)
        self.add_widget(self.textinput2.inputlayout)

    def enter_pulse(self, instance):
        global pulse_length
        pulse_length = int(self.textinput1.inputlayout.textfield.text)
        self.line.update()

    def enter_distance(self, instance):
        global distance_length
        distance_length = int(self.textinput2.inputlayout.textfield.text)
        self.line.update()


class GeneratorApp(App):
    def build(self):
        return MyFloatLayout()


if __name__ == '__main__':
    GeneratorApp().run()