Python (Kivy) - How to resize tabs of a TabbedPanel - python

I'm using the Kivy library for the first time and I'm trying to develop a simple UI with a TabbedPanel. I would set the size (x) of each tab (in the code TabbedPanelItem) to fit at the entire TabbedPanel's width but if I use height or size_hint in the .kv file, seems they doesn't work.
Here my kv code:
#:import sm kivy.uix.screenmanager
ScreenManagement:
transition: sm.FadeTransition()
SecondScreen:
<SecondScreen>:
tabba: tabba
name: 'second'
FloatLayout:
background_color: (255, 255, 255, 1.0)
BoxLayout:
orientation: 'vertical'
size_hint: 1, 0.10
pos_hint: {'top': 1.0}
canvas:
Color:
rgba: (0.98, 0.4, 0, 1.0)
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'MyApp'
font_size: 30
size: self.texture_size
BoxLayout:
orientation: 'vertical'
size_hint: 1, 0.90
Tabba:
id: tabba
BoxLayout:
orientation: 'vertical'
size_hint: 1, 0.10
pos_hint: {'bottom': 1.0}
Button:
background_color: (80, 1, 0, 1.0)
text: 'Do nop'
font_size: 25
<Tabba>:
do_default_tab: False
background_color: (255, 255, 255, 1.0)
# I would these three tabs' width filling the entire TabbedPanel's width
TabbedPanelItem:
text: 'First_Tab'
Tabs:
TabbedPanelItem:
text: 'Second_Tab'
Tabs:
TabbedPanelItem:
text: 'Third_Tab'
Tabs:
<Tabs>:
grid: grid
ScrollView:
do_scroll_y: True
do_scroll_x: False
size_hint: (1, None)
height: root.height
GridLayout:
id: grid
cols: 1
spacing: 10
padding: 10
size_hint_y: None
height: 2500
Here my Python code:
# coding=utf-8
__author__ = 'drakenden'
__version__ = '0.1'
import kivy
kivy.require('1.9.0') # replace with your current kivy version !
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.properties import StringProperty, ObjectProperty,NumericProperty
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.utils import platform
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
class Tabs(ScrollView):
def __init__(self, **kwargs):
super(Tabs, self).__init__(**kwargs)
class Tabba(TabbedPanel):
pass
class SecondScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("layout2.kv")
class MyApp(App):
def build(self):
return presentation
MyApp().run()
I've read about using a StripLayout inside the TabbedPanel but I don't know if is a good solution and how to apply it correctly. Any suggestions?

I have experimented a bit with your code and after reading the TabbedPanel Docs i found out that tab_width specifies the width of the tab header (as tab_height is for the height). To use it in your kivy file you have to add the following line:
<Tabba>:
do_default_tab: False
tab_width: self.parent.width / 3
background_color: (255, 0, 255, 1.0)
# I would these three tabs' width filling the entire TabbedPanel's width
TabbedPanelItem:
.
.
.
the rest of your kivy file
What we added in this line is that each tab will be the 1/3 of it's parent width.
This even works for fewer or more tabs than 3. If you add more tabs, a horizonal scroll bar will be added to scroll through the extra tabs.
I hope I was helpful.

Related

How can I change the height of Boxlayout Dynamically?

