I tried to make an kivy program wit two images , which are supposed to change when I click on them.
But when I tred to add two Widgets with the containing Images to an BoxLayout, I got one image at the position 0, 0
Why dosen't the BoxLayout work with my images?
Here is my code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.properties import NumericProperty
from kivy.properties import StringProperty
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class Feld(Widget):
droga = StringProperty('hinterg.png')
kr = StringProperty('kreuz.png')
ks = StringProperty('kreis.png')
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.droga = self.kr
root = Builder.load_string('''
BoxLayout:
orientation: 'horizontal'
Feld:
id: a1
Feld:
id: a2
<Feld>:
Image:
source: root.droga
''')
class app(App):
def build(self):
Window.clearcolor = (0, 0.54, 1, 1)
return root
if __name__ == "__main__":
app().run()
Your Feld instances are positioned by the BoxLayout, but the image is displayed in an Image widget that is a child of the Feld. Since Feld is just a normal Widget, it doesn't impose any automatic position or size on its children, so both actual images have the default size of (100, 100) and pos of (0, 0).
The best solution is to make Feld a subclass of some layout that will make its child fill itself by default, such as a FloatLayout.
Related
My app (using kivy) needs to substitute text with widgets inside an unspecified number of buttons using iteration. I don't use kv language. Only the last button displays any content.
My app should display an unspecified number of buttons. Simplified example:
import kivy
kivy.require('2.0.0')
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.image import Image
class Layout(Widget):
def __init__(self, app):
super().__init__()
self.layout = BoxLayout(orientation='vertical', size=Window.size)
contents = ['text1', 'text2', 'text3']
for text in contents:
button = Button(height=300, size_hint=(1, None))
button.text = text
self.layout.add_widget(button)
self.add_widget(self.layout)
class MyApp(App):
def build(self):
self.form = Layout(self)
return self.form
def on_pause(self):
return True
if __name__ in ('__main__', '__android__'):
MyApp().run()
Only instead of text buttons display images with descriptions. I change the code:
import kivy
kivy.require('2.0.0')
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.image import Image
class Layout(Widget):
def __init__(self, app):
super().__init__()
self.layout = BoxLayout(orientation='vertical', size=Window.size)
contents = [('img1', 'text1'), ('img2', 'text2'), ('img3', 'text3')]
for img, text in contents:
button = Button(height=300, size_hint=(1, None))
button_layout = BoxLayout(orientation='vertical', size_hint=(1, 1))
label = Label(text=text)
image = Image(source=f'{img}.jpg', size_hint=(1, 1), allow_stretch=True)
button_layout.add_widget(image)
button_layout.add_widget(label)
button.add_widget(button_layout)
self.layout.add_widget(button)
self.add_widget(self.layout)
class MyApp(App):
def build(self):
self.form = Layout(self)
return self.form
def on_pause(self):
return True
if __name__ in ('__main__', '__android__'):
MyApp().run()
And only the last button displays content.
As far as I can understand, at some point Python automatically gets read of the content of the previous button. How can I prevent it? Also what is the best way to center it?
I'm not a native speaker, so sorry for bad grammar. Thank you in advance.
I'm making an app in python kivy, and I have 2 draggable Images in my "TacScreen" and whenever I double tap on either of them It opens up a popup, inside the popup I have a Textinput field where I can input text. Whatever text I put in there I want that to show inside a Label below my Draggable Image. The problem that I'm facing now is when I type anything in the text input field and press the close button (you can see it below in my code) It closes the popup and I don't see any text. How can I fix this? Below is my code!
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.clock import Clock
from functools import partial
from kivy.lang import Builder
from kivy.uix.behaviors import DragBehavior
from kivy.uix.image import Image
from kivy.vector import Vector
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
class DragImage(DragBehavior, Image):
def on_touch_up(self, touch):
uid = self._get_uid()
if uid in touch.ud:
print(self.source, 'dropped at', touch.x, touch.y)
return super(DragImage, self).on_touch_up(touch)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and touch.is_double_tap:
print('this is a double tap')
self.layout = GridLayout(cols=1, row_force_default=True,
row_default_height=30,
spacing=80)
self.layout2 = GridLayout(cols=1,
row_force_default=True,
row_default_height=30,
spacing=80)
self.popuptextinput = TextInput(hint_text='Person Name',
multiline=False,
background_color='red',
halign='left',
cursor_blink=True,
cursor_color='white',
foreground_color='white',
font_size='14')
self.closebutton = Button(text="SAVE",
background_color="black",
font_size="14",
font_name='font1.otf')
self.output = Label(text='',)
self.layout.add_widget(self.popuptextinput)
self.layout.add_widget(self.closebutton)
self.layout2.add_widget(self.output)
self.popup = Popup(title='PERSON',
title_color="white",
separator_color="black",
title_align="center",
separator_height="0dp",
background_color="black",
title_font="font.otf",
title_size="12",
content=self.layout,
size_hint_y=.4,
size_hint_x=.5)
self.popup.open()
self.closebutton.bind(on_release=self.updttext)
return self.layout2
return super(DragImage, self).on_touch_down(touch)
def updttext(self, other):
self.output.text = self.popuptextinput.text
self.popup.dismiss()
class StartScreen(Screen):
pass
class TacScreen(Screen):
pass
class MyApp(App):
def build(self):
return Builder.load_string(kv)
MyApp().run()
my .kv file
kv = '''
<DragImage>:
drag_rectangle: self.center[0] - self.norm_image_size[0]/6,
self.center[1] -
self.norm_image_size[1]/6, \
self.norm_image_size[0]/3, self.norm_image_size[1]/3
drag_timeout: 10000000
drag_distance: 0
<TacScreen>:
#:import utils kivy.utils
DragImage
id: person1
pos: 0, 102
size_hint: 1, .1
source: "person1.png"
DragImage:
id: person2
pos: 50, 160
size_hint: 1, .1
source: "person2.png"
...
I'm learning how to use Kivy, and I would like to know how can I align the labels inside the listview I built.
The labels are centered by default, and I would like to align all the labels to the left.
My code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.listview import SimpleListAdapter
from kivy.uix.listview import ListView
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
class TestApp(App):
def build(self):
messages = ["a", "b"]
layout = BoxLayout(orientation='vertical')
btn1 = Button(text='Hello')
textinput = TextInput(text='Hello world', size_hint=(1, 0.1))
messages.append("sd")
simple_list_adapter = SimpleListAdapter(
data=messages,
cls=Label)
simple_list_adapter.cls
list_view = ListView(adapter=simple_list_adapter)
layout.add_widget(list_view)
layout.add_widget(textinput)
return layout
TestApp().run()
A simple solution is to create a Label with that desired alignment
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.listview import SimpleListAdapter
from kivy.uix.listview import ListView
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
class LeftLabel(Label):
def __init__(self, **kwargs):
super(LeftLabel, self).__init__(**kwargs)
# https://kivy.org/doc/stable/api-kivy.uix.label.html#text-alignment-and-wrapping
self.halign = "left"
self.bind(size=self.setter("text_size"))
class TestApp(App):
def build(self):
messages = ["a", "b"]
layout = BoxLayout(orientation="vertical")
btn1 = Button(text="Hello")
textinput = TextInput(text="Hello world", size_hint=(1, 0.1))
messages.append("sd")
simple_list_adapter = SimpleListAdapter(data=messages, cls=LeftLabel)
list_view = ListView(adapter=simple_list_adapter)
layout.add_widget(list_view)
layout.add_widget(textinput)
return layout
TestApp().run()
Since you are starting in kivy it is recommended that you learn RecycleView instead of ListView since the latter is deprecated:
Deprecated since version 1.10.0: ListView has been deprecated, use RecycleView instead.
I'm working in an app with kivy and I have an issue that involve the GridLayout. I have a screen with different rows and I want the buttons of the last row to have always the same height (11,1% of the height of the Screen). I have tried to modify the attribute height in the buttons but doesn't work properly. With size_hint_y works fine , but the fact is that i want to do with height because the screen won't have always the same number of rows (is responsive and it depends of the selections of previous screens). I attach here the code that I've done with the attribute height calculated through the command Window.height/9:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
class LoginScreen(GridLayout):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols=2
self.add_widget(Label(text='Subject'))
self.add_widget(Label(text=''))
self.add_widget(Label(text='1'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='2'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='3'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='4'))
self.add_widget(TextInput(multiline=False))
b1=Button(text='Exit',background_color=[0,1,0,1],height=int(Window.height)/9.0) #doesn't work properly
self.add_widget(b1)
b2=Button(text='Run',background_color=[0,1,0,1],height=int(Window.height)/9.0) #doesn't work properly
self.add_widget(b2)
b1.bind(on_press=exit)
class SimpleKivy(App):
def build(self):
return LoginScreen()
if __name__=='__main__':
SimpleKivy().run()
I know it could be done with kivy language in a easier way but for my app is better to do in this way. If anyone knows how to fix this problem I would be very grateful.
If you want a widget in a grid/box layout to have a fixed size, you should set its size_hint to None first. And always use kivy lang at such tasks - no exceptions.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
gui = '''
LoginScreen:
GridLayout:
cols: 2
Label:
text: 'Subject'
Label:
Label:
text: '1'
SingleLineTextInput:
Label:
text: '2'
SingleLineTextInput:
Label:
text: '3'
SingleLineTextInput:
Label:
text: '4'
SingleLineTextInput:
GreenButton:
text: 'Exit'
on_press: app.stop()
GreenButton:
text: 'Run'
<SingleLineTextInput#TextInput>:
multiline: False
<GreenButton#Button>:
background_color: 0, 1, 0, 1
size_hint_y: None
height: self.parent.height * 0.111
'''
class LoginScreen(Screen):
pass
class SimpleKivy(App):
def build(self):
return Builder.load_string(gui)
if __name__ == '__main__':
SimpleKivy().run()
Try this
class LoginScreen(GridLayout):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols=2
self.add_widget(Label(text='Subject'))
self.add_widget(Label(text=''))
self.add_widget(Label(text='1'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='2'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='3'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='4'))
self.add_widget(TextInput(multiline=False))
b1=Button(text='Exit',background_color=[0,1,0,1],size_hint_y=None, height=int(Window.height)/8.9)
self.add_widget(b1)
b2=Button(text='Run',background_color=[0,1,0,1],size_hint_y=None, height=int(Window.height)/8.9)
self.add_widget(b2)
b1.bind(on_press=exit)
Edited to change it to be at 11%.
And here one that keeps the button at 11% in response to the window size, where you redraw the Grid Layer whenever the window is resized (as by the bind to 'on_resize').
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
class LoginScreen(GridLayout):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
#init and add grid layer
self.cols=2
self.layout = GridLayout(cols=self.cols)
self.add_widget(self.layout)
#function to set the buttons based on the current window size
self.set_content(Window.width, Window.height)
#bind above function to get called whenever the window resizes
Window.bind(on_resize=self.set_content)
def set_content(self, width, height, *args):
#first remove the old sized grid layer
self.remove_widget(self.layout)
#now build a new grid layer with the current size
self.layout =GridLayout(cols=self.cols)
self.layout.add_widget(Label(text='Subject'))
self.layout.add_widget(Label(text=''))
self.layout.add_widget(Label(text='1'))
self.layout.add_widget(TextInput(multiline=False))
self.layout.add_widget(Label(text='2'))
self.layout.add_widget(TextInput(multiline=False))
self.layout.add_widget(Label(text='3'))
self.layout.add_widget(TextInput(multiline=False))
self.layout.add_widget(Label(text='4'))
self.layout.add_widget(TextInput(multiline=False))
b1=Button(text='Exit',background_color=[0,1,0,1],size_hint_y=None, height=int(Window.height)/8.9)
self.layout.add_widget(b1)
b2=Button(text='Run',background_color=[0,1,0,1],size_hint_y=None, height=int(Window.height)/8.9)
self.layout.add_widget(b2)
b1.bind(on_press=exit)
#add the newly sized layer
self.add_widget(self.layout)
class SimpleKivy(App):
def build(self):
return LoginScreen()
if __name__=='__main__':
SimpleKivy().run()
I just start Kivy programming and have a problem with the understanding of doing a layout:
import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
class CornerRectangleWidget(Widget):
def __init__(self, **kwargs):
super(CornerRectangleWidget, self).__init__(**kwargs)
with self.canvas:
Color(1, 0, 1, 1)
self.rect = Rectangle(size_hint=(1,None),height=48)
class ControllerApp(App):
def build(self):
Window.clearcolor = (1, 0, 0, 1)
root = FloatLayout(size_hint=(1,1))
root.add_widget(CornerRectangleWidget())
return root
if __name__ == '__main__':
ControllerApp().run()
Why is my CornerRectangleWidget keeping so small. I wanted to have a FloatLayout with Fullscreen and the CornerRectangleWidget too.
Actually I want to build the Widget "CornerRectangleWidget" to a floating top Toolbar with the x dimensins of the root Widget "FloatLayout size_hint=(1,None)"! How to do that?
Rectangle doesn't have a size_hint, you need to set a pos and size.
You'll also need to bind to a function to update its position when the widget position changes, as during the __init__ it will have the default pos of (0, 0) and size of (100, 100). This is described here.