I am currently working on an app for students, which describes various practical experiments.
The app consists of several AccordionItems.
The problem arises with one of them, where I want to embed an image, which keeps its aspect ratio but adapts to be as big as possible. To explain parts of the image, I would like to put semi-transparent buttons on interesting devices/objects that provide information in text form at on_release.
By stretching the app window in x or y direction and the fact that stretching the image is not allowed, there are areas above and below or to the right and left of the window that do not belong to the actual image.
How can scale and position the buttons depending on the current size of the image?
I tried to work with RelativeLayout, but the buttons seem to be oriented to the whole window, which I can't understand.
I also tried to work with ids, but I don't have the understanding to use it effectively.
Here some current code:
class LaserApp(App):
pass
if __name__ == '__main__':
#Config.set('graphics', 'fullscreen', 'auto')
Config.set('graphics', 'fullscreen', '0')
Config.set('graphics', 'window_state', 'windowed')
Config.write()
LaserApp().run()
#:import ScrollEffect kivy.effects.scroll.ScrollEffect
#:import Button kivy.uix.button.Button
Accordion:
AccordionItem:
title: 'titel1'
collapse: False
AccordionItem:
title: 'titel2'
AccordionItem:
title: 'relevant content'
RelativeLayout:
canvas:
Image
size_hint: 1, 1
pos: self.pos
size: self.texture_size
source: 'background.png'
canvas.after:
RelativeLayout:
Button: #Button i want to align and resize depending on Image: / 'background.png'
AccordionItem:
title: 'titel4'
Any comments and help are very welcome.
Many Thanks in advance
P.S.: Please excuse the bad description.
For your problem it sounds like you want something like this?:
_____________
| | | |
_____________
| | | |
_____________
| | | |
_____________
You have to create multiple buttons with:
RelativeLayout:
size_hint: 1,1
pos: self.pos
Button:
background_color: 0,0,0,.25 #rgba only, this makes the button semi-transparent
pos_hint: {'x': .66, 'y': 0}
size_hint: .33, .33
on_release: print/function
Button:
background_color: 0,0,0,.25
pos_hint: {'x': .33, 'y': 0}
size_hint: .33, .33
on_release: print/function
Button:
background_color: 0,0,0,.25
pos_hint: {'x': 0, 'y': 0}
size_hint: .33, .33
on_release: print/function
Button:
background_color: 0,0,0,.25
pos_hint: {'x': .66, 'y': .33}
size_hint: .33, .33
on_release: print/function
Button:
background_color: 0,0,0,.25
pos_hint: {'x': .33, 'y': .33}
size_hint: .33, .33
on_release: print/function
Button:
background_color: 0,0,0,.25
pos_hint: {'x': 0, 'y': .33}
size_hint: .33, .33
on_release: print/function
Button:
background_color: 0,0,0,.25
pos_hint: {'x': .66, 'y': .66}
size_hint: .33, .33
on_release: print/function
Button:
background_color: 0,0,0,.25
pos_hint: {'x': .33, 'y': .66}
size_hint: .33, .33
on_release: print/function
Button:
background_color: 0,0,0,.25
pos_hint: {'x': 0, 'y': .66}
size_hint: .33, .33
on_release: print/function
As long as you have a Layout attached to your image with size_hint: 1,1 and pos: self.pos this will bind the Layout to the same size and position of your image widget. The buttons in the example code should populate tiles that are 1/9 the size of the Layout as 9 are set in increments of .33 if you want more, just add more with the correct size_hint and pos_hint values. If RelativeLayout is an issue try using FloatLayout or BoxLayout, FloatLayout is similar to putting a blank canvas up and you can fill this widget anyway you see fit and BoxLayout functions by filling itself with children vertically or horizontally depending on how you set the orientation:. If this didnt fully answer your question or you need more help please let me know.
Related
Python kivy 2.1.0
In the code a popup opens when you press the button set credentials
Main layout
In the popup I want the user to type details and when they press set credentials I want to dismiss the popup and execute a funcction inside the on_release event of the popup or maybe pass those details to mainLayout somehow. The problem is I think kivy doesn't relate the popup box to the mainLayout. But I need those details in the mainLayout so that so that I can use them there (pressing button or showing the details in the log may be)
pop layout
Here's my .py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
#set screen ratio
from kivy.core.window import Window
Window.size = (480/1.8, 800/1.8)
Builder.load_file('ui.kv')
class FaucetLayout(Widget):
def set_cred(self, cred):
print(cred)
pass
class DollarApp(App):
def build(self):
return FaucetLayout()
if __name__ == '__main__':
DollarApp().run()
Heres my .kv file:
#: import Factory kivy.factory.Factory
<CredPopup#Popup>
auto_dismiss: False
title: "Type your Credentials"
size_hint: 1, .5
pos_hint: {'center_x': .5, 'top': .95}
on_dismiss: root.set_cred({'username':username.text, 'password':password.text})
GridLayout:
cols:1
padding: 10
spacing: 15
size: self.width, self.height
TextInput:
hint_text: "type your username"
hint_text_color: 0, 0, 0, .5
id: username
multiline: False
size_hint: 1, .1
focus: True
TextInput:
hint_text: "type your password"
hint_text_color: 0, 0, 0, .5
id: password
multiline: False
size_hint: 1, .1
Button:
text: "Set Credentials"
size_hint: .4, .1
on_press: root.dismiss()
<FaucetLayout>
GridLayout:
size: root.width, root.height
cols: 1
spacing: 10
Label:
id: faucet_log
text: '[color=000]log will appear here[/color]'
markup: True
background_color: 1, 1, 1, 1
size_hint: .5, None
height: root.height/2
canvas.before:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
orientation: 'vertical'
size: root.width, self.height
padding: 30
spacing: 25
Button:
text: 'Set Credentials'
size_hint: .6, None
height: 40
pos_hint: {'center_x': .5}
on_release: Factory.CredPopup().open()
Button:
text: 'Start'
size_hint: .6, None
height: 40
pos_hint: {'center_x': .5, 'center_y':.9 }
Getting this error:
File "t:\Codes\Kivy\dollar\ui.kv", line 9, in
on_dismiss: root.set_cred({'username':username.text, 'password':password.text}) File "kivy\weakproxy.pyx", line 32, in
kivy.weakproxy.WeakProxy.getattr AttributeError: 'CredPopup'
object has no attribute 'set_cred'
So sorry if I couldn't express myself. I am new to asking questions here. I'm not a pro python programmer, just try to solve some daily personal task with python by following tutorials on the internet.
Got answer from discord.
root refers to the base of the rule it is in, you could use app.root in this case instead to use to refer to the app's root widget .
<CredPopup#Popup>
auto_dismiss: False
title: "Type your Credentials"
size_hint: 1, .5
pos_hint: {'center_x': .5, 'top': .95}
on_dismiss: app.root.set_cred({'username':username.text, 'password':password.text})
I'm struggling to understand layout positioning in Kivy.
My aim is to create a simple form containing 3 rows, each one consisting of a label and a text input.
For example, like on this wireframe drawn with PlantUML, Salt (source):
What I tried
I've tried combining different layouts but cannot achieve the right alignment and I also don't understand what's going wrong.
First attempt:
KV file:
<CustomLabel#Label>:
size_hint: .2, .1
text_size: self.size
<CustomTextField#TextInput>:
size_hint: .2, .1
<CustomFloatLayout#FloatLayout>:
<TestLayout>:
orientation: 'horizontal'
CustomFloatLayout:
CustomLabel:
text: 'Field 1:'
pos_hint: {'x': .1, 'center_y': .95}
CustomTextField:
hint_text: 'hint 1'
pos_hint: {'x': .1, 'center_y': .92}
CustomFloatLayout:
CustomLabel:
text: 'Field 2:'
pos_hint: {'x': .1, 'center_y': .75}
CustomTextField:
hint_text: 'hint 2'
pos_hint: {'x': .1, 'center_y': .72}
CustomFloatLayout:
CustomLabel:
text: 'Field 3:'
pos_hint: {'x': .1, 'center_y': .55}
CustomTextField:
hint_text: 'hint 3'
pos_hint: {'x': .1, 'center_y': .52}
Python code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class TestLayout(BoxLayout):
pass
class TestApp(App):
def build(self):
return TestLayout()
if __name__ == '__main__':
TestApp().run()
Issue
The y-center of the labels is almost at the bottom of the y-center of the text input fields instead of being aligned at the same y-value. And what is even more puzzling: Each row has another offset from the left border of the window.
Second attempt:
KV file:
<CustomLabel#Label>:
size_hint: .2, .1
text_size: self.size
<CustomTextField#TextInput>:
size_hint: .2, .1
<CustomBoxLayout#BoxLayout>:
orientation: 'horizontal'
pos_hint: {'x': .1}
<TestLayout>:
CustomBoxLayout:
CustomLabel:
text: 'Field 1:'
pos_hint: {'x': .1, 'center_y': .95}
CustomTextField:
hint_text: 'hint 1'
pos_hint: {'x': .5, 'center_y': .95}
CustomBoxLayout:
CustomLabel:
text: 'Field 2:'
pos_hint: {'x': .1, 'center_y': .75}
CustomTextField:
hint_text: 'hint 2'
pos_hint: {'x': .5, 'center_y': .75}
CustomBoxLayout:
CustomLabel:
text: 'Field 3:'
pos_hint: {'x': .1, 'center_y': .55}
CustomTextField:
hint_text: 'hint 3'
pos_hint: {'x': .5, 'center_y': .55}
Python code:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
class TestLayout(FloatLayout):
pass
class TestApp(App):
def build(self):
return TestLayout()
if __name__ == '__main__':
TestApp().run()
Issue
Looks better, but label and text input are still not aligned at the same center y value. In addition, if I change the pos_hint of e.g. the second CustomLabel to pos_hint: {'x': .1, 'bottom': .75}, then this label appears at the very bottom of the entire window. I would have expected it to be at the bottom of the current BoxLayout row.
Question
How can I achieve a proper aligning of the labels and the text input fields?
Why are the labels/input fields of the first attempt located on the diagonal of the window?
Why do the labels appear at the bottom of the entire window when
changing their bottom value within the second attempt code?
For your desired layout as Grid (arranging 3 rows with 2 columns) use GridLayout:
GridLayout:
Widgets are arranged in a grid defined by the rows and cols properties.
The order in automatic arrangement of widgets (using add_widget) is controlled by property orientation:
orientation is an OptionProperty and defaults to ‘lr-tb’.
Python code (including widget declaration)
Adjusted from the examples in documentation of GridLayout:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
class TestApp(App):
def build(self):
return layouted_widgets() # replace this by `pass` for trying with KV
def layouted_widgets():
layout = GridLayout(rows=3, cols=2, row_force_default=True, row_default_height=40)
# 1st row
layout.add_widget(Label(text='Label 1'))
layout.add_widget(TextInput(text='Value 1', multiline=False, width=100))
# 2nd row
layout.add_widget(Label(text='Label 2'))
layout.add_widget(TextInput(text='Value 2', multiline=False, width=100))
# 3rd row
layout.add_widget(Label(text='Label 3'))
layout.add_widget(TextInput(text='Value 3', size_hint_x=None, width=100))
return layout
if __name__ == '__main__':
TestApp().run()
See how the 3rd text-input is reduced in size to fit the text-value. This is because of the property size_hint_x=None which overrides the given width=100.
For specific positions inside each cell:
you can not use property pos_hint
for Label which is inherited from Text, same as Button: use valign, halign together with text_size. See the Text-based components' alignment explained in:
Kivy button text alignment issue
for TextInput use padding as explained in Kivy TextInput horizontal and vertical align (centering text)
The original Kivy Guide: Widgets, Organize with Layouts is a bit vague on that.
KV file
Using both, your Custom.. inherited widgets (1st and 2nd row) and the basic widgets (in 3rd row only):
<CustomLabel#Label>:
# to add space from left and top-bottom, effects position x,y
text_size: self.width - dp(20), self.height - dp(10)
halign: 'left'
valign: 'center'
<CustomTextField#TextInput>:
size_hint: self.width - dp(20), self.height - dp(10)
# pos_hint: not supported as expected, thus use padding
# left, right
padding_x: dp(20)
# top, bottom
padding_y: dp(5)
GridLayout:
rows: 3
cols: 2
row_force_default: True
row_default_height:40
CustomLabel:
text: 'Field 1:'
CustomTextField:
hint_text: 'hint 1'
CustomLabel:
text: 'Field 2:'
CustomTextField:
hint_text: 'hint 2'
Label:
text: 'Field 3:'
TextInput:
hint_text: 'hint 3'
Renders the UI in 3 rows and 2 cols - almost all labels and text-inputs left-aligned. Only the 3rd label looks different to demonstrate the alignment-effect on others.
I have a problem that I don't know what command to use to prevent text from going outside the window
Label:
color: (0,1,0,1)
size_hint: (None,None)
pos_hint: {'y': 0.75, 'x': 0.2}
center: self.center
Button:
text: "Powrót"
font_size: 32
pos_hint: {'center_x': .5}
size_hint: (1, 0.1)
on_release: app.root.current = "first"
Thank you in advance for any advice
See the documentation. Try adding:
size: self.texture_size
to the Label.
I'm trying to do a video on top of a graph, with two buttons controlling the graph. I'd like one button to be at the left of the window, the other one at the right, both on the same row, and my problem is placing the - and + buttons.
I have the following kv file, the concerned buttons are the two Control class:
#:kivy 1.0.9
<Control>
canvas:
Rectangle:
size: 5, 5
pos: self.pos
<VideoWidget>:
video_player: video_handler
graph: graph_handler
data_plus: ctr_plus_handler
data_minus: ctr_minus_handler
BoxLayout:
orientation: 'vertical'
VideoHandler:
id: video_handler
state: 'play'
options: {'allow_stretch': True}
size_hint: 1, 0.45
on_touch_down: root.test()
on_position: root.move()
BoxLayout:
size_hint: 1, 0.1
orientation: 'horizontal'
Control:
id: ctr_minus_handler
size_hint: 0.1, 1
pos_hint: {'left': 0.1}
Control:
id: ctr_plus_handler
size_hint: 0.1, 1
pos_hint: {'right': 0.1}
GraphWidget:
id: graph_handler
size_hint: 1, 0.45
But both Control are taking half the width of the row, whatever I change... Any ideas ?
You can use an empty Widget to fill the blank space between the Controls like this:
BoxLayout:
size_hint: 1, 0.1
orientation: 'horizontal'
Control:
id: ctr_minus_handler
size_hint: 0.1, 1
Widget:
size_hint: 0.8, 1
Control:
id: ctr_plus_handler
size_hint: 0.1, 1
You have to use FloatLayout instead of BoxLayout with correct pos_hint values. Below is the snippet for the button widget part:
FloatLayout:
size_hint: 1, 0.1
orientation: 'horizontal'
Control:
id: ctr_minus_handler
size_hint: 0.1, 1
pos_hint: {'left': 1}
Control:
id: ctr_plus_handler
size_hint: 0.1, 1
pos_hint: {'right': 1}
Hope this would solve your issue.
Just started getting into Kivy, facing some alignment issue.
Please have a look on the image below, I am trying to adjust button weather icon marked in red circle at the location where it is marked with blue circle.
Here is the *.kv file code :
BoxLayout:
orientation:'horizontal'
BoxLayout:
orientation:'horizontal'
StackLayout:
orientation:'tb-rl'
canvas:
Color:
rgb: [.3, .320, .380]
Rectangle:
pos: self.pos
size: self.size
Button:
id:current_temperature
text: root.display_current_temperature()
font_size:40
size_hint: [None, None]
size:[200,50]
Button:
id:current_location
text: root.display_location()
font_size:15
size_hint: [None, None]
size:[200,50]
Button:
id:test
text: 'weather icon'
size_hint: [None, None]
size:[100,100]
One way is making use of the kivy.uix.AnchorLayout
Docs: AnchorLayout
BoxLayout:
orientation:'horizontal'
BoxLayout:
size_hint: [.8, 1]
orientation:'horizontal'
StackLayout:
orientation:'tb-rl'
canvas:
Color:
rgb: [.3, .320, .380]
Rectangle:
pos: self.pos
size: self.size
Button:
id:current_temperature
text: root.display_current_temperature()
font_size:40
size_hint: [None, None]
size:[200,50]
Button:
id:current_location
text: root.display_location()
font_size:15
size_hint: [None, None]
size:[200,50]
BoxLayout:
size_hint:[.2, 1]
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
Button:
id:test
text: 'weather icon'
size_hint: [1, None]
Please note that i have changed the size of the last Button Widget from a absolute one to a relative one. This will prevent unexpected behavior on rendering the app on different screen sizes.
Also changed the sizes of the main 2 BoxLayout Widgets to a relative one also, for the same reason as above.
Another way is adding a position hint to the Button Widget
Docs: pos_hint
Button:
pos_hint: {'y': 1-1/(self.parent.height/self.height)}
id:test
text: 'weather icon'
size_hint: [None, None]
size:[100,100]