I would like to put a circular button (buttonBehavior that I changed to make it circle) to the bottom right of a TabbedPanelItem.
So I did like this :
Kivy file :
#:kivy 1.0.9
<CircularButton>:
size: (min(self.width,self.height),min(self.width,self.height)) # force circle
canvas:
Color:
rgba: ((1,1,1,1) if self.state == "normal" else (.5,.5,.5,1))
Ellipse:
pos: self.pos
size: self.size
<MainWindow>:
tab_pos: 'left_mid'
size_hint: .5, .5
pos_hint: {'center_x': .5, 'center_y': .5}
do_default_tab: False
TabbedPanelItem:
addIngre: buttonAdd
text: 'All'
CircularButton:
id: buttonAdd
size_hint: .1, .1
pos : 90, 90 #Here it doesn't work
TabbedPanelItem:
text: 'first tab'
Label:
text: 'First tab content area'
TabbedPanelItem:
text: 'tab2'
BoxLayout:
Label:
text: 'Second tab content area'
Button:
text: 'Button that does nothing'
python file :
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.vector import Vector
from kivy.uix.behaviors.button import ButtonBehavior
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
class CircularButton(ButtonBehavior, Widget):
def __init__(self, **kwargs):
super(CircularButton, self).__init__(**kwargs)
def collide_point(self, x, y):
return Vector(x, y).distance(self.center) <= self.width / 2
def on_release(self):
print("Ok")
class MainWindow(TabbedPanel):
addIngre = ObjectProperty()
pass
class ListApp(App):
def build(self):
return MainWindow()
if __name__ == '__main__':
ListApp().run()
The size_hint works. I believe it gives my widget 10% of the size of the tab, but pos doesn't change the position of my widget.
I tried with pos, pos_hint, ... but nothing seems to works, position is not applied to my widget
Do I have to put a position in my widget, and call for this position instead of what I'm doing here?
Kivy ยป TabbedPanel
The TabbedPanel widget manages different widgets in tabs, with a
header area for the actual tab buttons and a content area for
showing the current tab content.
Solution
Add a FloatLayout inside the TabbedPanelItem
Add your CircularButton inside the FloatLayout
Use pos_hint:
Snippets
TabbedPanelItem:
addIngre: buttonAdd
text: 'All'
FloatLayout:
CircularButton:
id: buttonAdd
size_hint: .1, .1
# pos: root.center
# pos_hint: {'x': 0.9, 'y': 0.9}
pos_hint: {'x': 0.3, 'y': 0.5}
Output
Related
I'm struggling to get the text in a button to align in the centre of the button, it's just sitting at the bottom of the button.
I've tried changing text_size and font_size to the RoundedButton but nothing has worked so far.
FloatLayout:
RoundedButton:
size_hint: 0.417, 0.15625
pos_hint: {"x": 0.0556, "y": 0.15}
text: "Holding text that isn't aligning in the centre of the button'"
color: 0,0,0,1
text_size: self.width , self.height
halign: "center"
font_size: self.height - 75
<RoundedButton#Button>:
background_normal: ""
background_color: 0, 0, 0, 0
back_color: 0.2,0.6,1,1
border_radius: 10
font_size: '25'
color: self.back_color
bold: True
canvas.before:
Color:
rgba: self.back_color
Line:
rounded_rectangle: self.x, self.y, self.width, self.height, self.border_radius
width: 1
and a trimmed version of the python file:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.core.window import Window
Window.clearcolor = (1,1,1,1)
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("mykivy.kv")
sm = WindowManager()
class MyApp(App):
def build(self):
return sm
if __name__ == '__main__':
MyApp().run()
I've just figured it out, if anyone wants to know the answer in future I had to set valign to "center" (as well as having halign: "center")
Any assistance is much appreciated.
Here's an example code to show the problem,
when the ('Press me') button is pressed and the widget resized(white box come within view), the TabbedPanel redraws like a glitch (even though its not).
I've tried changing the layout order and toyed a bit with the animation, no
I wish to prevent that blinking behaviour of the tabbedpanel on the left.
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.animation import Animation
from kivy.lang import Builder
Builder.load_string('''
<Main>:
BoxLayout:
orientation:'vertical'
BoxLayout:
id: main_screen
orientation: 'horizontal'
TabbedPanel:
do_default_tab: False
tab_pos: 'left_top'
tab_width: main_screen.height/4
TabbedPanelItem:
text: 'four'
TabbedPanelItem:
text: 'three'
TabbedPanelItem:
text: 'two'
TabbedPanelItem:
text: 'one'
BoxLayout:
id: swidget
slide: 0
size_hint: None, None
height: main_screen.height
width: self.slide
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
Button:
size_hint_y: None
height: '33dp'
text: 'Press me'
on_release: root.display_widget(swidget)
''')
class Main(BoxLayout):
def __init__(self, **kwargs):
super(Main, self).__init__(**kwargs)
def display_widget(self, widget):
swidget = widget
if swidget.slide == 0:
anim = Animation(slide=105, duration=0.6)
anim.start(widget)
else:
anim = Animation(slide=0, duration=0.6)
anim.start(widget)
class TabbedApp(App):
def build(self):
return Main()
TabbedApp().run()
Add AnchorLayout to be the parent of swidget / BoxLayout and when the animation starts, it won't impact the instantiated TabbedPanel.
Snippets
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
size_hint_x: None
width: 0
BoxLayout:
id: swidget
slide: 0
size_hint_x: None
width: self.slide
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
I am trying to make an aap for MCQ's. Here i want a function, if anyone even click on the label corresponding to a checkbox, that check box must also be checked.. Is it possible in Kivy? Because Kivy doesn't provide any text association with checkbox directly.
Here is a portion of kv.
<MCQCheckBox#CheckBox>:
color:0,0,0,1
size_hint: 0.15, 1
group: 'opts'
<MCQLabel#Label>:
text_size: self.size
valign: 'center'
font_size: '13sp'
color: 0,0,0,1
<MCQsGUI>:
BoxLayout:
orientation:'vertical'
size_hint: 0.95, 0.7
spacing: 2
pos_hint: {'center_x': .5, 'center_y': .5}
MCQBoxLayout:
MCQCheckBox:
MCQLabel:
text:"option 1"
MCQBoxLayout:
MCQCheckBox:
MCQLabel:
text:"option 2"
MCQBoxLayout:
MCQCheckBox:
MCQLabel:
text:"option 3"
MCQBoxLayout:
MCQCheckBox:
MCQLabel:
text:"option 4"
You can do it using only kv languaje and dynamic classes:
To allow the label to behave like a button, you just have to make MCQLabel inherit from Label and ButtonBehavior classes.
To keep the group's own behavior you can call the _do_press () method of the ToggleButton class (CheckBox inherits from it) when the asociated label is pressed.
test.kv:
<MCQCheckBox#CheckBox>:
color: 0, 0, 0, 1
size_hint: 0.15, 1
<MCQLabel#ButtonBehavior+Label>:
text_size: self.size
valign: 'center'
font_size: '13sp'
color: 0, 0, 0, 1
<MCQLabelCheckBox#BoxLayout>:
text: ''
group: ''
MCQCheckBox:
id: cb
group: root.group
MCQLabel:
on_press: cb._do_press()
text: root.text
<MCQsGUI>:
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
MCQLabelCheckBox:
text:"option 1"
group: 'opts'
MCQLabelCheckBox:
text:"option 2"
group: 'opts'
MCQLabelCheckBox:
text:"option 3"
group: 'opts'
MCQLabelCheckBox:
text:"option 4"
group: 'opts'
main.py:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class MCQsGUI(BoxLayout):
pass
class TestApp(App):
def build(self):
return MCQsGUI()
if __name__ == '__main__':
TestApp().run()
I don't know if there is a direct/'built-in' way to do that. But here is an improvised example without using main.kv file.
When you create a CheckBox and a Label object, you can manually connect the CheckBox pressing this way :
import kivy
from kivy.uix.checkbox import CheckBox
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
class Answer(Label):
def __init__(self, text, associate):
Label.__init__(self);
self.text = text;
self.associate = associate;
def on_touch_down(self, touch):
Label.on_touch_down(self, touch);
if self.collide_point(touch.pos[0], touch.pos[1]):
self.associate.active = not self.associate.active;
class Page(GridLayout):
def __init__(self):
GridLayout.__init__(self, rows = 2, cols = 2);
self.check = [CheckBox(), CheckBox()];
self.ans = [Answer(text = 'Choice 1', associate = self.check[0]),\
Answer(text = 'Choice 2', associate = self.check[1])];
for i in self.ans:
self.add_widget(i);
for i in self.check:
self.add_widget(i);
class Example(App):
def build(self):
return Page()
So every time you press the Label, it will do : self.associate.active = not self.associate.active which works as the switch for the corresponding CheckBox.
*This is only one way, you can improvise or even find a better way. Is this okay?
So i have created some screens and added some properties to them (layouts, buttons, name etc) but everything is on the kivy file. Now, i want to make a function in the python main file which will take me from the starting screen to the next one after a brief time. Though nothing seems to work without me having to move everything from the kivy file to the python file. Any ideas?
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class AndAnotherScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class BackgroundLabel(Label):
pass
class CompanyImage(Image):
pass
class LoadingScreen(Screen):
def __init__(self, **kwargs):
super(LoadingScreen, self).__init__(**kwargs)
def ChangeScreen(self):
ScreenManager().current = MainScreen().name
presentation = Builder.load_file("tsalapp.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
.kv
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#:import Clock kivy.clock.Clock
#: import Color kivy.graphics
#: import Rectangle kivy.graphics
ScreenManagement:
transition: FadeTransition()
LoadingScreen:
MainScreen:
AnotherScreen:
AndAnotherScreen:
<BackgroundLabel>:
background_color: 30,144,255,1
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
<LoadingScreen>:
name: "main"
id: "yolo"
on_enter: Clock.schedule_once(none, 3)
#app.root.current: "menu"
Image:
source: 'Untitled.png'
height: root.height
width: root.width
allow_stretch: True
keep_ratio: False
AnchorLayout:
Label:
text: "Tsala Inc."
anchor_x: 'center'
anchor_y: 'center'
font_size: root.height/15
<MainScreen>:
name: "menu"
id: "swag"
BoxLayout:
orientation: "vertical"
Button:
text: "Next Page"
background_color: 1,1,4,1
size_hint: (0.5, 0.5)
on_release:
app.root.current= "Another Screen"
Button:
text: "Previous Page"
background_color: 1,4,1,1
size_hint: (0.5, 0.5)
on_release:
app.root.current= "And Another Screen"
<AnotherScreen>:
name: "Another Screen"
GridLayout:
cols: 2
Button:
text: "Previous Page"
background_color: 0,0,1,1
size_hint: (0.25,1)
on_release:
app.root.current = "menu"
Button:
text: "Exit"
background_color: 1,0,0,1
size_hint: (0.25, 1)
on_release:
exit()
<AndAnotherScreen>:
name: "And Another Screen"
BoxLayout:
orientation: "vertical"
Button:
text: "Next Page"
background_color: 1,1,4,1
size_hint: (0.5, 0.5)
on_release:
app.root.current= "menu"
Button:
text: "Nextest Page"
background_color: 1,4,1,1
size_hint: (0.5, 0.5)
on_release:
app.root.current= "Another Screen"
Firstly I recommend create a global variable for screenmanager or place screenmanager in the MainApp as class property and create a list or a dictionary for the screens:
class MyApp(App):
sm = ScreenManager() # screenmanager
screens = {} # dictionary for the screens, I prefer dictionary because string indexes are more convenient
Then in a build method you can create all screens you need and return the screenmanager as root widget:
class MyApp(App):
sm = ScreenManager() # screenmanager
screens = {} # dictionary for the screens, I prefer dictionary because string indexes are more convenient
def build(self):
self.__create_screens()
MyApp.sm.add_widget(MyApp.screens['main_screen'])
return MyApp.sm
def __create_screens(self):
MyApp.screens['main_screen'] = MainScreen(name='mainscreen')
MyApp.screens['another_screen'] = AnotherScreen(name='another_screen')
MyApp.screens['and_another_screen'] = AndAnotherScreen(name='and_another_screen')
So now you can switch your app's screens easily wherever you want using switch_to method:
MyApp.sm.switch_to(MyApp.screens['another_screen'], direction='left', duration=1)
I'm new to kivy , I'm trying to write an application using kivy in python, I got struck at one point where i have to read text from textinput whenever it is changed and based on that i want to implement my button's functionality - I have gone through all the documentation but i couldn't figure out how to do it - Can any body tell me how can I resolve this or am I missing something?
from __future__ import print_function
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.core.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import *
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
import walascan
from kivy.clock import Clock
import os
kv = """
<KartScan>:
IntroScreen:
<IntroScreen#Screen>:
orientation: 'horizontal'
name: 'introscreen'
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'index.png'
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
BoxLayout:
orientation:'horizontal'
size_hint: .5, .1
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
spacing: 20
pos_hint: {'center_x':.8, 'center_y': .8}
AnchorLayout:
anchor_x: 'left'
size_hint_x: .5
TextInput:
id: waybill
width: 20
text: "Enter Waybill No."
multiline: False
height: self.minimum_height
size_hint_y: None
font_size: 30
focus: True
on_text_validate: app.on_waybill()
AnchorLayout:
anchor_x: 'right'
size_hint_x: None
Button:
size_hint: None, None
height: 50
width: self.texture_size[0]
padding: 10, 10
text: "Add"
on_press:app.buttonClicked()
on_release: root.current = 'mainpage'
AnchorLayout:
anchor_x: 'right'
size_hint_x: None
Button:
size_hint: None, None
height: 50
width: self.texture_size[0]
padding: 10, 10
text: "Compare"
on_press:app.buttonClicked()
on_release: root.current = 'mainpage'
"""
Builder.load_string(kv)
waybill = TextInput(text="Enter Waybill No.", multiline=False)
class KartScan(FloatLayout):
def __init__(self, **kwargs):
super(KartScan, self).__init__(**kwargs)
self.register_event_type('on_text_validate')
def on_text(self, *args):
print('new value is ', waybill.text)
def on_text_validate(self):
pass
def on_focus(self, obj, focused):
if not focused:
self.dispatch('on_text_validate')
class KartScanApp(App):
def build(self):
return KartScan()
def buttonClicked(self):
popup = Popup(title='Result',
content=Label(text=self.on_waybill()),
size_hint=(None, None), size=(100, 100))
popup.bind()
popup.open()
def getwlbtstate(self):
return walascan.mainAntennas()
def on_waybill(self):
waybill.bind(text=KartScan.on_text_validate)
# popup = Popup(title='Result',
# content=Label(text=waybill.text),
# size_hint=(None, None), size=(100, 100))
# popup.bind()
# popup.open()
return waybill.text
if __name__ == '__main__':
KartScanApp().run()
kv file
TextInput:
on_text: print(self.text)
You could use on_text to call a function when the text changes. I think it sometimes even triggers when no change has occured.
TextInput has a text property, and in kivy, all properties have associated on_xxx events, that you can bind to to react to them, so when reading the doc, even if it's not explicitly mentionned for them, everytime you see something is defined as a Property (or any subclass of it, like StringPropertyNumericProperty,ObjectProperty,AliasProperty`).
TextInput:
on_text: do_something(self.text)
or in python
t = TextInput(text='', on_text=do_something)
or
t = TextInput(text='')
t.bind(on_text=do_something)