Anonymous
Kivy ScrollView и ScreenManager: как обработать значение чрезмерной прокрутки и вызвать событие на главном экране
Сообщение
Anonymous » 31 мар 2026, 14:04
Во-первых, программирование для меня — хобби, а не профессия. В приведенном ниже примере кода создаются некоторые искусственные данные в словаре, к которым я хочу получить доступ по ключу, когда контейнер ScrollView прокручивается слишком сильно. Я уже несколько дней пытаюсь найти решение... играю с декораторами и ищу подобные проблемы в сети, но безуспешно. Я использую Python 3.14.1 в Linux с Kivy 2.3.1.
Пример main.kv
Код: Выделить всё
#: import Effect main.Effect
ManagerWidget:
StartScreen:
Page2Screen:
:
name: 'startscreen'
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size_hint_y: .1
Button:
id: btn1
text: 'Simulation of desired "prev_key" function via overscroll event'
on_release: root.prev_key ( )
Button:
id: btn2
text: 'Simulation of desired "next_key" function via overscroll event'
on_release: root.next_key ( )
ScrollView:
id: scroller
effect_cls: Effect
size_hint: 1, 1
do_scroll_x: False
GridLayout:
id: container
cols: 5
spacing: 2
size_hint_y: None
height: self.minimum_height
BoxLayout:
size_hint_y: .1
Button:
text: 'Go to Page 2'
on_release: root.manager.current = 'page2'
:
name: 'page2'
BoxLayout:
padding: 5
spacing: 5
orientation: 'vertical'
Label:
text: 'Placeholder 1'
Label:
text: 'Placeholder 2'
Label:
text: 'Placeholder 3'
BoxLayout:
size_hint_y: .3
Button:
text: 'Back to main screen'
on_release:
root.manager.current = 'startscreen'
И main.py
Код: Выделить всё
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.textinput import TextInput
from kivy.effects.dampedscroll import DampedScrollEffect
from kivy.metrics import dp
from kivy.clock import Clock
from time import time
class CallControl:
def __init__ ( self, max_call_interval ):
self._max_call_interval = max_call_interval
self._last_call = time ( )
def __call__ ( self, function ):
def wrapped ( *args, **kwargs ):
now = time ( )
if now - self._last_call > self._max_call_interval:
self._last_call = now
function ( *args, **kwargs )
return wrapped
class Effect ( DampedScrollEffect ):
def __init__ ( self, *args, **kwargs ):
super ( Effect, self ).__init__ ( **kwargs )
def on_overscroll ( self, *args ):
self.args = args
if self.overscroll < -100:
self.scrollup ( )
elif self.overscroll > 100:
self.scrolldown ( )
@CallControl ( max_call_interval = 1 )
def scrollup ( self ):
print ( 'How to update the Scrollview widgets? (i.e. the prev_key method in StartScreen )' )
# StartScreen.prev_key ( self.overscroll ) ???
@CallControl ( max_call_interval = 1 )
def scrolldown ( self ):
print ( 'How to update the Scrollview widgets? (i.e. the next_key method in StartScreen )' )
# StartScreen.next_key ( self.overscroll ) ???
class StartScreen ( Screen ):
def __init__ ( self, **kwargs ):
super( StartScreen, self).__init__( **kwargs )
# Create some artificial data for this sample code to fill the GridLayout container
self.num = '11'
self.data = { }
self.keys = [ ]
lines = [ ]
for k in range ( 10, 15 ):
for v in range ( 25 ):
lines.append ( 'ONE,Key %d,%d,yxz,,white,gray,yellow,red,green,\n' % ( k, k+v ) )
self.data[ str ( k ) ] = lines
self.keys.append ( str ( k ) )
lines = [ ]
self.colour_mapping = { 'white' : ( 1,1,1,1, ),
'gray' : ( 0,0,0,.2 ),
'green' : ( 0,1,0,.3 ),
'red' : ( 1,0,0,.3 ),
'yellow' : ( 1,1,0,.3 ) }
Clock.schedule_once ( self.finish_init, 0 )
def finish_init ( self, dt ):
self.update_container ( )
# @classmethod ? But how to call self.update_container then? And the keys list is empty anyway... hmpf...
def prev_key ( self, *args ):
idx = self.keys.index ( self.num )
if idx in ( 0, len ( self.keys ) ):
print ( 'no more keys in the dict...' )
self.num = self.keys[ idx ]
else:
self.num = self.keys[ idx - 1 ]
self.update_container ( )
def next_key ( self, *args ):
idx = self.keys.index ( self.num )
try:
self.num = self.keys[ idx + 1 ]
except IndexError:
print ( 'no more keys in the dict...' )
self.num = self.keys[ idx ]
self.update_container ( )
def update_container ( self ):
hgt_row = 35
fnt_size = 15
self.ids.container.clear_widgets ( )
for line in self.data.get ( self.num ):
d1, d2, d3, d4, empty, bg1,bg2,bg3,bg4,bg5, nl = line.split( ',' )[ : ]
colour1 = self.colour_mapping.get ( bg1 )
colour2 = self.colour_mapping.get ( bg2 )
colour3 = self.colour_mapping.get ( bg3 )
colour4 = self.colour_mapping.get ( bg4 )
colour5 = self.colour_mapping.get ( bg5 )
in_c1 = TextInput ( height = hgt_row,\
text = d1,\
font_size = dp ( fnt_size ),\
size_hint = ( .1, None ),\
background_color = colour1 )
in_c2 = TextInput ( height = hgt_row,\
text = d2,\
font_size = dp ( fnt_size ),\
size_hint = ( .12, None ),\
background_color = colour2 )
in_c3 = TextInput ( height = hgt_row,\
text = d3,\
size_hint = ( .07, None ),\
background_color = colour3 )
in_c4 = TextInput ( height = hgt_row,\
size_hint = ( .09, None ),\
text = d4,\
background_color = colour4 )
in_c5 = TextInput ( height = hgt_row,\
size_hint = ( .7, None ),\
hint_text = empty,\
background_color = colour5 )
self.ids.container.add_widget ( in_c1 )
self.ids.container.add_widget ( in_c2 )
self.ids.container.add_widget ( in_c3 )
self.ids.container.add_widget ( in_c4 )
self.ids.container.add_widget ( in_c5 )
class Page2Screen ( Screen ):
pass
class ManagerWidget ( ScreenManager ):
pass
class MainApp ( App ):
pass
if __name__ == '__main__':
MainApp( ).run( )
Что мне здесь не хватает? Есть предложения?
1774955054
Anonymous
Во-первых, программирование для меня — хобби, а не профессия. В приведенном ниже примере кода создаются некоторые искусственные данные в словаре, к которым я хочу получить доступ по ключу, когда контейнер ScrollView прокручивается слишком сильно. Я уже несколько дней пытаюсь найти решение... играю с декораторами и ищу подобные проблемы в сети, но безуспешно. Я использую Python 3.14.1 в Linux с Kivy 2.3.1. Пример main.kv [code]#: import Effect main.Effect ManagerWidget: StartScreen: Page2Screen: : name: 'startscreen' BoxLayout: orientation: 'vertical' canvas.before: Color: rgba: 1,1,1,1 Rectangle: pos: self.pos size: self.size BoxLayout: size_hint_y: .1 Button: id: btn1 text: 'Simulation of desired "prev_key" function via overscroll event' on_release: root.prev_key ( ) Button: id: btn2 text: 'Simulation of desired "next_key" function via overscroll event' on_release: root.next_key ( ) ScrollView: id: scroller effect_cls: Effect size_hint: 1, 1 do_scroll_x: False GridLayout: id: container cols: 5 spacing: 2 size_hint_y: None height: self.minimum_height BoxLayout: size_hint_y: .1 Button: text: 'Go to Page 2' on_release: root.manager.current = 'page2' : name: 'page2' BoxLayout: padding: 5 spacing: 5 orientation: 'vertical' Label: text: 'Placeholder 1' Label: text: 'Placeholder 2' Label: text: 'Placeholder 3' BoxLayout: size_hint_y: .3 Button: text: 'Back to main screen' on_release: root.manager.current = 'startscreen' [/code] И main.py [code]from kivy.app import App from kivy.uix.screenmanager import ScreenManager, Screen from kivy.uix.textinput import TextInput from kivy.effects.dampedscroll import DampedScrollEffect from kivy.metrics import dp from kivy.clock import Clock from time import time class CallControl: def __init__ ( self, max_call_interval ): self._max_call_interval = max_call_interval self._last_call = time ( ) def __call__ ( self, function ): def wrapped ( *args, **kwargs ): now = time ( ) if now - self._last_call > self._max_call_interval: self._last_call = now function ( *args, **kwargs ) return wrapped class Effect ( DampedScrollEffect ): def __init__ ( self, *args, **kwargs ): super ( Effect, self ).__init__ ( **kwargs ) def on_overscroll ( self, *args ): self.args = args if self.overscroll < -100: self.scrollup ( ) elif self.overscroll > 100: self.scrolldown ( ) @CallControl ( max_call_interval = 1 ) def scrollup ( self ): print ( 'How to update the Scrollview widgets? (i.e. the prev_key method in StartScreen )' ) # StartScreen.prev_key ( self.overscroll ) ??? @CallControl ( max_call_interval = 1 ) def scrolldown ( self ): print ( 'How to update the Scrollview widgets? (i.e. the next_key method in StartScreen )' ) # StartScreen.next_key ( self.overscroll ) ??? class StartScreen ( Screen ): def __init__ ( self, **kwargs ): super( StartScreen, self).__init__( **kwargs ) # Create some artificial data for this sample code to fill the GridLayout container self.num = '11' self.data = { } self.keys = [ ] lines = [ ] for k in range ( 10, 15 ): for v in range ( 25 ): lines.append ( 'ONE,Key %d,%d,yxz,,white,gray,yellow,red,green,\n' % ( k, k+v ) ) self.data[ str ( k ) ] = lines self.keys.append ( str ( k ) ) lines = [ ] self.colour_mapping = { 'white' : ( 1,1,1,1, ), 'gray' : ( 0,0,0,.2 ), 'green' : ( 0,1,0,.3 ), 'red' : ( 1,0,0,.3 ), 'yellow' : ( 1,1,0,.3 ) } Clock.schedule_once ( self.finish_init, 0 ) def finish_init ( self, dt ): self.update_container ( ) # @classmethod ? But how to call self.update_container then? And the keys list is empty anyway... hmpf... def prev_key ( self, *args ): idx = self.keys.index ( self.num ) if idx in ( 0, len ( self.keys ) ): print ( 'no more keys in the dict...' ) self.num = self.keys[ idx ] else: self.num = self.keys[ idx - 1 ] self.update_container ( ) def next_key ( self, *args ): idx = self.keys.index ( self.num ) try: self.num = self.keys[ idx + 1 ] except IndexError: print ( 'no more keys in the dict...' ) self.num = self.keys[ idx ] self.update_container ( ) def update_container ( self ): hgt_row = 35 fnt_size = 15 self.ids.container.clear_widgets ( ) for line in self.data.get ( self.num ): d1, d2, d3, d4, empty, bg1,bg2,bg3,bg4,bg5, nl = line.split( ',' )[ : ] colour1 = self.colour_mapping.get ( bg1 ) colour2 = self.colour_mapping.get ( bg2 ) colour3 = self.colour_mapping.get ( bg3 ) colour4 = self.colour_mapping.get ( bg4 ) colour5 = self.colour_mapping.get ( bg5 ) in_c1 = TextInput ( height = hgt_row,\ text = d1,\ font_size = dp ( fnt_size ),\ size_hint = ( .1, None ),\ background_color = colour1 ) in_c2 = TextInput ( height = hgt_row,\ text = d2,\ font_size = dp ( fnt_size ),\ size_hint = ( .12, None ),\ background_color = colour2 ) in_c3 = TextInput ( height = hgt_row,\ text = d3,\ size_hint = ( .07, None ),\ background_color = colour3 ) in_c4 = TextInput ( height = hgt_row,\ size_hint = ( .09, None ),\ text = d4,\ background_color = colour4 ) in_c5 = TextInput ( height = hgt_row,\ size_hint = ( .7, None ),\ hint_text = empty,\ background_color = colour5 ) self.ids.container.add_widget ( in_c1 ) self.ids.container.add_widget ( in_c2 ) self.ids.container.add_widget ( in_c3 ) self.ids.container.add_widget ( in_c4 ) self.ids.container.add_widget ( in_c5 ) class Page2Screen ( Screen ): pass class ManagerWidget ( ScreenManager ): pass class MainApp ( App ): pass if __name__ == '__main__': MainApp( ).run( ) [/code] Что мне здесь не хватает? Есть предложения?