Alright, my question is "How can I position a Label in Kivy?" like I do know, that in Tkinter we have a "Place" attribute in Labels. But, I am a new Kivy learner so I can't figure it out the placement of a kivy label in a kivy window. Here's a simple program I built.
import kivy
from kivy.app import App
from kivy.uix.label import Label
class MyApp(App):
def build(self):
return Label(text = "Hey there! I am a Label! [Place Me!]")
MyApp().run()
According to the official kivy documentation about widget positioning, there's 2 ways to do it:
Using the pos argument to assign widget's position by pixels, similar to the x and y argument from tkinter:
def build(self):
return Label(text = "Hey there! I am a Label! [Place Me!]",
pos = (200, 300),
)
Using the pos_hint argument to assign widget's position as a fraction between 0.0 and 1.0 of the height and width of the window/parent widget, similar to the relx and rely argument from tkinter:
def build(self):
return Label(text = "Hey there! I am a Label! [Place Me!]",
pos_hint = (0.5, 0.35),
)
Keep in mind that though it's pretty similar to how it works in tkinter, the kivy's widgets are positioned based from the left and the bottom side of the window, different from the left and top fashion of tkinter, for example relx=0.5, rely=0.3 would be at the same place as pos_hint=(0.5,0.7).
Related
I'm stuck trying to position widgets in kivy in a FloatLayout using pos_hint.
If the label exists from the beginning, e.g. if I can define the pos_hint in the .kv file, everything works as expected. However, I'm trying to create buttons later on. Using this .kv file (named layouttest.kv):
<NewButton#Button>:
size_hint: (0.1, 0.1)
<BasicFloatLayout#FloatLayout>:
Button:
size_hint: (0.4, 0.2)
pos_hint: {'x': 0.0, 'top': 1.0}
text: 'create Button'
on_release: self.parent.create_Button()
and this python code, I am trying to position newly created blank buttons at a random y-position ranging from 0%-100% of the size of my BasicFloatLayout, and at a random x-position ranging from 0-200px.
If I press the button once, everything behaves as expected. On a second press, the first created button will change its y-position such that it is identical with the newly created button. On a third press, both old buttons will align with the newly created button and so on. The x-positioning will however remain as expected. Can someone please explain what I'm doing wrong here?
(Bonus points if you can help me moving the buttons using the update function and pos_hint)
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.properties import NumericProperty
from kivy.clock import Clock
import random
class BasicFloatLayout(FloatLayout):
timer = NumericProperty(0)
def update(self, *args):
self.timer += 1
for child in self.children:
try: child.update()
except AttributeError:
pass
def create_Button(self):
button = NewButton( (random.random(), random.random()) )
self.add_widget(button)
class NewButton(Button):
def __init__(self, pos, **kwargs):
super(NewButton, self).__init__(**kwargs)
self.pos[0] = 200*pos[0]
self.pos_hint['y'] = pos[1]
class layouttestApp(App):
def build(self):
GUI = BasicFloatLayout()
Clock.schedule_interval(GUI.update, 1/30.)
return GUI
if __name__ == "__main__":
layouttestApp().run()
First, make pos_hint: {'x': 0.0, 'y': 0.5}(because it's hard to get 2 different things work, if you are using x, use y instead of Top, if you are using Top, then use Bottom insead of x)
Second, instead of giving on_release: self.parent.create_Button() in the kv file, do this: on_release: root.create_Button()
Third, you have only assigned for the y value , you should also assign for the x value, the line is self.pos_hint['y'] = pos[1] inside the NewButton class.
But you can make it more simple by doing this:
#from your first class......
def create_Button(self):
button = NewButton(self.pos_hint{'x' : random.random(), 'y' : random.random()}
self.add_widget(button)
class NewButton(Button):
def __init__(self, *kwargs):
pass
Hope this makes some kind of sense, and you can modify it more.
(Note: I haven't wrote the begining part of your main class, I am lazy ;-p)
I want to scale an image in Widget using size_hint, but it doesn't work. What did I do wrong?
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
class Game(Widget):
def __init__(self):
super().__init__(size=[1280, 960])
self.add_widget(Image(source='img.jpg', size_hint=(1, 1)))
class GameApp(App):
def build(self):
gm = Game()
return gm
GameApp().run()
As a result I see a black window with a 100x100 image.
The problem is that you are adding your Image to a class that extends Widget. The Widget class is not intended as a container, and does not handle things like size_hint. So, I suggest changing Game to extend some type of container, like FloatLayout. Here is your code with that change:
class Game(FloatLayout):
def __init__(self):
super().__init__(size=[1280, 960])
self.add_widget(Image(source='img.jpg', size_hint=(1,1), allow_stretch=True))
You may need the allow_stretch=True to allow the Image to stretch the source.
Question
Why size_hint doesn't work for Widget?
Answer
The keyword, size_hint that you have provided, is not applying to the
Widget. It is applying to the Image object. The base class of Image widget is a Widget. The default size of a widget is (100, 100) and default size_hint is (1, 1). Therefore, by providing size_hint=(1, 1) when instantiating an Image widget, you are not changing the default size_hint or size.
When you provided size=[1280, 960] to the super constructor, you have already overrode the default size of the widget of (100, 100) or default size_hint of (1, 1)
Solution
If you add pos=self.pos, size=self.size, allow_stretch=True) to Image, you will get an image displayed.
But there is still a black strip at the bottom.
Snippets
self.add_widget(Image(source='Jupiter.png', pos=self.pos, size=self.size, allow_stretch=True))
Recommended Solution.
You can replace Image object by doing the following:
Use the Widget's canvas to display an image in a Rectangle
Bind the Widget's pos and size to a method to redraw the image
Add import statement, from kivy.graphics import Rectangle
Snippets
from kivy.graphics import Rectangle
class Game(Widget):
def __init__(self):
super().__init__(size=[1280, 960])
with self.canvas:
self.rect = Rectangle(source="Jupiter.png", pos=self.pos, size=self.size)
self.bind(pos=self.redraw, size=self.redraw)
def redraw(self, *args):
self.rect.size = self.size
self.rect.pos = self.pos
Output
I am trying to make a slider using a button and when pressed reveals two other buttons but does not change colors when pressed solely for aesthetics. I am very comfortable with the .kv language and know you can use
background_color: (1,0,0,1) if self.state == 'normal' else (0,1,0,1)
in the .kv portion of the script and have tried variations of this and using if self.state == 'down': and others such as that in python. However I am trying to learn the dynamic aspect of kivy now and am just starting to play around with animations and would like to be able to do this without using a .kv file or Builder.
The goal is to have two buttons that change background colors beneath a button that simply slides on_press. My issue is that the sliding button is changing colors when I want the background color to remain static and the buttons underneath are not highlighting on_press if I adjust their background colors. I am mainly looking for help with the sliding button remaining one color and am not too concerned if the buttons beneath must remain default. If anyone could offer some help I would greatly appreciate it.
from kivy.animation import Animation
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import ButtonBehavior
global active
active = True
class TestApp(App):
def plop_slide_animation(self, instance):
global active
if active == True:
plop_slide = Animation(pos=(250, 350), duration=0.5)
active = False
elif active == False:
plop_slide = Animation(pos=(250, 200), duration=0.5)
active = True
plop_slide.start(instance)
def ploppi_press(self,instance):
print('ploppi')
def plopper_press(self,instance):
print('plopper')
def build(self):
button1 = Button(background_color = (1,.6,1,1), size_hint=(.2, .2), pos=(250, 200), text='PLOP', on_press=self.plop_slide_animation)
button3 = Button(background_color = (128,0,0,.5), size_hint=(.09, .09), pos=(260, 250), text='ploppi', on_press=self.ploppi_press) #background_color = (128,0,0,.5),
button4 = Button(background_color = (0,0,255,.5), size_hint=(.09, .09), pos=(450, 250), text='plopper', on_press=self.plopper_press) #background_color = (0,0,255,.5),
layout = FloatLayout()
def change_text(button):
global active
if button1.state == 'down':
print(button1.state)
if active == True:
button1.text = 'PLOOOOP'
print('PLOOOOP')
if active == False:
button1.text = 'PLOP'
print('PLOP')
button1.bind(on_press=change_text)
layout.add_widget(button3)
layout.add_widget(button4)
layout.add_widget(button1)
return layout
if __name__ == '__main__':
TestApp().run()```
Assing empty string to button's images in different states
button1.background_down = ''
button1.background_normal = ''
and you get one color all the time.
But it will be little different color because it will show real color, not dim color.
Doc: Button
EDIT: You can also assign one image to another and it will use the same image for both states - and you get the same dim color.
button1.background_down = button1.background_normal
again! I'm trying to add a character counter to my TextInput widget, but I don't know what parameters to pass for the four arguments, or much on how they're supposed to function. I checked the documentation, but it was putting me further into the woods. Anyway, here are the relevant snippets.
def charsLeft(window, keycode, text, modifiers):
# Do some magic, pass some parameters, and then...
ansLen.text = str(len(hidden.text) - len(answer.text))
And here's the code for my layout:
ansLen = Label(bold=True, halign="center", size_hint=(.2, .5), text_size=self.size, valign="middle")
answer = TextInput(id="sbt", multiline=False, size_hint=(.8, .5), text="")
answer.bind(keyboard_on_key_down=charsLeft)
I figure since it's on virtually every website, it ought to be fairly straightforward. I just don't know what I don't know here.
if you want to set a text counter you do not need to use keyboard_on_key_down, you just need to catch the text change for them we use bind, then we can use a lambda function to update the values since the bind returns the instance and the property changed, to set the value we use setattr:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
class MyApp(App):
def build(self):
layout = BoxLayout(orientation='vertical')
answer = TextInput(multiline=False, text="", size_hint=(1, 0.5))
ansLen = Label(bold=True, halign="center", text="", size_hint=(1, 0.5))
answer.bind(text=lambda instance, text: setattr(ansLen, "text", str(len(text))))
layout.add_widget(answer)
layout.add_widget(ansLen)
return layout
if __name__ == '__main__':
MyApp().run()
In Kivy:
from kivy.app import App
from kivy.uix.label import Label
class TestApp(App):
def build(self):
label = Label(text="TEST")
return label
TestApp().run()
My label is centred in the window:
How can I instead anchor my label to the bottom right corner of the window?
You'd think
label.halign = 'right'
label.valign = 'bottom'
would do the trick, but as the Label documentation points out,
The valign property will have no effect and halign will only have an effect if your text has newlines; a single line of text will appear to be centered even though halign is set to left (by default).
It looks like adding the label to an AnchorLayout, then shrinking the size of the label relative to its parent widget, together achieves what I want.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.anchorlayout import AnchorLayout
class TestApp(App):
def build(self):
anchor_layout = AnchorLayout(anchor_x='right', anchor_y='bottom')
label = Label(text="TEST")
label.size_hint = (0.1, 0.1)
anchor_layout.add_widget(label)
return anchor_layout
TestApp().run()
Produces:
Set the Label's text_size to its size, e.g. in kv text_size: self.size. The text_size controls the bounding box within which text is wrapped.