The good news is that you had the kivy referencing correct – this was more of a python issue.
def blink_on_off(self):
anim = self.ids.spot.anim
if self.ids.blsw.active:
anim.repeat = False
else:
anim.repeat = True
anim.start(self.ids.spot)
class BlinkingLabel(Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.anim = Animation(color=[1, 1, 0, 0], duration=0.5) + \
Animation(color=[1, 1, 0, 1], duration=0.5)
self.anim.repeat = True
self.anim.start(self)
In the __init__() of BlinkingLabel, you had declared anim, not self.anim. Without the “self.” anim is a local variable. self.anim is an instance variable. For your use case you needed to declare self.anim.
I also made some minor changes to blink_on_off()
--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/63a94833-d953-4f4a-8f87-990c4710869cn%40googlegroups.com.
Glad to hear it is all working. If you decide to move the biink_on_off() method to BlinkingLabel the code needs to be changed to work in that spot. I suggest moving it because it is more naturally belongs in that class. You could call it with the state of the switch, and then you would access anim as self.amim and the widget as self. But it’s your code…
Yes, can create a new class to create a DragBlinkingLabel…. derive DragBlinkingLabel from just DragLabel and use the existing __init__(). The new class will inherit both the Label attributes and the DragBehaviors.
I don’t think you can combine the DragBehavior with pos_hint. The pos_hint will prevent you from dragging the widget. Instead set the initial position using pos. You can use the widget data to write an expression to set the widgets initial position, but the pos will be updated by the drag.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/3acd439e-079a-4203-85aa-15d3f08d3cfbn%40googlegroups.com.
Here is an example… I tried using a pos_hint with the DragBlinkLabel and as I thought, the label will not drag… Note how pos was used to center the DragBlinkLabel in the center of the Layout.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.behaviors import DragBehavior
from kivy.animation import Animation
kv = """
<DragLabel>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
<DragBlinkLabel>:
text: 'drag me'
size_hint: None, None
size: self.texture_size
RelativeLayout:
DragBlinkLabel:
pos: self.parent.center_x - self.width / 2, self.parent.center_y
id: drag_blink_label
Switch:
size_hint: None, None
size: dp(100), dp(48)
pos_hint: {'center_x': 0.5, 'y': 0}
active: True # set initial state
on_active: drag_blink_label.blink_on_off(self.active)
"""
class DragLabel(DragBehavior, Label):
pass
class DragBlinkLabel(DragLabel):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.anim = (Animation(color=[1, 1, 0, 0], duration=0.5) +
Animation(color=[1, 1, 0, 1], duration=0.5))
self.anim.repeat = True
self.anim.start(self)
def blink_on_off(self, active):
if not active:
self.anim.repeat = False
else:
self.anim.repeat = True
self.anim.start(self)
class DragBlinkApp(App):
def build(self):
return Builder.load_string(kv)
DragBlinkApp().run()
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/003c01da22cb%246db49e70%24491ddb50%24%40cox.net.
In tutorial.kv
Switch: #switch for blink on/off
id: blsw
name: 'blsw'
text: 'No'
active: True # set to True to match initial state of blinking
size_hint: .14,.05
pos_hint: {'x':.735, 'y':.605}
on_active: spot.blink_on_off(self.active)
BlinkingLabel: #spot for position
id: spot
name: "spot"
text: '*'
font_size: 30
color: 1,1,0,1
background_color: 255,255,0,1
size_hint: None,None
size: 1,1
pos_hint: {'x':.2, 'y':.25}
And in python, added the blink_on_off from my example to the BlnkingLabel class.
class BlinkingLabel(Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.anim = Animation(color=[1, 1, 0, 0], duration=0.5) + \
Animation(color=[1, 1, 0, 1], duration=0.5)
self.anim.repeat = True
self.anim.start(self
)
def blink_on_off(self, active):
if not active:
self.anim.repeat = False
else:
self.anim.repeat = True
self.anim.start(self)
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/d65303c9-be11-43b4-8f87-c456febe6e35n%40googlegroups.com.
In my example the RelativeLayout is the root widget. That is why it is on the far left column of the file.
As the error states, “only one root object is allowed by kv.”
My example was just a simple example with a drag area and a switch. You will need to work through how you want to integrate the elements.
Share you main.py and kv file if you get stuck.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/188cb893-102a-4021-bd79-f464064b9d15n%40googlegroups.com.
Here is the edit to kv:
DragBlinkLabel: #spot for position
id: spot
name: "spot"
text: '*'
font_size: 30
color: 1,1,0,1
background_color: 255,255,0,1
size_hint: None,None
size: self.texture_size
pos_hint: {'x':.2, 'y':.25}
# pos: root.right * .2, root.height * .25
And python:
class DragBlinkLabel(DragLabel):
There is an issue with the code where when dragging the spot, the mouse and the spot get farther away from each other. I suspect the issue is 2 things are being changed at once. Moving the spot changes the slider, and moving the slider changes the spot.
You will need to know if the spot is moving from a touch, and not change it with the slider when the spot is touched. You can add ButtonBehavior to the Label and use the on_press and on_release to create a touched property for the DragBlinkLabel and only have the slider change the pos of the spot, whet the spot is not touched.,
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/24c6eb6a-e34d-455d-bde2-3fce746d9b32n%40googlegroups.com.
I also though it was a feedback loop – but I know it was because the pixel density is not 1, so there needs to be an adjustment made relative to the pixel density.
The code below was going to be an example of how to use the touched property I created to break the feedback loop – but as I said that was not the issue. FWIW ButtonBehavior did not seem to work with ButtonBehavior. This is odd because I know I have dragged buttons in other apps….
Look at the highlighted code below:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.behaviors import DragBehavior
from kivy.animation import Animation
from kivy.properties import BooleanProperty
kv = """
<DragLabel>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
<DragBlinkLabel>:
text: '*'
size_hint: None, None
size: self.texture_size
BoxLayout:
orientation: 'vertical'
AnchorLayout:
FloatLayout:
id: fl
size_hint: None, None
size: dp(400), dp(400)
canvas:
Line:
rectangle: (*self.pos, *self.size)
DragBlinkLabel:
pos: self.parent.center
id: spot
on_y:
y_slider.value = (self.y - fl.y) / dp(1)
Slider:
id: y_slider
size_hint_x: None
width: dp(48)
orientation: 'vertical'
pos: fl.pos
max: 400
value: 200
on_value:
spot.y = fl.y + dp(self.value)
Switch:
size_hint: None, None
size: dp(100), dp(48)
pos_hint: {'center_x': 0.5, 'y': 0}
active: True # set initial state
on_active: spot.blink_on_off(self.active)
"""
class DragLabel(DragBehavior, Label):
pass
class
DragBlinkLabel(DragLabel):
touched = BooleanProperty(False)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.anim = (Animation(color=[1, 1, 0, 0], duration=0.5) +
Animation(color=[1, 1, 0, 1], duration=0.5))
self.anim.repeat = True
self.anim.start(self)
def blink_on_off(self, active):
if not active:
self.anim.repeat = False
else:
self.anim.repeat = True
self.anim.start(self)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.touched = True
return super().on_touch_down(touch)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
self.touched = False
return super().on_touch_up(touch)
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/84d8770e-60a8-47f9-8009-fb81004d1275n%40googlegroups.com.
Why does the on_y fire twice before anything?
The default position of a widget is (0,0). I’m guessing that as the layouts position the widget to the initial position in the center it is happening across a few clock ticks, causing multiple events that end with the widget in the correct position.
Why does spot go from 50 to 324 - shouldn't it be 200?
The position of spot is relative the bottom of the enclosing layout. We need to add the distance from the bottom of the window to the bottom of the rectangle to get the zero position of the slider.
I have no idea what fl.y is. I can see it's the id for FloatLayout. It starts out at -150 then stays at 124 forever. Can layouts have a position?
Yes, Layouts are widgets. They have all the attributes of a Widget. The have pos, size… fl.y is the y value of the FloatLayout position, or in this case the bottom of the rectangle.
I can see that 124 is the difference between spot.y and y_slider.value, but why?
That must be the difference between the bottom of the window and the size of the rectangle (the rectangle surrounds the FloatLayout). The FloatLayout has a fixed size and is positioned in the center of the Window by the AnchorLayout. If the slider went from the top to the bottom of the window this offset would not be necessary.
When I touch a point on the slider it fires the on_value then on_y then on_value twice.
This is the “feecback loop” The spot changes the slider, the slider changes the spot. You can use the touched property to fix this:
DragBlinkLabel:
pos: self.parent.center
id: spot
on_y:
if self.touched: y_slider.value = (spot.y - fl.y) / dp(1)
print('on_y ',
'spot.y', int(spot.y),
'fl.y', int(fl.y),
'dp(1)', int(dp(1)),
'y_slider.value',int(y_slider.value))
Slider:
id: y_slider
size_hint_x: None
width: dp(48)
orientation: 'vertical'
pos: fl.pos
min: 0
max: 400
value: 200
on_value:
if not spot.touched: spot.y = fl.y + dp(y_slider.value)
print('on_value',
'spot.y', int(spot.y),
'fl.y', int(fl.y),
'dp(1)', int(dp(1)),
'y_slider.value',int(y_slider.value))
For example: If I touch a point at the bottom of slider (200) the y_slider value comes out to 0 but the spot.y goes to 124.
The bottom of the slider is not in the bottom of the Window. The spot.y is showing the Window coordinate. You could change the FloatLayout to a RelativeLayout to having the spot position relative to the Layout rather than the window… this brings on all sorts of interesting complications. Read: https://kivy.org/doc/stable/api-kivy.uix.relativelayout.html#coordinate-systems and the section below it titled Common Pitfalls.
Hope that helps!
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/aaf5b31f-84a9-4b89-900f-7f296446108en%40googlegroups.com.
You have a race condition…
Pos is changing both x and y.
The one of the sliders change, this causes the position to be updated, changing the position of the object, but using the old data from one of the sliders.
A bit of a hack… I changed x and y separately. It would be better to more explicitly remove this race condition by having each slider only update the x or y attribute of spot.
Here is my change:
def ask_it(self):
println('def xask_it',self.ids.spot.pos)
x=self.ids.txt2.text
y=self.ids.txt3.text
println(x,y)
if x.isdigit() and y.isdigit():
x=int(x); y=int(y)
println(x,y)
self.ids.spot.x = x
self.ids.spot.y = y
println(self.ids.spot.pos)
self.ids.txt2.text=''
self.ids.txt3.text=''
self.ids.txt2.focus=True
I would recommend you change each of the sliders so they change only the parameter they are effecting, spot.x or spot.y
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/b3dc86f6-20f6-49e9-a6f8-3cf13c9b5aabn%40googlegroups.com.
After rereading, I thought I would try again:
In ask_it(), you are changing spot.pos, changing both x and y. When one of these values is changing, lets say x. It changes the value of the x slider. This causes the x slider to fire on_value. This updates the x and y position, using the old data from the y slider in the call to slider_move() – it has not been updated yet. Overwriting the new data with the old data. When you enter the data a second time, only the changed data (Y) fires an on_value event, so things work correctly.
I would recommend you change each of the sliders, so they change only the parameter they are affecting, spot.x or spot.y. You could for example update slider_move to pass in a character ‘x’ or ‘y’ and a value, so you only change the parameter the slider is effecting.
Let me know if that makes sense.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/007601da2625%249780ff70%24c682fe50%24%40cox.net.
It is simple enough to just make the update to kv:
Slider: #vertical slider
id: sliv
name: 'sliv'
orientation: 'vertical'
min: 0
max: 600
step: 1
size_hint: None,None
size: 10,170
pos: 27,102
value: int(spot.pos[1])
on_value:
slav.text = str(int(self.value))
if not spot.touched: spot.y = int(self.value)
And make a similar change the slih slider.
From: elli...@cox.net <elli...@cox.net>
Sent: Sunday, December 3, 2023 1:33 PM
To: 'kivy-...@googlegroups.com' <kivy-...@googlegroups.com>
Subject: RE: [kivy-users] Turn on/off blinking label
After rereading, I thought I would try again:
In ask_it(), you are changing spot.pos, changing both x and y. When one of these values is changing, lets say x. It changes the value of the x slider. This causes the x slider to fire on_value. This updates the x and y position, using the old data from the y slider in the call to slider_move() – it has not been updated yet. Overwriting the new data with the old data. When you enter the data a second time, only the changed data (Y) fires an on_value event, so things work correctly.
I would recommend you change each of the sliders, so they change only the parameter they are affecting, spot.x or spot.y. You could for example update slider_move to pass in a character ‘x’ or ‘y’ and a value, so you only change the parameter the slider is effecting.
Let me know if that makes sense.
From: kivy-...@googlegroups.com <kivy-...@googlegroups.com> On Behalf Of elli...@cox.net
Sent: Sunday, December 3, 2023 1:16 PM
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/007601da2625%249780ff70%24c682fe50%24%40cox.net.