Kivy: Touch the label to check the checkbox - python

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?

Related

Kivy Dynamic Classes - multiple objects

I am trying to simplify the code that generates rows with the same content multiple times by using dynamic classes as shown in the simplified example below.
So far so good, code is simpler but I'm trying to find solutions to access from python for example Label in the second row. In my old solution, I have a unique ID for each widget but now buttons and labels are defined as only ones for all rows, and they have the same ID.
Is there any possibility to access them individually?
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
Builder.load_file("test.kv")
class MyLayout(Widget):
pass
class MyNewApp(App):
def build(self):
return MyLayout()
if __name__ == "__main__":
MyNewApp().run()
test.kv
<RowTemplate#GridLayout>:
cols: 2
Label:
id: row_label
text: 'test'
Button:
id: row_button
text: 'button'
<MyLayout>
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
GridLayout:
size: root.width, root.height
cols: 1
canvas.before:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
RowTemplate: #fist row
RowTemplate: #second row
RowTemplate: #third row
from kivy.properties import StringProperty
class RowTemplate(GridLayout):
button_text = StringProperty('')
label_text = StringProperty('')
test.kv
<RowTemplate>:
cols: 2
Label:
id: row_label
text: root.label_text
Button:
id: row_button
text: root.button_text
Now you can change text dynaminally as bellow.
<MyLayout>:
RowTemplate:
label_text: 'label one'
button_text: 'button one'
RowTemplate:
label_text: 'label two'
button_text: 'button two'

Kivy widget can't change pos

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

How to use image as splash screen in kivy python desktop application with labels?

This is my code which just accepts numbers and add them and displays results in label, numbers are accepted through spinners. But I need splash screen before the main app is loaded. I want to know how we can use image as an splash screen and display for a while until the main app starts.
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
from kivy.uix.spinner import Spinner
from kivy.app import App
Builder.load_string('''
<MainScreen>:
GridLayout:
orientation: 'vertical'
cols: 1
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
GridLayout:
orientation: 'vertical'
cols: 2
Spinner:
id: first
text: ' First Number'
values: ['1','2','3','4','5','6','7','8','9']
Spinner:
id: second
text: ' Second Number'
values: ['1','2','3','4','5','6','7','8','9']
Label:
id: result
text: ' Result'
color: 0,0,0,1
Button:
id: res
on_press: root.onPow(first.text,second.text)
text: 'Progress'
''')
class MainScreen(FloatLayout):
changet = StringProperty()
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
def onPow(self,fir,sec):
a = 0
b = 0
for i in range((10000)):
print(b)
self.ids.result.text=str(b*(int(fir)*int(sec)+i))
b+=1
class TestApp(App):
def build(self):
return MainScreen()
if __name__ == "__main__":
TestApp().run()
Image for splash screen:
Even can we use labels with this image. Thank you.

kivy scrollview inside gridlayout on label

I trying to create a one screen app with one root rule set. by pressing a button i should see a scrollable text in the grid next to the button. In all the example solutions I see that scrollview is working in similar KV files that I have seen in other questions. Could someone please identify what I have missed in KV file.
my .py file:
import kivy
import string
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
from kivy.properties import StringProperty
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
class RootContainer(BoxLayout):
instance = ObjectProperty(None)
def __init__(self, **kwargs):
super(RootContainer, self).__init__(**kwargs)
def clickAction1(self, instance):
#identify the button pressed
buttonText = instance.text
self.lbl1.text = instance.text + " some text goes here ... "
myresult = " this is scrolling text.\n " * 30
self.lbl5.text = myresult
class MBApp(App):
def build(self):
return RootContainer()
if __name__ == '__main__':
MBApp().run()
my KV file:
#:kivy 1.0.9
<RootContainer>:
id: theRoot
lbl1: my_labelC
lbl5: my_labelCS
BoxLayout:
orientation: 'vertical'
spacing: 20
padding: 20
canvas:
Color:
rgb: 0, .33, 0
Rectangle:
pos: self.pos
size: self.size
Button:
text: "This is 1st button"
text_size: self.size
size_hint: (.5,1)
on_press: theRoot.clickAction1(self)
Button:
text: "This is 2nd button"
text_size: self.size
size_hint: (.5,1)
on_press: root.clickAction1(self)
GridLayout:
rows: 2
cols: 1
spacing: 10
padding: 10
canvas:
Color:
rgb: .7, .63, 0
Rectangle:
pos: self.pos
size: self.size
Label:
id: my_labelC
canvas.before:
Color:
rgb: 0,0,0
Rectangle:
pos: self.pos
size: self.size
text: "Header text for button clicked ......."
text_size: self.size
ScrollView:
GridLayout:
cols:1
rows:1
height: self.minimum_height
Label:
id: my_labelCS
text: "Scrolling text goes here ....."
I hope this is not a duplicate. Any other suggestions to code are also welcome. Thank you.
You don't set the size of your Label, so the default applies, as any widget, the defaults are
size: 100, 100
size_hint: 1, 1
since size_hintis 1, 1, and the parent is a layout, size itself is overriden by the parent's available space
since you set size: minimum_size in the parent layout, it'll give the minimum size its children require, but the Label doesn't ask for any space, it's size_hint of 1, 1 means that it's happy to take all the available space. Kivy solve this situation by not giving it any space, so the Label size ends up being 0, 0, and the GridLayout's size as well.
So you want to disable size_hint (at least for the height, of the Label, and instead set it to the texture heights
size_hint_y: None
height: self.texture_size[1]
Which is usually sided with setting the texture width to the available width, by setting text_size.
text_size: self.width, None
size_hint_x: 1 # this is the default, so this line is not required, but you want to be sure not to disable this default
Do you really need the GridLayout inside your scrollview? This works for me:
ScrollView:
Label:
id: my_labelCS
size_hint: 1, None
text_size: self.width, None
height: self.texture_size[1]
text: "Scrolling text goes here ....."

Read the text from Textinput dynamically when text changed in text box?

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)

Categories

Resources