Kivy DropDown dwa problemy

0

Witam forumowiczów.

from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.dropdown import DropDown
from kivy.uix.filechooser import FileChooserController
from kivy.lang import Builder

class kivyApp(App):
	def build(self):
		kDrp=DropDown()
		#kDrp.bind(on_release=lambda x="vv": self.kMenuBtn.text(x))
		kMnBtn1=Button(text="Load",size_hint_y=None,height=40)
		kMnBtn1.bind(on_release=lambda x=kMnBtn1: self.m_MenBtn(x,kDrp))
		kDrp.add_widget(kMnBtn1)
		kMnBtn2=Button(text="Save as",size_hint_y=None,height=40)
		kMnBtn2.bind(on_release=lambda x=kMnBtn2: self.m_MenBtn(x,kDrp))
		kDrp.add_widget(kMnBtn2)
		#kMnBtnQ=Button(text="Quit",size_hint_y=None,height=40) ##### Dodanie trzeciego guziora i nic się nie pokazuje
		#kMnBtnQ.bind(on_release=lambda x=kMnBtnQ: self.m_MenBtn(x,kDrp))
		#kDrp.add_widget(kMnBtnQ)
		kMenuBtn=Button(text="==",size_hint_y=0.1,size_hint_x=None)
		#kMenuBtn.bind(on_release=lambda x: self.m_MenuOpen(kDrp)) ########## Jak zamknąć kDrp przez metodę
		kMenuBtn.bind(on_release=kDrp.open) ##### Tak działa, ale nic nie można zrobić podczas otwierania
		kTxt=TextInput(multiline=True)
		kTxt.bind(text=self.m_kTxtInp_txtChange)
		kGrid=GridLayout(rows=2)
		kGrid.add_widget(kMenuBtn)
		kGrid.add_widget(kTxt)
		return kGrid
	
	def m_MenuOpen(self,DrpDwn):
		#self.kMenuBtn.text="vv"
		print("tutaj",DrpDwn.open) # Pokazuje właściwą wartość kDrp jeśli przekażemy przez metodę w lambda 
		#DrpDwn.open(self.kDrp) ######## Nie działa
		#DrpDwn.open(kDrp) ######## Nie działa
		#DrpDwn.open() ######## Nie działa
		pass

	def m_kTxtInp_txtChange(self,wdg,value):
		print(value)
		return

	def m_MenBtn(self,oWhat,oMenuToClose):
		print(oWhat)
		if oWhat.text=="Load":
			print("Load")
			oWhat.text="Loaded"
		elif oWhat.text=="Save as": print("Save as")
		#oMenuToClose.dismiss()
		return

app = kivyApp()
app.run()

W komentarzach:
1 problem. Nie mogę dodać pozycji drop menu numer 3. Dwie działają. Trzy i nic sie nie pokazuje.
2 problem. Podczas otwierania drop down: kDrp, chcę zmienić tekst na kMenuBtn z '==' na 'vv' więc potrzebuję metody, która wywołana
otworzy dropdown i jednocześnie zmieni napis na kMenuBtn. Bo w lambdzie nie da się robić kilku rzeczy?

Pozdrawiam
Głębicki Radosław

0

Też pozdrawiam, a jakbyś jakoś podał problem w postaci możliwej do odtworzenia, bez instalacji bibliotek; a co tam debugger?

