Kivy-使用on_size事件更新Canvas无法正常工作

时间:2019-11-13 15:55:56

标签: python kivy kivy-language

我要做的就是绘制12个圆,并将其在窗口中居中。

我有一个FloatLayout,其中有一个BoxLayoutBoxLayout应该“保持” 12个圆圈。

由于我要连续排列圆圈,因此BoxLayout的宽度应为高度的12倍。 FloatLayout会调整BoxLayout的大小,因此总是这样。 (FloatLayout为绿色,BoxLayout为红色)。

correctly_drawn

当我将窗口调整为足够小的尺寸时,会出现问题。现在,圆圈不在BoxLayout中居中。当我调整窗口大小(使其看起来完全不光滑)时,圆圈还会从左向右“跳跃”。

incorrectly_drawn1

此外,如果我最大化窗口(而不是通过拖动窗口的边缘或角落来调整大小),那么它会在任何地方绘制圆。

enter image description here

这里有什么线索吗?我正在使用BoxLayout的左下(x,y)坐标开始绘制圆。最左边的圆圈应使用相同的精确坐标,但显然已关闭。

我没有尝试将画布指令与on_size绑定到self.bind(size=self.update_canvas, pos=self.update_canvas)事件,而是尝试在self.redraw_note_markers的末尾调用self.on_size。这样,当我们绘制圆圈时,内部BoxLayout应该已经被调整大小。但这产生了与上述相同的结果。

更新 我已经将其合并到一个更大的应用程序中,该应用程序是TabbedPanel的一部分,这些圆圈已完全被替换掉了。

# filename: keysigdisplay.py
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import InstructionGroup, Ellipse, Color
from kivy.clock import Clock


class KeySigDisplay(FloatLayout):

    def __init__(self, **kwargs):
        # To get note_markers drawn in center of self.ids.box, need super() at top
        # of __init__ and then Clock.schedule_once(self.redraw_note_markers)...?
        super().__init__(**kwargs)
        self.note_markers = InstructionGroup()
        self.bind(size=self.update_canvas, pos=self.update_canvas)
        Clock.schedule_once(self.update_canvas)

    def update_canvas(self, *args):
        self.redraw_note_markers()

    def redraw_note_markers(self, *args):
        self.canvas.remove(self.note_markers)
        self.note_markers.clear()
        x, y = self.ids.box.pos
        for i in range(12):
            self.redraw_note_marker(i, x, y)
        self.canvas.add(self.note_markers)

    def redraw_note_marker(self, i, x, y):
        white = Color(1, 1, 1, 1)  # white
        blue = Color(68 / 255, 93 / 255, 209 / 255, 1)  # blue
        black = Color(0, 0, 0, 1)

        # Draw 2 concentric circles, c1 and c2.
        # Circles are defined by a square's lower left corner.
        r1 = self.ids.box.height / 2
        r2 = r1 * 0.9
        rdiff = r1 - r2
        c1x, c1y = (2*r1)*i + x, y
        c2x, c2y = c1x + rdiff, c1y + rdiff

        self.note_markers.add(white)
        self.note_markers.add(Ellipse(pos=[c1x, c1y], size=[2 * r1, 2 * r1]))
        self.note_markers.add(blue)
        self.note_markers.add(Ellipse(pos=[c2x, c2y], size=[2 * r2, 2 * r2]))

    def on_size(self, instance, value):
        width, height = self.size
        target_ratio = 12
        if width / height > target_ratio:
            self.ids.box.height = height
            self.ids.box.width = height * target_ratio
        else:
            self.ids.box.width = width
            self.ids.box.height = width / target_ratio


class KeySigDisplayApp(App):
    def build(self):
        return KeySigDisplay()


if __name__ == "__main__":
    KeySigDisplayApp().run()
# filename: keysigdisplay.kv
<KeySigDisplay>:
    canvas:
        Color:
            rgba: [0, 1, 0, 0.25]
        Rectangle:
            size: self.size
            pos: self.pos
    BoxLayout:
        id: box
        size_hint: [None, None]
        pos_hint: {"center_x": 0.5, "center_y": 0.5}
        canvas:
            Color:
                rgba: [1, 0, 0, 0.25]
            Rectangle:
                size: self.size
                pos: self.pos

2 个答案:

答案 0 :(得分:0)

尝试放置:

from kivy.config import Config
Config.set('graphics', 'maxfps', 0)
在您的keysigdisplay.py顶部的

。这将意味着kivy将最大限度地提高其cpu使用率,并产生最平滑的显示效果。 maxfps的默认值为60(fps)。如果这可以提高应用程序的图形性能,则可以尝试使用不同的maxfps值来平衡CPU使用率和图形平滑度。

答案 1 :(得分:0)

正在回答我自己的问题...

我无法找到问题的根源。 FloatLayout的{​​{1}}事件应1)调整on_size的大小,以及2)根据BoxLayout的大小/位置更改来更新画布。它确实(1)很好,但似乎落后于(2)。更改不同步。结果是,在调整窗口大小时,图形会“跳来跳去”,并且只有一点点偏离。我仍然不确定为什么会这样。

我正在使用的解决方法是绑定到BoxLayout的位置,并在那里更新画布。

BoxLayout
# filename: keysigdisplay.py
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import InstructionGroup, Ellipse, Color
from kivy.properties import NumericProperty, ReferenceListProperty


class KeySigDisplay(FloatLayout):
    # Added all these properties. box_pos is bound to box.pos in kv file.
    box_x = NumericProperty(0)
    box_y = NumericProperty(0)
    box_pos = ReferenceListProperty(box_x, box_y)  

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.note_markers = InstructionGroup()

    def update_canvas(self, *args):
        self.redraw_note_markers()

    def redraw_note_markers(self, *args):
        self.canvas.remove(self.note_markers)
        self.note_markers.clear()
        x, y = self.ids.box.pos
        for i in range(12):
            self.redraw_note_marker(i, x, y)
        self.canvas.add(self.note_markers)

    def redraw_note_marker(self, i, x, y):
        white = Color(1, 1, 1, 1)  # white
        blue = Color(68 / 255, 93 / 255, 209 / 255, 1)  # blue
        black = Color(0, 0, 0, 1)

        # Draw 2 concentric circles, c1 and c2.
        # Circles are defined by a square's lower left corner.
        r1 = self.ids.box.height / 2
        r2 = r1 * 0.9
        rdiff = r1 - r2
        c1x, c1y = (2*r1)*i + x, y
        c2x, c2y = c1x + rdiff, c1y + rdiff

        self.note_markers.add(white)
        self.note_markers.add(Ellipse(pos=[c1x, c1y], size=[2 * r1, 2 * r1]))
        self.note_markers.add(blue)
        self.note_markers.add(Ellipse(pos=[c2x, c2y], size=[2 * r2, 2 * r2]))

    def on_size(self, instance, value):
        width, height = self.size
        target_ratio = 12
        if width / height > target_ratio:
            self.ids.box.height = height
            self.ids.box.width = height * target_ratio
        else:
            self.ids.box.width = width
            self.ids.box.height = width / target_ratio

    # Added this method to receive the event.
    def on_box_pos(self, instance, value):
        self.update_canvas(instance, value)

class KeySigDisplayApp(App):
    def build(self):
        return KeySigDisplay()


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

不确定这样做是否正确,但是可以。希望有人觉得这有帮助。