kivy layout: different computer different display - python

The same code for python layout returns different GUI. I'm terribly confused:
# ---------- VQCIA.kv ----------
VQCIA:
<VQCIA>:
orientation: "vertical"
goi: goi
padding: 10
spacing: 10
size: 400, 200
pos: 200, 200
size_hint:None,None
BoxLayout:
Label:
text: "Enter gene of interest with TAIR ID:"
font_size: '25sp'
BoxLayout:
TextInput:
hint_text: 'AT3G20770'
multiline: False
font_size: '25sp'
id: goi
BoxLayout:
Button:
text: "Submit"
size_hint_x: 15
on_press: root.submit_goi()
# ---------- vqcia.py ----------
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
class VQCIA(BoxLayout):
# Connects the value in the TextInput widget to these
# fields
goi = ObjectProperty()
def submit_goi(self):
# Get the student name from the TextInputs
goi = self.goi.text
print goi
return
class VQCIAApp(App):
def build(self):
return VQCIA()
dbApp = VQCIAApp()
dbApp.run()
my lab computer is macOS Sierra 10.12.6 with Kivy==1.10.1 and has ideal output:
on the other side my personal mac, macOS high Sierra 10.13.6 with Kivy==1.10.1, has wrong outputs:
what happens?

Try using Density-independent Pixels, dp.
In kv file, there are two roots. There is a root rule, VQCIA: and also a class rule, <VQCIA>: for the root. Since in Python code, it is using return VQCIA(), which is associated to class rule, <VQCIA>: in kv file. Therefore, remove root rule, VQCIA: to avoid confusion.
kv file - vqcia.kv
<VQCIA>: # class rule for root widget
orientation: "vertical"
goi: goi
padding: dp(10)
spacing: dp(10)
size: dp(400), dp(200)
pos: dp(200), dp(200)
size_hint: None, None
Dimensions
dp
Density-independent Pixels - An abstract unit that is based on the
physical density of the screen. With a density of 1, 1dp is equal to
1px. When running on a higher density screen, the number of pixels
used to draw 1dp is scaled up a factor appropriate to the screen’s
dpi, and the inverse for a lower dpi. The ratio of dp-to-pixels will
change with the screen density, but not necessarily in direct
proportion. Using the dp unit is a simple solution to making the view
dimensions in your layout resize properly for different screen
densities. In others words, it provides consistency for the real-world
size of your UI across different devices.
sp
Scale-independent Pixels - This is like the dp unit, but it is also
scaled by the user’s font size preference. We recommend you use this
unit when specifying font sizes, so the font size will be adjusted to
both the screen density and the user’s preference.

Related

how can i use floatLayout inside stacklayout

I am making an app in which, on one screen, I want buttons stacked along the right edge of the screen(for which I need stack layout) and 2 buttons at the centre of the screen(for this I want to use float layout). I have searched for it but nowhere I can see any examples of using two different layouts on one screen.
Can we use two different layouts on a screen? if yes how can we do that?
hers a sample code-
from kivy.uix.stacklayout import StackLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class screen_1(Screen,Stacklayout): ''' here I tried to inherit
floatlayout, but i guess it
doesnt work that way'''
pass
class main(App):
def build(self):
return screen_1()
m = main()
m.run()
kivy code-
<screen_1>:
StackLayout:
orientation: 'tb-rl'
spacing: 10
padding: 90
TextInput:
text: "write your word here"
color: 1,1,1,1
id: word_input
width: 300
size_hint: None, .10
stackLayout:
orientation: 'rl-tb'
spacing: 10
padding: 90
TextInput:
text: "write your word here"
color: 1,1,1,1
Layouts, lake all widgets, can be nested.
If you want two layouts in your screen, you just put them in an appropriate layout to manage their respective size/positions.
First, you likely don't want to do multiple-inheritance with widgets, at least not inheriting from multiple widgets (inheriting from one widget and one or multiple other objects can be fine, but inheriting from multiple widgets will cause issues). Also, Screen is already a RelativeLayout[1] which is almost the same as a FloatLayout (only it makes the pos coordinates of children relative to its own).
Instead, what you want is to compose (by nesting).
Screen:
StackLayout:
size_hint: .5, 1 # let's take half of the screen in width, and all of it in height
# whatever you want inside this layout
BoxLayout:
size_hint: .5, 1 # same, half of the width, all of the height
pos_hint: {'right': 1} # stick to the right of the screen
Button:
[1] https://kivy.org/docs/api-kivy.uix.screenmanager.html?highlight=screen#kivy.uix.screenmanager.Screen
[2] https://kivy.org/docs/api-kivy.uix.relativelayout.html#kivy.uix.relativelayout.RelativeLayout

Scroll contents of GridLayout in ScrollView - Kivy

