Kivy Access Touch Object - kivy

I'm new to kivy and I want to access the touch Object without overwriting motion event functions like on_touch_down(self,touch), on_touch_up(), etc..
I want to access touch.pos in order to dynamically draw lines on an Image in a Scatter Class.
Simplified my kv file looks like the following:
<Map#Scatter>:
source: None
do_rotation: False
Image:
id: main_image
source: root.parent.parent.parent.parent.map_path
allow_stretch: True
keep_ratio: True
size_hint_y: None
size_hint_x: None
width: self.parent.width
height: self.parent.width/self.image_ratio
<Zoom>:
id: zoom_class
Map:
id: map_class
<RoomMarkerScreen>:
id: room_marker_screen
AnchorLayout:
rows: 2
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
Zoom:
id: zoom_class
AnchorLayout:
anchor_x:'center'
anchor_y: 'bottom'
Button:
text: "Fix/Unfix Map"
size_hint: 0.5, 0.05
on_press:
root.fix_unfix(root.fix)
Knowing that Class Zoom Inherits from FloatLayout, how can I access Touch Object in Zoom?
Thanks in advance!
If I take the touch.pos from the event motion functions, they will be overwritten and I won't be able use the drag and scale features that came with the Scatter Class.

Whenever you write a function that exists in a super class, you should call that that super class method, otherwise, you will be interfering in the functioning of that super class (for example, DragBehavior). So, I suggest trying something like:
class Zoom(FloatLayout):
fixed_map = BooleanProperty(False)
def on_touch_down(self, touch):
print(touch.pos)
return super(Zoom, self).on_touch_down(touch)
And returning True from a on_touch_down() method stops all further dispatching of that touch event, so other widgets do not see that event. See the documentation.
Also, from the same documentation:
on_touch_down(), on_touch_move(), on_touch_up() don’t do any sort of
collisions. If you want to know if the touch is inside your widget,
use collide_point().

Related

Kivy Button macro w get screen instructions

looking to set up a reusable button in my kivy file that includes various root.manager.get_screen instructions under on_press ... Anyone know if this is possible to do in the kv? Thanks so much for any help in advance
<ProductButton#Button>
color: 'black'
outline_color: 'white'
outline_width: 15
on_press:
root.manager.transition.direction = "left"
root.manager.transition.duration = 1
root.manager.current = (root.ids.product1.text+root.ids.label.text+'Detail')
root.manager.get_screen(root.manager.MY_GLOBAL).ids.selection.background_normal = self.background_normal
etc.
The code works in the individual screens but it is long and ugly so I would like to set up a macro instead...
Here is the error I get -
AttributeError: 'ProductButton' object has no attribute 'manager'

Understanding KivyMD Screen Layouts

I want to understand how to layout a KivyMD screen using a GridLayout with ScrollView, and with a BottomNavigation. My code doesn't work because the GridLayout displays over the BottonNavigation which then can't be seen. What is the correct way to setup this layout? And, generally are there rule-of-thumbs for KivyMD screen layouts? Thanks
<MyScreen>:
name: 'myscreen'
ScrollView:
orientation:'vertical'
MDGridLayout:
cols: 3
adaptive_height: True
padding: dp(4), dp(4)
spacing: dp(4)
etc.
BoxLayout:
orientation:'vertical'
MDBottomNavigation:
panel_color: .2, .2, .2, 1
MDBottomNavigationItem:
name: 'left'
text: 'Left'
icon: 'chevron-left'
MDLabel:
text: 'Left'
halign: 'center'
font_style: 'Icon'
MDBottomNavigationItem:
name: 'right'
text: 'Right'
icon: 'chevron-right'
MDLabel:
text: 'Right'
halign: 'center'
font_style: 'Icon'
etc.
The Layout classes monitor the sizes of their children, and recalculates the layout and redraws if that size changes. So, I believe that if your Image size does not change, then only that Image will be updated.
The kivy Builder maintains a set of rules based on the kv files loaded, so whether those rules come from separate files doesn't really matter. I would recommend structuring your kv file(s) based on things like maintainability, readability, and logical partitioning

Kivy: how to set subclass property relative to parent class?

