Reputation: 123
All I'm trying to do is draw 12 circles, and keep them centered in the window.
I have a FloatLayout
which holds a BoxLayout
. The BoxLayout
should "hold" the 12 circles.
Since I want the circles in a row, the BoxLayout
's width should be 12 times it's height. The FloatLayout
resizes the BoxLayout
so this is always the case. (FloatLayout
is green, BoxLayout
is red).
The problem occurs when I resize the window small enough. Now the circles are not centered in the BoxLayout
. The circles also "jump" left to right as I resize the window (making it look totally unsmooth).
Also, if I maximize the window (instead of resizing by dragging an edge or corner of the window) then it draws the circles where ever.
Any clue whats happening here? I am using the BoxLayout
's lower left (x,y) coordinates to start drawing circles. The leftmost circle should use the same exact coordinates, but obviously it's off.
Instead of binding the canvas instructions to the on_size
event with self.bind(size=self.update_canvas, pos=self.update_canvas)
, I also tried calling self.redraw_note_markers
at the end of self.on_size
. That way, when we're drawing the circles, the inner BoxLayout
should have already been resized. But that is producing the same result as shown above.
UPDATE
I've incorporated this into a larger app, where it is part of a TabbedPanel
, and these circles are getting drawn totally out of place.
# 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
Upvotes: 0
Views: 1648
Reputation: 123
Answering my own question...
I was not able to get to the root of the problem. The FloatLayout
's on_size
event should 1) resize the BoxLayout
and 2) update the canvas based on the size/position change of the BoxLayout
. It does (1) just fine, but seems to lag with (2). The changes are not in sync. The result is graphics "jumping" around as the window is resized, and being just a little off. I still am unsure why it is behaving this way.
The work-around I'm using is to bind to the BoxLayout
's position, and update the canvas there.
# 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()
# filename: keysigdisplay.kv
<KeySigDisplay>:
box_pos: box.pos
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
Not sure if this is the right way to be doing this, but it works. Hopefully someone finds this helpful.
Upvotes: 1
Reputation: 38937
Try putting:
from kivy.config import Config
Config.set('graphics', 'maxfps', 0)
at the top of your keysigdisplay.py
. This will mean that kivy
will maximize its cpu usage and produce the smoothest display that it can. The default value for maxfps
is 60
(fps). If this improves your app's graphics performance, you can then try different values of maxfps
to balance between cpu usage and graphics smoothness.
Upvotes: 0