I will say first off I have tried every single example on the web involving kv lang. Not once have I had any success.
The idea is pretty simple: As I swipe up/down/scroll the contents of GridLayout() within ScrollView() are scrolled up or down.
The best I have been able to do is have the scroll bar fade into view when running the program. Not able to scroll unfortunately.
<Root>
grid_layout: grid_layout
ScreenManager:
...
Screen:
...
ScrollView:
GridLayout:
id: grid_layout
size_hint_y: None
cols: 1
height: self.minimum_height
<list of buttons>
Binding minimum_heightin the __init__ method of the root class (RelativeLayout):
grid_layout = ObjectProperty(None)
self.grid_layout.bind(minimum_height=self.grid_layout.setter('height'))
I have followed https://github.com/kivy/kivy/blob/master/examples/widgets/scrollview.py converting it to kv lang - scroll bar visible, unable to scroll. Also tried every example on Google Groups and here related to using kv lang. Still no scroll :\
Compiling using buildozer and running on Android fails for an unknown reason.
I would appreciate any assistance that can be given.. I am completely clueless at this point
This:
height: self.minimum_height
should be:
minimum_height: self.height
This is unnecessary:
grid_layout = ObjectProperty(None)
self.grid_layout.bind(minimum_height=self.grid_layout.setter('height'))
It also won't scroll unless the contents are larger than the scrollview's height:
Full code:
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
Builder.load_string('''
<Root>:
ScrollView:
size_hint: 1, .1
# setting the width of the scrollbar to 50pixels
bar_width: 50
# setting the color of the active bar using rgba
bar_color: 5, 10, 15, .8
# setting the color of the inactive bar using rgba
bar_inactive_color: 5, 20, 10, .5
# setting the content only to scroll via bar, not content
scroll_type: ['bars']
GridLayout:
size_hint_y: None
cols: 1
minimum_height: self.height
Button
text: 'one'
Button:
text: 'two'
Button:
text: 'three'
Button:
text: 'four'
''')
class Root(FloatLayout):
pass
class DemoApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DemoApp().run()
Being unable to scroll was due to a misunderstanding of Kivy's touch handlers. Completely unrelated to the code mentioned in my question.
The key is to have GridLayout be larger than ScrollView, so GridLayout can be panned within ScrollView.
For those wanting to use ScrollView inside ScreenManager using kvlang only:
ScreenManager:
id: screen_manager
Screen:
manager: screen_manager
id: main_screen
name: 'main'
ScrollView:
bar_width: 4
# pos_hint defaults to 1,1 so no need to declare it
GridLayout:
size_hint_y: None
cols: 1
# you do not need to manually bind to setter('height') in
# python - perfectly possible with kv lang
# this allows for height to update depending on the
# collective heights of its child widgets
height: self.minimum_height
<----- widgets here ----->
# for scroll to show/work there must be more widgets
# then can fit root.height. If not there is no need
# for scrollview :)

What is a good way to handle widget placement to account for screen rotation using python / kivy?

