Scenario: I've a window with 3 columns, in the 2nd is a RecycleView
with 20 items. When the user drops an item onto the RecycleView
, I want to get the corresponding data-item.
All I need is to tell, onto which RecycleView
's item was a file dropped. The same would go for a on_touch
event, when dispatched at the parent's level; of course, the item can have its own on_dispatch
, but the on_file_drop
is bound at the Window
class itself, so I have to drill-down which item in the RV is hit.
Here is the demo-code:
<PageThumb>:
size_hint: 1, None
orientation: 'vertical'
Image:
id: imgThumbnail
size_hint: 1, None
height: 300
Label:
id: lblPageNr
size_hint: 1, None
height: 20
GridLayout:
cols: 3
id: mainGrid
BoxLayout:
size_hint: 0.4, 1
orientation: 'vertical'
Label:
text: '1st column'
RecycleView:
size_hint: 0.2, 1
id: rvMain
viewclass: 'PageThumb'
RecycleBoxLayout:
padding: 5
spacing: 20
default_size: None, 320
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
BoxLayout:
size_hint: 0.4, 1
id: layoutPreview
Label:
text: '3rd column'
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty
class PageThumb(BoxLayout):
page_nr = NumericProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
def on_page_nr(self, obj, page_nr):
obj.ids.lblPageNr.text = str(page_nr+1)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
# touch is inside this widget
app = App.get_running_app()
print(f"touched page_nr={self.page_nr}")
class DemoApp(App):
def getSystemSizeFromScaledSize(self, win, scaledX, scaledY):
scaleX = win.size[0] / win.system_size[0]
scaleY = win.size[1] / win.system_size[1]
systemX = scaledX * scaleX;
systemY = scaledY * scaleY;
return (systemX, systemY)
def on_drop_file(self, win, bFileName, x, y):
fileName = bFileName.decode("utf-8")
xS, yS = self.getSystemSizeFromScaledSize(win, x, y)
if self.root.ids.rvMain.collide_point(xS, yS):
yL = self.root.ids.rvMain.layout_manager.size[1] - yS
# pos = self.root.ids.rvMain.to_local(xS, yL)
ndx = self.root.ids.rvMain.layout_manager.get_view_index_at(pos=(xS, yL))
page = list(self.root.ids.rvMain.layout_manager.view_indices)[ndx]
print(f"d&d ndx={ndx} page_nr={page.page_nr}")
def build(self):
from kivy.core.window import Window
Window.bind(on_drop_file = self.on_drop_file)
pages = ({"page_nr": i} for i in range(0, 20))
self.root.ids.rvMain.data = pages
DemoApp().run()
Questions:
on_file_drop
?My analysis so far:
The problem boils down to handle the on_drop_file
event, which gets the screen's x/y. I've realized (despite the doc/examples don't show this) that I must convert the screen's x/y using the resolution density: My Windows' desktop uses 250% scaling and other event-handlers, like on_touch
, need the density multiplication for correct detection in collide_point()
.
I think I must apply this density calculation also inside the layout_manager.get_view_index_at(pos)
. When I look into the layout_manager.size
, I see a huge y value, suggesting it covers all the underlying data, it's a bit bigger than 20 (items) x 320 (height
of 1 item), but it's unfortunately the same small value. I've tried all the to_local()
/to_widget()
combinations of RecycleView
, it's .layout_manager
and that like - but all trials got me rather small values, although to get the item's index 0, I need to call the layout_manager.get_view_index_at()
with something like (0, 6790) and not like (0, 10).
I've tried to play with the layout_manager.view_indices
and children[..]
etc. with no luck.
TY :)!
Here is a hack that seems to work. I added an id
to your RecycleBoxLayout
:
RecycleView:
size_hint: 0.2, 1
id: rvMain
viewclass: 'PageThumb'
RecycleBoxLayout:
id: rvBox
Then, I use that in the on_drop_file()
method:
def on_drop_file(self, win, bFileName, x, y):
fileName = bFileName.decode("utf-8")
rvbox = self.root.ids.rvBox
x1, y1 = rvbox.to_widget(x, self.root.top - y)
for w in rvbox.walk():
if isinstance(w, PageThumb):
if w.collide_point(x1, y1):
print('\tdropped', fileName, 'on', w.ids.lblPageNr.text)
break
The coordinate transform methods are difficult to comprehend, and I believe my code will only work for your specific widget tree.