0
  1. U mnie działa, może sobie nie odkomentowałeś kDrp.add_widget(kMnBtnQ)
  2. To co tu masz kMenuBtn.bind(on_release=kDrp.open) oznacza, że w momencie dostarcia eventu release (to jest zwolnienie przycisku), wywołana zostaje metoda open dropdownu i tylko ona, więc wypadałoby zbindować osobną metodę, która dodatkowo ustawi też napis na przycisku (ergo pewnie coś pokroju kMenuBtn.bind(on_release=self.do_your_stuff) . Ale ogólnie zamiast bindować ładniej jest definiować własne klasy i zrobić w nich listenery na eventy (metody on_something) albo definiować sobie odpowiednie on_cośtam w kv skryptach.

W każdym razie twój kod jest strasznie szpetny. Wszystko masz wpieprzone w budowanie aplikacji. Weź sobie rozdziel strukturę widgetów i logikę jak człowiek używając kv skryptu.

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout


kv = """
<MyWidget>:
    Button:
        id: dropdown_button
        text: "=="
        pos_hint: {'top': 1}
        size_hint: None, None
        on_press: root.open_dropdown()
    DropDown
        id: dropdown
        on_dismiss: root.release_dropdown()
        Button:
            text: "Load"
            size_hint_y: None
            on_release: root.load()
        Button:
            text: "Save"
            size_hint_y: None
            on_release: root.save()
        Button:
            text: "Close"
            size_hint_y: None
            on_release: root.close()
"""


class MyWidget(BoxLayout):
    def open_dropdown(self):
        self.ids["dropdown_button"].text = "vv"
        self.ids["dropdown"].open(self.ids["dropdown_button"])

    def release_dropdown(self):
        self.ids["dropdown_button"].text = "=="

    def load(self):
        self.ids["dropdown"].dismiss()
        print("Load")

    def save(self):
        self.ids["dropdown"].dismiss()
        print("Save")

    def close(self):
        App.get_running_app().stop()


class MyApp(App):
    def build(self):
        my_widget = MyWidget()
        # it's initially opened if constructed using add_widget in kv script
        my_widget.ids["dropdown"].dismiss()
        return my_widget


if __name__ == "__main__":
    Builder.load_string(kv)
    MyApp().run()
0

To mój pierwszy kontakt z kivy. Pisałem sobie w tkinter i pyqt5. Kivy samo w sobie posiada "inną" logikę. Próbuję ją zrozumieć, ale jedynie cisną mi się na usta same brzydkie wyrazy. Nie raz nie widzę związków pomiędzy elementami szczególnie pomiedzy właśnie kv, a samym programem. Generalnie na tym etapie (początki) Kivy dla mnie to porażka. W każdym razie wielkie dzięki.

0

Język kv to DSL to definiowania struktury widgetów i ich własności (jak text dla przycisku czy etykiety) w formie hierarhicznego drzewka, odpowiednik QML-a w Qt. Każdą klasę, jaką sobie stworzymy możemy zapełnić widgetami i ustawić definiując je jako <NazwaKlasy> w tym DSL-u. Wygląda to mniej więcej tak

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

kv = """
<MyWidget>:
    FunnyButton
        size_hint_x: 0.2
    FunnyButton
        size_hint_x: 0.6
    FunnyButton
        size_hint_x: 0.2

<FunnyButton>:
    text: "funny"
    on_press: print("Funny")
"""


class MyWidget(BoxLayout):
    pass


class FunnyButton(Button):
    pass


class MyApp(App):
    def build(self):
        return MyWidget()


if __name__ == "__main__":
    Builder.load_string(kv)
    MyApp().run()

Oczywiście, żeby nie mieszać logiki ze strukturą, lepiej zamiast stosować kod Pythona bezpośrendio w kodzie kv, lepiej je wrzucić do klasy:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

kv = """
<MyWidget>:
    FunnyButton
        size_hint_x: 0.2
    FunnyButton
        size_hint_x: 0.6
    FunnyButton
        size_hint_x: 0.2

<FunnyButton>:
    text: "funny"
    on_press: self.funny()
"""


class MyWidget(BoxLayout):
    pass


class FunnyButton(Button):
    def funny(self):
        print("Funny")


class MyApp(App):
    def build(self):
        return MyWidget()


if __name__ == "__main__":
    Builder.load_string(kv)
    MyApp().run()

Jako że jednak całe Kivy działa na eventach, to zamiast tego można też zdefiniować metody on_eventname w takiej klasie:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

kv = """
<MyWidget>:
    FunnyButton
        size_hint_x: 0.2
    FunnyButton
        size_hint_x: 0.6
    FunnyButton
        size_hint_x: 0.2

<FunnyButton>:
    text: "funny"
"""


class MyWidget(BoxLayout):
    pass


class FunnyButton(Button):
    def on_press(self, *args):
        print("Funny")


class MyApp(App):
    def build(self):
        return MyWidget()


if __name__ == "__main__":
    Builder.load_string(kv)
    MyApp().run()
0

W kv po ostatnim przycisku dodałem:

	TextInput:
		multiline: True

A w def build my_widget.orientation='vertical' i mam u góry dropdown, a poniżej pole do pisania tekstu. Tylko, że po wpisaniu paru znaków i ponownym wywołaniu dropdown przerywa wypisując:

Traceback (most recent call last):
File "/home/radek/Dokumenty/python/calcNotes.py", line 87, in <module>
MyApp().run()
File "/home/radek/.local/lib/python3.7/site-packages/kivy/app.py", line 950, in run
runTouchApp()
File "/home/radek/.local/lib/python3.7/site-packages/kivy/base.py", line 582, in runTouchApp
EventLoop.mainloop()
File "/home/radek/.local/lib/python3.7/site-packages/kivy/base.py", line 347, in mainloop
self.idle()
File "/home/radek/.local/lib/python3.7/site-packages/kivy/base.py", line 391, in idle
self.dispatch_input()
File "/home/radek/.local/lib/python3.7/site-packages/kivy/base.py", line 342, in dispatch_input
post_dispatch_input(*pop(0))
File "/home/radek/.local/lib/python3.7/site-packages/kivy/base.py", line 248, in post_dispatch_input
listener.dispatch('on_motion', etype, me)
File "kivy/_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "/home/radek/.local/lib/python3.7/site-packages/kivy/core/window/init.py", line 1412, in on_motion
self.dispatch('on_touch_down', me)
File "kivy/_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "/home/radek/.local/lib/python3.7/site-packages/kivy/core/window/init.py", line 1428, in on_touch_down
if w.dispatch('on_touch_down', touch):
File "kivy/_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "/home/radek/.local/lib/python3.7/site-packages/kivy/uix/widget.py", line 545, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy/_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
File "/home/radek/.local/lib/python3.7/site-packages/kivy/uix/behaviors/button.py", line 151, in on_touch_down
self.dispatch('on_press')
File "kivy/_event.pyx", line 705, in kivy._event.EventDispatcher.dispatch
File "kivy/_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
File "kivy/_event.pyx", line 1132, in kivy._event.EventObservers._dispatch
File "/home/radek/.local/lib/python3.7/site-packages/kivy/lang/builder.py", line 57, in custom_callback
exec(kvlang.co_value, idmap)
File "<string>", line 8, in <module>
File "/home/radek/Dokumenty/python/calcNotes.py", line 61, in open_dropdown
self.ids["dropdown"].open(self.ids["dropdown_button"])
File "kivy/weakproxy.pyx", line 32, in kivy.weakproxy.WeakProxy.getattr
File "kivy/weakproxy.pyx", line 28, in kivy.weakproxy.WeakProxy.ref
ReferenceError: weakly-referenced object no longer exists

0

Takie cósik znalazłem:

" An id is a weakref to the widget and not the widget itself. As a consequence, storing the id is not sufficient to keep the widget from being garbage collected.
To keep the widget alive, a direct reference to the id refresh must be kept. This is achieved using id.self or refresh.self in this case."
Czy to ma jakiś związek? Jest to jakieś rozwiązanie? Jak to zastosować?

0

Takie cósik znalazłem:

" An id is a weakref to the widget and not the widget itself. As a consequence, storing the id is not sufficient to keep the widget from being garbage collected.
To keep the widget alive, a direct reference to the id refresh must be kept. This is achieved using id.self or refresh.self in this case."
Czy to ma jakiś związek? Jest to jakieś rozwiązanie? Jak to zastosować?

0

I jeszcze kilka pytań. Czy to co prezentuje Kivy to jest normalne obiektowe czy jakieś nienormalne? Jak rozumieć logikę tych procesów? Jak przekazywać parametry do elementów? Tylko i wyłącznie poznanie logiki class w pythonie da mi możliwość programowania w Kivy????
Czy jest możliwość pominięcia pliku/stringu kv???

0

Co do błędu, to nie potrafię go zreprodukować, może jakbyś dał cały kod to byłbym w stanie stwierdzić dlaczego się wywala.

Czy to co prezentuje Kivy to jest normalne obiektowe czy jakieś nienormalne?

Co znaczy "normalne"? Dla niektórych to programowanie obiektowe to jakaś chora aberracja, którą wyklinają od czci i wiary na rzecz swoich funkcjonalnych odpowiedników :D Po prostu framework jak framework, z własnymi udziwnieniami i pułapkami. Pewnie wypadałoby przeczytać trochę dokumentacji, tutoriali i przykładów na Stack Overflow przed skakaniem od razu na głęboką wodę i próbie pisania dużego projektu

Jak przekazywać parametry do elementów?

Albo w kv, albo w konstruktorach (Button(text="...")), albo ustawiając własności (button.text = "..." - to dodatkowo wywoła event on_text, który można bindować do metod).

Tylko i wyłącznie poznanie logiki class w pythonie da mi możliwość programowania w Kivy?

Nie rozumiem pytania. Im lepiej znasz język i framework, tym łatwiej w nim będziesz pisać, to dość jasne. Kivy akurat wydaje się miejscami udziwnione, więc trzeba eksperymentować...

Czy jest możliwość pominięcia pliku/stringu kv???

Jak najbardziej, patrz https://stackoverflow.com/questions/41512799/create-kivy-widgets-without-using-kv-language

0

Pytanko. Czy jest możliwość wywołania jakieś funkcji automatycznie zaraz po otwarciu głównego okna Kivy. Jakieś ładowanie do textinput, ustawienie "cursora" na 0,0 i ustawienie "focus" dla textinput? Jest coś takiego?

0

No dzięki. Szukałem i znalazłem te same rozwiązania. Już zastosowałem. Okazało się, że jest jakiś problem z textwidget.cursor(0,0) więc trzeba stosować:

Clock.schedule_once(lambda _: setattr(textwidget, 'cursor', (0, 0)))

Ja to sobie wstawiłem w on_start.
I to rozwiązanie zadziałało.

Pozdrawiam i walczę z Kivy dalej.

0

Czy jest możliwość stworzenia apk dla androida z Tkinterem, a nie z Kivy jako GUI?

1

Nie, możesz się bawić w Pydroid3, ale tak naprawdę apkę na androida w pythonie napiszesz raczej tylko w Kivy.

0

Witam ponownie w tym wątku. Kolejne pytanie. Jak uzyskać informacje o wymiarach, wysokość i długość linii w textinput.
w dokumentacji jest: from kivy.core.text import FontContextManager as FCM, a tam jest funkcja content_size zwracajaca width i length.
Jeśli mam najdłuższą linię z textinput to czy mogę jakoś używając tej funkcji ją wymierzyć. Dodatkowo nie wiem jak przekazać jej wykorzystywany font.
Lub czy można użyć innego narzędzia/modułu w pythonie?

0

Wysokość linii to po prostu line_height, szerokość można obliczyć metodą text_input._get_text_width("text to calculate width", text_input.tab_width, text_input._label_cached), zawartość linii pobrać atrybutem _lines, razem:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

from kivy.lang import Builder

Builder.load_string(
    """

<MyWidget>:
    Button:
        text: "Print width"
        on_press: root.print_text_dimensions()
    TextInput
        text: "Open source Python library for rapid development of applications that make use of innovative user interfaces, such as multi-touch apps"
        id: ti
"""
)


class MyWidget(BoxLayout):
    def print_text_dimensions(self):
        text_input = self.ids["ti"]
        for line in text_input._lines:
            line_width = text_input._get_text_width(
                line,
                text_input.tab_width,
                text_input._label_cached,
            )
            line_height = text_input.line_height
            print(f"Line ({line}): {line_width}x{line_height}")
        print('~' * 20)


class MyApp(App):
    def build(self):
        return MyWidget()


if __name__ == "__main__":
    MyApp().run()

Źródła:

0

Dobra. Przetestowane. U mnie line_height zawsze daje 26. Dodatkowo wyliczenie długości linii jest o parę pixeli za krótkie i ucina ostatni znak w polu tekstowym. Ja odkryłem/zastosowałem pokręconą metodę i ona daje wynik przy font_size=22 o 6px dłuższy width. Moja dziwna metoda stawiam "cursor" na końcu najdłuższej i odczytuję jego pozycję za pomocą "_get_cursor_pos()"
Height wyliczam font_size+line_spacing+1.
I jeszcze jedna metoda ._line_labels[index_linii].size daje tuplę (długość,wysokość), ale też długość jest za krótka. Coś to Kivy to jakaś beta powinna być ;-D

0

I kolejny problem. W czasie wpisywania, gdy nacisnę "#" wywołuję popupa z buttonami z ustalonymi wyrazami które chcę wstawić na pozycji kursora. Komendą popup.open() wywołuję popupa pojawiają się buttony z wyrazami. Po wybraniu przycisku w konsoli mam tekst. Tylko, że nie wiem jak go wstawić w pozycji kursora w textinput? Okazuje się, że po wywołaniu open() program kontynuuje. nie czeka na wybranie przycisku. Wprowadzenie wyboru w zmienną globalną nic nie daje. Program nie czeka. Ktoś, coś?

Mam: textinput.insert_text(textToInsert)

0

I kolejne dziwne problemy: "filechooserlistview" w "kv" posiada metodę "on_selection", a w kodzie nie można jej użyć/nie działa. Jak to możliwe? Chcę jednym kliknięciem zaznaczyć plik i wpisać go do textinput poniżej listy plików.

ROZWIĄZANIE to samo "selection" bez "on_" Dziwne.

A drugi to rozmiar czcionki w "filechooserlistview". Znalazłem taką informację:
"
There is no direct way to do that, but you can fiddle with the FileListEntry template that the FileChooserListView uses to display the entries.
"
Czy ktoś wie jak to użyć?
Albo jakoś regulować wysokość pozycji na liście plików.
Rozwiąznie problemu 2.

Builder.load_string('''
[FileListEntry@FloatLayout+TreeViewNode]:
    locked: False
    entries: []
    path: ctx.path
    is_selected: self.path in ctx.controller().selection

    orientation: 'horizontal'
    size_hint_y: None
    height: '38dp' if dp(1) > 1 else '18dp'                         # height must be big enough to hold font sized below
    # Don't allow expansion of the ../ node
    is_leaf: not ctx.isdir or ctx.name.endswith('..' + ctx.sep) or self.locked
    on_touch_down: self.collide_point(*args[1].pos) and ctx.controller().entry_touched(self, args[1])
    on_touch_up: self.collide_point(*args[1].pos) and ctx.controller().entry_released(self, args[1])
    BoxLayout:
        pos: root.pos
        size_hint_x: None
        width: root.width - dp(10)
        Label:
            id: filename
            font_size: '18dp'                                  # adjust this font size
            size_hint_x: None
            width: root.width - sz.width                       # this allows filename Label to fill width less size Label
            text_size: self.width, None
            halign: 'left'
            shorten: True
            text: ctx.name
        Label:
            id: sz
            font_size: '16dp'                                  # adjust this font size
            #text_size: self.width, None
            size_hint_x: None
            width: self.texture_size[0]                        # this makes the size Label to minimum width
            text: '{}'.format(ctx.get_nice_size())
''')
0

A tu dziwne zachowanie Textinput wewnątrz ScrollView. Przykład minimalny z zaobserwowanymi problemami:

import time,os,datetime,re,inspect,json,codecs
import datetime as dt
from datetime import datetime,date
from sys import platform

from kivy.app import runTouchApp,stopTouchApp
from kivy.core.window import Window
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.clock import Clock

lorem = """'1 Seerspica
2 totam rem aperaque it fds fds fds fds 9876543210
Nemo enim ipsaptatem dfd fd fds
sed quia conser magn dsfd f dfd fds9876543210

qui dolorem ipia dol dfd f9876543210
sed quia non n eiusd.
Ut enim ad minniam,
quis nostrum eatione
nisi ut aliquia commdsf df dsf9876543210
Quis autem velure rem
vel illum qui m eumqui dolorem ipia dol dfd 9876543210
sed quia non n eiusd.
Ut enim ad minniam,
quis nostrum eatione
nisi ut aliquia commdsf df dsfdsfdsfdsf 9876543210
Quis autem velure rem
vel illum qui m eumqui dolorem ipia dol dfd fd 9876543210
sed quia non n eiusd.
Ut enim ad minniam,
quis nostrum eatione
nisi ut aliquia commdsf df dsfdsfd9876543210
Quis autem velure rem
vel illum qui m eumqui dolorem ipia dol cvcdfd fd 9876543210
sed quia non n eiusd.
Ut enim ad minniam,
quis nostrum eatione
Last-2: nisi ut aliquia commdsf df dsfdsf9876543210
Last-1: Quis autem velure rem
Last  : vel illum qui m eum"""

gnLongest=0
gnLongestPx=0
gidxLongest=0
gnLinesCount=0
gnFontSize=22

glLines=lorem.splitlines()
for idx,line in enumerate(glLines):
	nLen=len(line)
	if nLen>gnLongest:
		gnLongest=nLen
		gidxLongest=idx
gnLinesCount=idx

kScrl=ScrollView(scroll_type=['content'],size_hint=(1,1))
kTxt=TextInput(text=lorem,multiline=False,size_hint=(None,None),padding=[20,20,40,20],font_size=gnFontSize)

nWidth=kTxt._get_text_width(glLines[gidxLongest],kTxt.tab_width, kTxt._label_cached)
kTxt.width=nWidth

nTxtInputHeight=gnLinesCount*(gnFontSize+kTxt.line_spacing) #,scrl.height))
if nTxtInputHeight<Window.height: kTxt.height=int(Window.height*0.94)
else: kTxt.height=nTxtInputHeight

Clock.schedule_once(lambda _: setattr(kTxt, 'cursor', (0, 0))) # col,line
kTxt.focus=True
kScrl.add_widget(kTxt)
runTouchApp(kScrl)
quit()

ScrollView nie podąża za cursorem jeśli używamy strzełek kierunku bądż gdy piszemy. Dopiero osiągnięcie jakiegoś tam końca powoduje przesuwanie scrolla. Cięzkie to kivy. Cały czas myslę, że niedopracowane.
Można prościć o podpowiedzi. Myślałem o przechwytywaniu pozycji kursora, ale jak wykryć naciskanie klawiszy kierunku na klawiaturze?
Przy zwykłym pisaniu coś tam kombinuję ze skroll_x i podawaniem tam wartości. Wywoływanie po każdej literce textinput.focus=True nic nie daje.
Dodam kolejną dziwną rzecz. textinput._get_line_options()
w wyniku słownik a tam czcionka zawsze 22.5 i Roboto mimo że ustawiam inną.
Ta kolejna dziwna rzecz to rozwiązaniem jest odczyt tych parametrów zaraz przed: runTouchApp()

Podążanie za pisanym tekstem/kursorem to wyliczenie z dok. 2 miejsc: act. pozycja kursora / najdłuższa linia i przekazanie tego do scrollview.scroll_x=
Trzeba sprawdzać czy właśnie nie generujemy najdłuższej linii i ew. aktualizować tę wartość.
Ale to się dzieje tylko przy pisaniu. Jak przechwytywać inne klawisze: strzałki kierunku, Home, End etc.?

Ze względu na to, że nie znalazłem nic jak przechwytywać klawisze w textinput to zastosowałem odczyt pozycji kursora co 250 milisekund:

	Clock.schedule_interval(lambda _: cursor_move(),0.25)

I w funkcji cursor_move sprawdzam, czy jest różnica w stosunku do poprzedniej pozycji przechowywanej w zm. globalnej. Jeśli tak to wyliczam nową dalej wyliczam podaną wyżej proporcję dla x i y i ustawiam scroll_x i scroll_y. Tu wychodzi nastepne dziwactwo Kivy Współrzędne y liczy się od dołu. Więc dla scroll_y góra jest na 1 a dół na 0. Ach to Kivy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1