I am moving my layout from a .py file to a .kv file. However, I am having difficulties in setting the text size of custom label subclass as relative to that of its parent class.
The two custom Label classes are defined in the .py script as follows:
class BaseLabel(Label):
pass
class SmallLabel(BaseLabel):
pass
while the .kv file reads:
<BaseLabel>:
color: 1, 0, 0, 1
font_size: '20sp'
<SmallLabel>:
font_size: 0.8*BaseLabel().font_size
However this last line causes an error saying that BaseLabel is not defined. How should I adjust this in the kv file?
An easy way to do that is to define a key in the kv, like this:
#:set my_font_size sp(20)
<BaseLabel>:
color: 1, 0, 0, 1
font_size: my_font_size
<SmallLabel>:
font_size: 0.8*my_font_size

Kivy:Can't put 'BorderImage' at the specified position

(1) By using the following kv file version was able to place BorderImagewidget at the specified position..
<Screen>:
ProgressBar:
max: 100
pos_hint: {'top':0.86, 'x':0.01}
size_hint_x: 0.49
size_hint_y: 0.1
canvas:
BorderImage:
border: (10, 10, 10, 10)
pos: self.x, self.center_y
size: self.width, 8
source: '0.png'
(2) But, the following Pure Python code that should realize the same function as (1) doesn't work properly.BorderImagewidget is placed at the bottom of the screen.
pos_hint={'top':0.86,'x':0.01} doesn't work.
I think that how to specify pos=(bar.x, bar.center_y) is not good because bar.center_y value is different from the code of (1).
class BarWidget(FloatLayout):
def __init__(self, **kwargs):
super(BarWidget, self).__init__(**kwargs)
self.build()
def build(self):
bar = ProgressBar(pos_hint={'top':0.86,'x':0.01}, max=100, size_hint_x=0.49, size_hint_y=0.1)
with bar.canvas:
BorderImage(border=(10, 10, 10, 10), pos=(bar.x, bar.center_y), size=(self.width/2, 8), source='0.png')
self.add_widget(bar)
How should I modify bar.center_y?
(1):screen shot
(2):screen shot
In the kv language, when you set an attribute like size: self.size, this attribute is automatically updated whenever the 'self' widget changes size of shape. When things are loading up in a screen/layout, they start with funky positions and sizes then move to be correct. Since things automatically update upon changing sizes/positions if you work in kv, it works as you expect.
In python, you have to explicitly bind some function to update the canvas if the size or position of the widget it's inheriting its size and pos from changes. You can do that by using the bind function (available in most/all kivy widgets). Using bind, you can say bind(<attribute>=<function>) which means anytime the widget's <attribute>, aka size or pos, is changed, it calls the <function>
I didn't test this exactly with your code since not all of it is posted, but this is what I do for my projects. Let me know how it works. If it doesn't work, edit your answer to be a snippet of code that I can copy/paste to work with and I'll update my answer.
class BarWidget(FloatLayout):
def __init__(self, **kwargs):
super(BarWidget, self).__init__(**kwargs)
self.build()
def build(self):
# Make it so you can reference the bar and border image later
self.bar = ProgressBar(pos_hint={'top':0.86,'x':0.01}, max=100, size_hint_x=0.49, size_hint_y=0.1)
with self.bar.canvas:
# Make it so you can reference the border image
self.border_image = BorderImage(border=(10, 10, 10, 10), pos=(bar.x, bar.center_y), size=(self.width, 8), source='0.png')
self.add_widget(self.bar)
# Make it so whenever the widget's pos or size changes, we update the border image
self.bar.bind(pos=self.update_border_image, size=self.update_border_image)
# make a function to update border image
def update_border_image(self, *args):
self.border_image.size = (self.width, 8) # Should this 8 be 0.8 by the way?
self.border_image.pos = (self.bar.x, self.bar.center_y)

rotate entire widget, not only its canvas

In this example, we can see a rotation of the canvas. But clicking the button's corners isn't registered. How can I rotate the entire button?
You can probably override its collide_point method to account for the rotation by transforming the touch coordinates.
If you use the widget system (e.g. putting the Button in a Scatter), the collision is taken care of for you.
Based on inclement's answer, I've created a button on a scatter layout, which has its hitbox set correctly.
from kivy.app import App
from kivy.lang import Builder
kv = '''
FloatLayout:
ScatterLayout:
size_hint: None, None
size: 200, 200
pos_hint: {'center_x': .5, 'center_y': .5}
rotation: 45
do_rotation: False
do_scale: False
do_translation: False
Button:
text: 'hello world'
'''
class RotationApp(App):
def build(self):
return Builder.load_string(kv)
RotationApp().run()
I used scatter layout instead of scatter, because it passes its size to children widgets. The do_x: False aren't needed in this example, because button intercepts touching events, but if I placed a label, it would move on touch.

Resources