When I build an application using kivy, I can get everything where I want it, but when I rotate the screen from portrait to landscape, my widgets start colliding with one another. What is a good way to prevent this from happening?
In the attached example, I was able to bind the placement of a settings button to my header label widget, but I was unsuccessful in getting my scrollview to bind to it so when it rotates it stays X amount of space away from the bottom of the label.
Python:
class First_Screen(Screen):
def __init__(self, **kwargs):
super(First_Screen, self).__init__(**kwargs)
list_of_buttons = [
'button1',
'button2',
'button3',
'button4',
'button5',
'button6 ',
'button7',
'button8',
]
class My_Grid(FloatLayout, ScrollView):
grid = ObjectProperty(None)
def on_grid(self, *args):
for btn in list_of_buttons:
btn = Button(
text = btn,
)
btn.bind(on_press= First_Screen.print_message)
self.grid.add_widget(btn)
def print_message(self):
print ('Congratulations! You have clicked a button!')
class ScreenManagement(ScreenManager):
pass
class Stack_Overflow(App):
def build(self):
sm = ScreenManager(transition = FadeTransition())
sc0 = First_Screen(name = 'first_screen')
sm.add_widget(sc0)
return sm
if __name__ == '__main__':
Stack_Overflow().run()
kv:
ScreenManagement:
First_Screen:
<ImageButton#ButtonBehavior+Image>:
<First_Screen>:
name: 'first_screen'
Label:
id: header
text: 'header'
pos_hint: ({'left' : 1, 'top' : 1})
size_hint: (1,None)
height: dp(50)
canvas.before:
Color:
rgba: (.6, .6, .6, 1)
Rectangle:
pos: self.pos
size: self.size
ScrollView:
size_hint: (1,.8)
pos_hint: ({'center_x' : .5, 'center_y' : .5})
My_Grid:
grid: grid
GridLayout:
id: grid
cols: 1
size_hint_y: None
row_default_height: "40dp"
height: self.minimum_height
ImageButton:
id: settings
size_hint: None, None
height: dp(30)
width: dp(30)
pos: header.x, header.y + 10
pos_hint: {'right': 1}
source: 'settings_black.png'
Images: portrait, landscape
Thanks!
Edit: I misunderstood the question, I thought you were unsatisfied with the Image. In the case of ScrollView the problem isn't the ScrollView itself, because that uses relative positions and sizes.
The problem is the Label(header), which has height set as an absolute number i.e. 50 dense pixels. It means that whatever screen you have or however you size anything else, this widget will have 50 pixels corrected with dpi of the screen.
In reality this means that if your screen reaches 400x100 resolution, your Label will take half of the screen and ScrollView will be placed on top of that (most likely you won't see the Label then).
To fix that you have two options. If you want to play with FloatLayout alone, you'll need to use correct positioning/sizing i.e. relative and make sure widgets aren't overlaping.
Otherwise just drop the widgets to BoxLayout or a similar other layout that manages such things on its own:
class First_Screen(BoxLayout, Screen):
def __init__(self, **kwargs):
super(First_Screen, self).__init__(orientation='vertical', **kwargs)
or a little bit different (and better looking):
class First_Screen(BoxLayout, Screen):
def __init__(self, **kwargs):
and in kv:
<First_Screen>:
name: 'first_screen'
orientation: 'vertical'
Also note, that there's a screen module, which makes testing such things a way more easier than dropping the code to apk and to android when it's just unnecessary. Example:
python main.py -m screen:note2,landscape,scale=.5

Kivy TextInput horizontal and vertical align (centering text)

How to center a text horizontally in a TextInput in Kivy?
I have the following screen:
But I want to centralize my text like this:
And this is part of my kv language:
BoxLayout:
orientation: 'vertical'
Label:
markup: True
text: '[b] Type something... [/b]'
size_hint: 1, 0.6
size: self.parent.size[0], 200
font_size: self.size[0] * 0.1
text_size: self.size
halign: 'center'
valign: 'middle'
canvas.before:
Color:
rgb: 0, 0, 204
Rectangle:
pos: self.pos
size: self.size
TextInput:
focus: True
How can I center my TextInput's text?
Afaik, there's no such thing as aligning in the same way as it's in Label, however, you can use padding to push the position wherever you want. Keep in mind that changing the size of text will affect the centering, therefore you'll need to recalculate on change of size (e.g. when working with multiple devices, sizes, etc).
Or there could be even a workaround, where you could make TextInput invisible, use Label to fetch touch event to trigger TextInput (which would open keyboard) and change Label's text on change of TextInput's text property. You'll lose the possibility to work with cursor this way and you'll need to handle wrapping text.
Example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_string('''
<Test>:
TextInput:
text: 't'
font_size: 60
# left, right
padding_x:
[self.center[0] - self._get_text_width(max(self._lines, key=len), self.tab_width, self._label_cached) / 2.0,
0] if self.text else [self.center[0], 0]
# top, bottom
padding_y: [self.height / 2.0 - (self.line_height / 2.0) * len(self._lines), 0]
''')
class Test(BoxLayout):
pass
class TestApp(App):
def build(self):
return Test()
TestApp().run()
self._get_text_width(...) is obviously a method of a TextInput. It's working with the core of the widget so it might be unstable (first example I posted was buggy because of my mistake) ^^
Now if padding_x's values padd from left and right, you'll need only the left side (the difference is only in using addition and substraction in the right place), so let's do that:
get the longest substring in the TextInput
get its width (because it's not consistent!) via the fancy method
substract from center[0] coordinate
When we've already centered the X axis, let's go to the Y. padding_y's values are top and bottom:
padd down the half of the widget's height
get a half of a height of a single line
multiply the number by the count of lines that are in TextInput
substract the number from self.height / 2.0
bottom is 0, we don't care about it
Note: max() expects some arguments and if there's no text, max() will raise its voice. We'll shut it with an alternative left padding for padding_x using only the center:
<padding_x with max> if self.text else [self.center[0], 0]
#pete halign: 'center' plus the padding on y axis only does the job as well
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_string('''
<Test>:
TextInput:
text: 't'
halign: 'center'
padding_y: [self.height / 2.0 - (self.line_height / 2.0) * len(self._lines), 0]
''')
class Test(BoxLayout):
pass
class TestApp(App):
def build(self):
return Test()
TestApp().run()
Simply use halign: "center" , in your TextInput

placement of widgets in kivy when useing a BoxLayout and no absolute coordinates

I want to create a view of some parameters. A parameter has a name, a description and a value.This should look like:
parameter name, slider, numeric display
description
in kivy you can use a BoxLayout in the kv language:
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
Label:
text: 'warp power'
Slider:
id: pwSlider
min: 0.0
max: 1.0
value: 0.0
TextInput:
text: "%.4f" % pwSlider.value
multiline: False
on_text_validate: pwSlider.value = float(self.text)
Label:
markup: True
text: '[color=a0a0a0]sets the warp power[/color]'
The problem is, that the description and paramter name does not start at the same x position.
A other problem ist, that I want to scale the text x-size down to the size of the text inside.
any ideas?
To restrict a label width to the texture width (same logic for height if you need), you can:
Label:
size_hint_x: None
width: self.texture_size[0]
If you use a such label in a BoxLayout, it will be left/bottom aligned by default. For your information, you can use pos_hint to place it somewhere else.
I guess in your example, just using the method described above in your description Label will be enough.

Categories