Kivy - centrowanie obrazka w butonie bez pliku kv

0

Witam ponownie walcząc z kivy.

from kivy.app import runTouchApp,stopTouchApp
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.uix.button import Button

layout=FloatLayout(size_hint=(1,1))
btn=Button(size_hint=(.2,.1),pos_hint={'x':0.5,'y':0.4}) #,background_normal=,border=[70,70,70,70])
img=Image(source="./pic/0.png",allow_stretch=True,size_hint=(.3,.3),pos_hint={'x':.5,'y':.5})
btnLay=FloatLayout()
btnLay.add_widget(img)
btn.add_widget(btnLay)
layout.add_widget(btn)
runTouchApp(layout)
quit()

W powyższym kodzie próbuję wycentrować obrazek wewnątrz przycisku. Próbowałem bezpośrednio. Znalazłem poradę, aby wewnątrz przycisku umieścić Layout. Tu jest FloatLayout. I coś nie chce zaskoczyć. Co robię źle?

Pozdrawiam
Głębicki Radosław

1

Przycisk to przycisk, obrazek to osobny widget, nawet jak sobie umieścisz dokładnie nad przyciskiem, to jedynie go sobie przysłonisz. Poprawne podejście polega na stworzeniu sobie własnego przycisku poprzez stworzenie podklasy ButtonBehavior (widget Button jest zaimplentowany jako klasa dziedzicząca po Label i ButtonBehavior), z wybranym layoutem. W najprostszym przypadku jest to po prostu podklasa widgetu Image i ButtonBehavior.

from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.behaviors import ButtonBehavior


class MyButton(ButtonBehavior, Image):
    def __init__(self, **kwargs):
        super(MyButton, self).__init__(**kwargs)
        self.source = 'off.jpeg'

    def on_press(self):
        self.source = 'on.jpeg'

    def on_release(self):
        self.source = 'off.jpeg'


class SampleApp(App):
    def build(self):
        return MyButton()


SampleApp().run()

off.jpeg
on.jpeg

Jak chcesz mieć przycisk składający się z obrazka i napisu, to tworzysz podklasę BoxLayout i ButtonBehavior i w layout wstawiasz obrazek i napis, jak wymagane, a w odpowiednich metodach stylizujesz (bądź nie) aby było widać, kiedy przycisk jest naciskany.

Prymitywny przykład

from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.graphics import Color, Rectangle


class MyButton(ButtonBehavior, BoxLayout):
    def __init__(self, **kwargs):
        super(MyButton, self).__init__(**kwargs)
        self.image = Image(source="off.jpeg")
        self.label = Label(text="Click me...")
        self.orientation = "horizontal"
        self.add_widget(self.image)
        self.add_widget(self.label)

    def on_size(self, *args):
        with self.canvas.before:
            self.canvas.before.clear()
            Color(1., 0, 0)
            Rectangle(pos=self.pos, size=self.size)

    def on_press(self):
        self.image.source = 'on.jpeg'
        self.label.text = "yay...!"

    def on_release(self):
        self.image.source = 'off.jpeg'
        self.label.text = "Click me..."


class SampleApp(App):
    def build(self):
        layout = FloatLayout()
        layout.add_widget(MyButton(size_hint=(0.2, 0.2), pos=(40, 200)))
        return layout


SampleApp().run()
0
from kivy.app import runTouchApp,stopTouchApp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.uix.button import Button

def on_start(*Data):
	print("center_count",btn.pos[0]+btn.size[0]*0.5,btn.pos[1]+btn.size[1]*0.5)
	print("pos",btn.pos)
	print("size",btn.size)
	print("img",img.size)
	img.size=(30,30) # tu wymuszam rozmiar i trochę pomaga, ale wciąż nie na środku, zmiana kolejności linii 1 i 2 i...
	img.pos=btn.center
	print('img_pos',img.pos)
	return None

Window.borderless=True
MainLay=BoxLayout(size_hint=(1,1),orientation='vertical')
DownLay=BoxLayout(size_hint=(1,0.9),orientation='vertical')
btn=Button(size_hint=(.4,.1))
img=Image(source="./pic/0.png",allow_stretch=False,size_hint=(.8,.8)) #,pos_hint={'center_x':1,'y':.5})
btn.add_widget(img)
MainLay.add_widget(DownLay) #       1
MainLay.add_widget(btn) #                 2
Clock.schedule_once(lambda _: on_start())
runTouchApp(MainLay)