I am trying to change Boxlayout height dynamically. There will an empty Boxlayout at the beginning, then with a ToggleButton I will add Buttons on that Boxlayout vertically. But this Boxlayout locates in Gridlayout which has ScrollView. And the number of buttons I will add will change, so the height of the box layout should change too. But it doesn't change. Could you please help.
Here is my main.py:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.spinner import Spinner
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.properties import ObjectProperty, StringProperty, BooleanProperty
from kivy.properties import ListProperty
from collections import OrderedDict
data1=["mother","father","son"]
data2=["uncle","aunt","grandfather"]
data3=["jack","mike","simon"]
data4=["1898","1975","1985","1885"]
class MainWidget(Widget):
an0=tuple(list(OrderedDict.fromkeys(data1)))
cal5= ObjectProperty()
def btn10(self,text):
if self.cal5:
self.cal5.parent.remove_widget(self.cal5) #EVERY TIME I NEED TO REMOVE IT BECAUSE MY BUTTON QUANTITIY AND TEXT WILL CHANGE DUE TO TOGGLE BUTTON SELECTION
self.cal5 =ModelSpecifications()
a=data2
b=data3
c=data4
mi=[]
n=0
while n < len(a):
aba=(str(a[n])+"\n"+str(b[n])+"\n"+str(c[n]))
mi.append(aba)
n+=1
for i in mi:
self.b1=Button(text=str(i),size_hint=(1,None),height="10dp")
self.cal5.add_widget(self.b1)
self.ids.scd.add_widget(self.cal5, index=3) #I ADD THE BUTTONS IN HERE
class SecondPage(ScrollView):
pass
some_number=120
calculated_height=(str(some_number)+"dp") #<------I CAN CHANGE THE HEIGHT AT THE BEGINING BUT IT DOSENT CHANGE THE HEIGHT DYNAMICALLY
class ModelSpecifications(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.size_hint=(1,None)
self.height=calculated_height
class Calculation(GridLayout):
pass
class MyApp(App):
pass
MyApp().run()
And here is my my.kv file:
#:import C kivy.utils.get_color_from_hex
MainWidget:
<MainWidget>:
ScreenManager:
id: scmanager
size: root.width, root.height
Screen:
id: scndpage
name: "second"
SecondPage:
Calculation:
id:scd
cols:1
height: self.minimum_height
row_default_height: "70dp" #<---THIS HEIGHT MUST BE SPECIFY BECAUSE OF SCROLLWIEW
size_hint_y: None
spacing:"10dp"
canvas.before:
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size_hint: 1, None
height: "50dp"
pading:"10dp"
spacing:"10dp"
orientation: "vertical"
BoxLayout:
orientation: "horizontal"
Label:
text:"Name:"
color: 0,0,0,1
TextInput:
text:"---"
color: 0,0,0,1
Label:
text:"Surname:"
color: 0,0,0,1
TextInput:
text:"-----"
color: 0,0,0,1
BoxLayout:
id:scdd
size_hint: 1, 1
height: "100dp"
orientation: "vertical"
BoxLayout:
size_hint: 1, None
height: "50dp"
orientation: "horizontal"
Label:
text: " Sellection:"
color: 0,0,0,1
Spinner:
text: 'Home'
values: root.an0
on_text: app.root.btn10(self.text)
Button:
text:" Calculate"
Button:
text:"Sellect"
Button:
text:"Back"
<ModelSpecifications>: #<---------HOW CAN I DYNAMICALLY CHANGE THE HEIGHT OF THIS WIDGET ?
id:anss
pading:"10dp"
spacing:"10dp"
orientation: "vertical"
canvas.before:
Color:
rgba: (C('EBE4E4'))
RoundedRectangle:
size: self.size
pos: self.pos
radius: [15]
I want to change the height in the python script because I will get the information from another function as variable.
Please run the file. Looking forward to your comments.

How to make the widgets on the popup window widgets stay with the popup window when the root window is resized

The widgets on my popup window do not resize when the root window resizes. The popup window and the labels on the popup window stay where they are. Does it have something to do with the size_hint and size of the popup window itself? It seems that the widgets(icons) are independent of the popup window.
main file
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.button import ButtonBehavior
from kivy.uix.image import Image
from kivy.properties import StringProperty, ObjectProperty,NumericProperty
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
class MainScreen(Screen, FloatLayout):
mantra_text = ObjectProperty(None)
def printMantra(self):
print(self.ids.mantra_text.text)
def icon_popup(self):
popup = Popup(title="Profile Icon", content=Popup_Content(), size_hint=(None, None), size=(300, 200))
popup.open()
class Popup_Content(FloatLayout):
pass
class ImageButton(ButtonBehavior, Image):
pass
class MainApp(App):
def build(self):
return MainScreen()
def set_profile_icon(self, image):
self.root.ids.profile_icon.source = image.source
print(image)
#print(self.root.ids.profile_icon)
MainApp().run()
kivy file
#:import utils kivy.utils
<MainScreen>
Popup_Content:
id: popup_content
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#ffbb99")
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols: 2
pos_hint: {"x":0.6, "top":1}
size_hint: 0.4,0.2
spacing_horizontal: [0.9*root.width]
Label:
text: "Name"
ImageButton:
id: profile_icon
source: "profile_icon"
on_release: root.icon_popup()
Label:
text: mantra_text.text
pos_hint: {"x":0, "top":0.8}
size_hint: 1, 0.2
text_size: self.size
halign: "center"
font_size: 25
TextInput:
id: mantra_text
pos_hint: {"x": 0.15, "top":0.6}
size_hint: 0.7, 0.1
#text_size: self.size
Label:
text: "Time"
pos_hint: {"x":0.3, "top":0.6}
size_hint: 0.4, 0.2
text_size: self.size
halign: "left"
font_size: 30
Button:
text: "Time"
pos_hint: {"x":0.3, "top":0.5}
size_hint: 0.4, 0.2
on_release: root.printMantra()
<Popup_Content>
#profile_icon: profile_icon
FloatLayout:
GridLayout:
cols: 5
pos_hint: {"x":0.95, "y":1.6}
ImageButton:
id: man_01
source: "icons/male_icon_01.png"
on_release: app.set_profile_icon(man_01)
ImageButton:
id: man_02
source: "icons/male_icon_02.png"
on_release: app.set_profile_icon(man_02)
ImageButton:
source: "icons/male_icon_01.png"
on_release: app.set_profile_icon()
ImageButton:
source: "icons/male_icon_01.png"
on_release: app.set_profile_icon()
ImageButton:
source: "icons/male_icon_01.png"
on_release: app.set_profile_icon()
ImageButton:
id: female_01
source: "icons/female_icon_01.png"
on_release: app.set_profile_icon(female_01)
If you want your Popup to change size when you resize the App, then use size_hint. Something like:
popup = Popup(title="Profile Icon", content=Popup_Content(), size_hint=(0.5, 0.5))
Using size_hint=(None, None), size=(300, 200) forces the Popup size to (300, 200) regardless of the size of MainScreen.
And to get the Popup content to follow the Popup, you can use RelativeLayout. In the documentation for RelativeLayout, it says:
When a widget with position = (0,0) is added to a RelativeLayout, the
child widget will also move when the position of the RelativeLayout is
changed. The child widgets coordinates remain (0,0) as they are always
relative to the parent layout.
So if you define your Popup_Content as a RelativeLayout, then the GridLayout will follow it. I suggest defining Popup_Content as:
class Popup_Content(RelativeLayout):
pass
Then, in the kv:
<Popup_Content>
#profile_icon: profile_icon
GridLayout:
cols: 5
# pos_hint: {"x":0.95, "y":1.6}
ImageButton:
id: man_01
.
.
.

Strech image to fit gridlayout in kivy

I was trying to get an image to fill out my entire grid, but it would leave parts of it blank - even while defining allow_strech and keep_ratio. How do I get my image to fill out the entire grid?
(The problem is the width in this case as the height is fine, but I assume it's to do with the image itself and not the code..)
python code:
import kivy
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.core.window import Window
GUI = Builder.load_file('style.kv')
# Window.size = (2224, 1668)
class NotebookScreen(GridLayout):
def __init__(self, **kwargs):
self.rows = 1
super(NotebookScreen, self).__init__(**kwargs)
class MainApp(App):
def build(self):
return NotebookScreen()
if __name__ == "__main__":
MainApp().run()
kv file:
<NotebookScreen>
FloatLayout:
rows: 2
GridLayout:
size_hint: 1, .1
pos_hint: {"top": 1, "left": 1}
id: tool_bar
cols: 1
canvas:
Color:
rgba: 0, 0, 1, 1
Rectangle:
pos: self.pos
size: self.size
GridLayout:
id: notebook_grid
size_hint: 1, .9
pos_hint: {"top": .9, "left": 0}
cols: 1
Image:
id: notebook_image
source: 'images/notebook.jpg'
allow_strech: True
keep_ratio: False
pos: self.pos
size_hint: 1, 1
Spelling error:
allow_strech: True
should be:
allow_stretch: True
Instead of GridLayout, use BoxLayout

Making TextInput on_text_validate event accessible from another widget

I'm trying to create a custom widget as a module. My custom widget includes a TextInput and some other stuff inside a FloatLayout widget.
When i import my custom widget i want to use on_text_validate event for TextInput within the Floatlayout.
As you know we can add the button behavior to any widget via ButtonBehavior class but i couldn't find a way to add on_text_validate event to my custom widget.
That's my module named mymodule.py inside my project folder.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
#from kivy.uix.behaviors import FocusBehavior
Builder.load_string("""
<SearchBar>:
id: search_bar
size_hint: 1,.1
pos_hint: {'top':1}
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
TextInput:
id: input
hint_text: 'search'
size_hint: .82,None
height: dp(33)
pos_hint: {'center_x':.565,'center_y': .5}
multiline: False
padding_y: [self.height / 2.0 - (self.line_height / 2.0) * len(self._lines), 0]
padding_x: 20,150
""")
class SearchBar(FloatLayout):
pass
I call it from main.py as
#:import SearchBar mymodule.SearchBar
And that's my main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string("""
#:import SearchBar mymodule.SearchBar
<MyTest>:
SearchBar:
id: search
size_hint: 1,.1
# on_text_validate: root.do_something()
""")
class MyTest(FloatLayout):
def do_something(self,*args):
print('done')
class Test(App):
def build(self):
return MyTest()
Test().run()
A possible solution is to create an event with the same name and shoot it with the TextInput event:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
Builder.load_string("""
<SearchBar>:
id: search_bar
size_hint: 1,.1
pos_hint: {'top':1}
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
TextInput:
id: input
hint_text: 'search'
size_hint: .82,None
height: dp(33)
pos_hint: {'center_x':.565,'center_y': .5}
multiline: False
padding_y: [self.height / 2.0 - (self.line_height / 2.0) * len(self._lines), 0]
padding_x: 20,150
on_text_validate: root.dispatch('on_text_validate', *args)
""")
class SearchBar(FloatLayout):
def __init__(self, **kwargs):
super(SearchBar, self).__init__(**kwargs)
self.register_event_type('on_text_validate')
def on_text_validate(self, *args):
pass

Kivy BoxLayout - move widgets to the top

I have all the widgets sized and positioned relative to one another. If I add a "Label:" to the bottom of the kv code it will move everything up to the top. But that can't be the "right" way to do it. What am I missing?
import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string('''
<Controller>:
BoxLayout:
orientation: 'vertical'
padding: 20
spacing: 20
TextInput:
hint_text: 'Feed Name'
multiline: False
size_hint: (0.75, None)
height: 30
pos_hint: {'center_x': 0.5}
TextInput:
hint_text: 'Feed URL'
multiline: True
size_hint: (0.75, None)
height: 68
pos_hint: {'center_x': 0.5}
Button:
text: 'Add Feed'
padding: (10, 10)
height: 30
size_hint: (None, None)
pos_hint: {'center_x': 0.5}
''')
class Controller(BoxLayout):
pass
class PodcastApp(App):
def build(self):
return Controller(info='Hello world')
if __name__ == '__main__':
PodcastApp().run()
From the BoxLayout docs:
padding Added in 1.0.0 Padding between layout box and children:
[padding_left, padding_top, padding_right, padding_bottom].
padding also accepts a two argument form [padding_horizontal,
padding_vertical] and a one argument form [padding].
Changed in version 1.7.0: Replaced NumericProperty with
VariableListProperty.
padding is a VariableListProperty and defaults to [0, 0, 0, 0].
Try adding a padding to the bottom of the boxlayout if you want it's children to get pushed toward the top.
for instance giving the padding a value of
[20, 20, 20, 'new bottom padding here']

Categories