Jedynie img ma domyślne 100 na 100, więc może dlatego, ale gdy wymuszam jest trochę lepiej, ale zmiana kolejności linii 1 i 2 i dalej się rozjeżdża. Obrazek dowolny.
Dziwnym jest, że numerycznie się zgadza:
center_count 86.4 726.75 <- wyliczone z rozmiarów i pozycji
btn_pos [0, 688.5] <- dolna krawędź butona
btn_size [172.8, 76.5]
btn_center [86.4, 726.75] <- dostarczone przez kivy
img_size [30, 30] < połowa to piętnaście
img_pos [71.4, 711.75] <- 726-15=711

screenshot-20211025233034.png

A tak, gdy na dole:
screenshot-20211025233231.png

0

A tak wygląda kompletne zrąbanie.
screenshot-20211026021426.png
Niby jest na 711 pikseli, a jest koło zera.

0

No to po kolei:

  • Tworzymy FloatLayout, on będzie głównym layoutem, domyślnie zajmie cały ekran, nazywamy go main_layout
  • Tworzymy drugi FloatLayout, nazywamy go button_layout, on będzie trzymał przycisk i obrazek. Ustawiamy mu rozmiar na sztywno (size_hint=(None, None) oraz size=(300, 120)), a pozycję gdzieś w wybranej pozycji wewnątrz rodzica (pos_hint={'center_x': 0.3, 'center_y':0.5}) - można by też ustawić na sztywno (pos=(10, 10))
  • Do button_layout wstawiamy przycisk i obrazek. Domyślnie mają one size_hint=(1, 1), a więc wypełnią całe miejsce w layoucie (300x120 pikseli). Jakby miały size_hint=(0.5, 0.5) to zajęłyby połowę (150x60 pikseli). Jeżeli miałyby size_hint=(None, None) to domyślnie miałyby 100x100 pikseli i trzeba by ręcznie ustawić size (tak jak to zrobiliśmy dla button_layout). Dodatkowo wstawiamy pos_hint={'center_x': 0.5, 'center_y':0.5}, aby zawsze były centrowane na środku layoutu

Razem (zauważ, że nie trzeba tu ustawiać żadnych callbacków w schedule_once):

from kivy.app import runTouchApp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.uix.button import Button

main_layout = FloatLayout()
button_layout = FloatLayout(
    size_hint=(None, None),
    size=(300, 120),
    pos_hint={'center_x': 0.3, 'center_y':0.5},
)
main_layout.add_widget(button_layout)

button_layout.add_widget(Button(pos_hint={'center_x': 0.5, 'center_y':0.5}))
button_layout.add_widget(Image(source="foo.png", pos_hint={'center_x': 0.5, 'center_y':0.5}))

runTouchApp(main_layout)
0

Trochę przerobiłem i wygląda obiecująco. Dałem jednak dla butona: size_hint i ładnie skaluje.

from kivy.app import runTouchApp
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.button import Button

#Window.borderless=True # Można bez ramek włączyć, a ja jeszcze startuję program.py tak: 
# KIVY_DPI=240 KIVY_METRICS_DENSITY=1.5 python program.py --size 432x765

main_layout = FloatLayout()
button_layout = FloatLayout(size_hint=(0.4, 0.1),pos_hint={'left': 0, 'top':1},)
lbl=Label(text="Poźniej coś\ntu będzie",font_size="12sp",size_hint=(0.6,0.1),pos_hint={'right': 1, 'top':1})
main_layout.add_widget(button_layout)
main_layout.add_widget(lbl)
button_layout.add_widget(Button(pos_hint={'center_x': 0.5, 'center_y':0.5}))
button_layout.add_widget(Image(source="./pic/0.png", pos_hint={'center_x': 0.5, 'center_y':0.5}))
runTouchApp(main_layout)

Problem mam jedynie z fontem. Niby coś czytam o sp i wydawało mi się, że powinien się skalować, ale jednak nie.
kivy.metrics tam szukałem.

Dzięki i pozdrawiam.

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