I'm a learning kivy on my own and am trying to create a sublcass of TextInput with the custom behavior that, when selected, the contents of the text box are highlighted (so that a user can then begin typing in a new value). I have two questions, one for each attempt.
My first attempt was this:
from datetime import date
# kivy imports
from typing import Text
from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from kivy.properties import (StringProperty)
class MedAssignApp(App):
def build(self):
return MainWindow()
class MainWindow(AnchorLayout):
pass
class TextEntry(TextInput):
def on_touch_down(self, touch):
super().on_touch_down(touch)
Clock.schedule_once(lambda dt: self.select_all())
I then have several instances of TextEntry in the medassign.kv file:
.. other stuff above
GridLayout:
cols: 4
rows: 3
Label:
text: 'Date'
DateEntry:
Label:
text: 'Shift'
TextEntry:
text: 'THIS WILL BE A DROPDOWN'
Label:
text: 'ST1'
TextEntry:
text: 'Enter name..'
Label:
text: 'ST2'
TextEntry:
text: 'Enter name..'
Label:
text: 'ST3'
TextEntry:
text: 'Enter name..'
Label:
text: 'ST4'
TextEntry:
text: 'Enter name..'
<TextEntry>:
However, when I test the application and I click on one of the text fields, all texts in all instances are highlighted. So my first question is: Does anyone understand why?
My second attempt:
Then I attempted to do it this way (below), which works, but again, I don't know why it works.
# built-in imports
from datetime import date
# kivy imports
from typing import Text
from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from kivy.properties import (StringProperty)
class MedAssignApp(App):
def build(self):
return MainWindow()
class MainWindow(AnchorLayout):
pass
class TextEntry(TextInput):
pass
and medassign.kv
<TextEntry>:
TextInput:
on_touch_down: Clock.schedule_once(lambda dt: root.select_all())
Note: In this second case, if I use root it works as I would like. But if I use self, it does not work as I would like (it behaves like the first attempt).
My second question: Can anyone explain why this works with root?
Thank you!
The first version fails because you have not taken into account that all the widgets get the on_touch_down() call on every touch down event. It is the responsibility of the instances on_touch_down() method to determine if the touch is relevant to that instance. The on_touch_down() method returns a bool:
If True, the dispatching of the touch event will stop. If False,
the event will continue to be dispatched to the rest of the widget
tree.
So your on_touch_down() for every instance applies the select_all(). Here is a modified version of on_touch_down():
def on_touch_down(self, touch):
if self.collide_point(*touch.pos): # check if touch is on this instance
Clock.schedule_once(lambda dt: self.select_all())
return super().on_touch_down(touch) # Let the TextInput handle this touch as normal.
Related
import kivy
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.widget import Widget
from kivy.clock import Clock
Builder.load_string('''
<apper>
input_val : input_t
BoxLayout:
size: root.size
Label:
id: label_t
text: root.texter
TextInput:
id: input_t
Button:
on_press : root.doer()
''')
class apper(Widget):
texter = StringProperty()
input_val = ObjectProperty(None)
run = 0
def doer(self, dt):
self.run+=1 # keep track of number of times this function ran
#self.texter = self.ids.input_t.text
print(self.ids.input_t.text, self.run) #prints an empty string for self.ids.input_t.text
self.texter = self.input_val.text
class runs(App):
def build(self):
Clock.schedule_interval(apper().doer, 1.0/2.0)
return apper()
runs().run()
#the code works when you remove the Clock statement and the second argument or apper.doer() and you have to press the button
#to rename the label
#how comes it doesnt work when you introduce the clock statement
#Note that I am new to kivy but not new to programming
When I use a button to run the function on press the correct values from text input are returned but using a clock it doesn't work, can anyone help.
Also when the program should be running properly with the Clock then the button is unnecessary since it updates the label with the text of the TextInput every 0.5 seconds.
Is there any way the code can be rewritten or mended such that the TextInput returns a value and not an empty string.
move the clock to ur apper class: and put it like this
Clock.schedule_interval(self.doer, 1/2)
and declare doer like this:
def doer(self, *args):
and feel free to check this tuto i uploaded recently
https://www.youtube.com/watch?v=DbX-CdVob6E&t=57s
it is a simple program but i can not find a way to make it work. i just want to add a widget in boxlayout2 when the user presses the button (and has not writed anything in textinput) which is located in boxlayout1 .The widget do not display in screen.What should i do?
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class BoxLayout1(BoxLayout):
def Search(self):
if self.ids.textinput.text!='':
BoxLayout2()
class BoxLayout2(BoxLayout):
def Print(self):
self.add_widget(Button(text='hello'))
class TestApp(App):
pass
TestApp().run()
and here is my kivy code
test.kv
<BoxLayout1>:
BoxLayout:
Label:
text:'Hello'
TextInput:
id: textinput
Button:
text: 'write'
on_press: root.Search()
BoxLayout:
orientation: 'vertical'
BoxLayout1:
BoxLayout2:
I see the presentation layout i want but the button is nowhere to be found.
To make it clear let's follow the stream of the app you've written.
it creates a BoxLayout and puts BoxLayout1 and BoxLayout2 in it, the second one doesn't have any content. When you click on write, the app checks the content of the text box and if valid, calls the constructor of BoxLayout2! Now at this point it creates an instance of this class, but does not keep it's reference so it will be discarded immediately. Now what you want is to call a function of a currently existing instance, not to create another one. Here's the code:
python:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
class BoxLayout1(BoxLayout):
def Search(self):
if self.ids.textinput.text!='':
self.parent.ids.bxl2.addButton()
# BoxLayout2()
class BoxLayout2(BoxLayout):
def addButton(self):
button=Button(text='hello')
self.add_widget(button)
kivy language:
<BoxLayout1>:
BoxLayout:
Label:
text:'Hello'
TextInput:
id: textinput
Button:
text: 'write'
on_press: root.Search()
BoxLayout:
orientation: 'vertical'
BoxLayout1:
BoxLayout2:
id:bxl2
I'm doing a proyect using kivy but i have a problem with the checkboxes. At first I'm trying to do the program like python coding (I know it is'nt clean, but I understand more) And i have a first screen with this coding:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.checkbox import CheckBox
class MainScreen(GridLayout):
def __init__(self,**kwargs):
e=[]
super(MainScreen, self).__init__(**kwargs)
self.cols=2
def on_checkbox_active(checkbox, value):
if value:
e.append(value)
print e
else:
print('The checkbox', checkbox, 'is inactive')
self.add_widget(Label(text='Inserta assignatures desitjades',font_size=35))
self.add_widget(Label(text=''))
ch1 = CheckBox()
self.add_widget(ch1)
self.add_widget(Label(text='Termotecnia'))
ch2 = CheckBox()
self.add_widget(ch2)
self.add_widget(Label(text='Termotecnia'))
ch3 = CheckBox()
self.add_widget(ch3)
self.add_widget(Label(text='Termotecnia'))
ch4 = CheckBox()
self.add_widget(ch4)
self.add_widget(Label(text='Termotecnia'))
b1=Button(text='Exit',background_color=[0.7,0.7,1,1],font_size=24)
self.add_widget(b1)
b2=Button(text='Next',font_size=24,font_color=[1,3,4,0],background_color=[1,2,3,6])
self.add_widget(b2)
ch1.bind(active=on_checkbox_active)
ch2.bind(active=on_checkbox_active)
b1.bind(on_press=exit)
b2.bind(on_press=reloaded)
...
class SimpleKivy(App):
def build(self):
return MainScreen()
if __name__=='__main__':
SimpleKivy().run()
I want to select two or three options for example, and save it for the next screen, like a type of selection. If anyone knows how to do it and save information for the next screen it woul help me a lot, because i have the code of the next screen for all the options, but i want to preselect in the first screen and then only use which i have selected. Also if anyone can help me, i want to know hoy to do the transition to another class (screen) when the button "Next" is pressed. I know this question are pretty simple but I'm new in kivy programming and some concepts are pretty difficult. Thanks.
What you want is accessing variables in other classes. Sometimes this can be annoying and you can do it either hard way with all __init__() and stuff, or... a simplier way comes along: it's get_running_app().
You can create a dictionary or something else, where you can store any value your other classes need to access. It's similar to using globals and it costs you less lines of code. For example in your case you could use a dictionary(or nested dictionaries, json, ...) to store for example 'checkboxes':'<names of checked ones>' and in each init you can loop over these values to make checkboxes active
Basically all you need is a = App.get_running_app() somewhere and something to access in main - App - class.
Example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string('''
<Root>:
MainScreen:
name: 'main'
AnotherScreen:
name: 'another'
<MainScreen>:
BoxLayout:
Button:
text: 'next screen'
on_release: root.parent.current='another'
Button:
text: 'ping!'
on_release: root.ping()
<AnotherScreen>:
BoxLayout:
Button:
text: 'previous screen'
on_release: root.parent.current='main'
Button:
text: 'ping!'
on_release: root.ping()
''')
class MainScreen(Screen):
def __init__(self, **kw):
super(MainScreen, self).__init__(**kw)
self.a = App.get_running_app()
def ping(self):
print self.a.big_dict['hi']
class AnotherScreen(Screen):
def ping(self):
b = App.get_running_app()
print b.big_dict['hi']
class Root(ScreenManager):
pass
class SimpleKivy(App):
big_dict={'hi':'hi there!'}
def build(self):
return Root()
SimpleKivy().run()
You can see there's no need to call __init__(), no need to write more lines of code if you really don't need to.
I am having problems understanding the usage of custom Properities and ways of binding methods to events.
Here's my code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import StringProperty
kivy_lang = '''
<MainWidget>:
on_my_property: my_label.text = 'from button bind method via StringProperty' + my_property
Label:
id: my_label
text: root.my_property
Button:
id: my_button
text: 'intro button'
'''
class MainWidget(BoxLayout):
# bind some properties
my_property = StringProperty('0')
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
# if needed to do sth on widget construction
self.ids.my_button.bind(on_press=self.my_method)
def my_method(self,*args,**kwargs):
self.my_property = str(int(self.my_property)+1)
self.ids.my_button.text = 'new'
class MyApp(App):
def build(self):
Builder.load_string(kivy_lang)
return MainWidget()
if __name__ == '__main__':
MyApp().run()
When I run it it renders OK, but when I click a button, as a result I get
NameError: name 'my_property' is not defined
I tried binding method for Button in kv lang with (and removing whole 'init()' on python side):
on_press: root.my_method
and then when I press button the app doesn't crash but nothing happens
Can someone explain me how to adjust this code to work?
I understand the code is a little 'mixed techniques' but I did it that way to get to know different approaches, so I would appreciate if You don't turn it all around :)
1/ you are missing 'self' before 'my_property' in 'on_my_property' bindind, hence the crash
2/ in kv bindings. the python code is called as written, so you need '()' after 'root.my_method', or the statement has no effect.
I try to design a GUI to handle stepper motors via ROS, kivy and python. You can find a minimal version of the GUI below. Actually I want to use a ROS message to update a kivy text input field (read only). In the minimal example, pressing the button should transfer the data of the first input field over a local ROS node to the second input field. Actually it seems, that the Callback within the rospy.Subscriber() doesn't enter the Test class.
Thank You for any suggestions!
main.py
import kivy
kivy.require('1.7.2')
import rospy
from std_msgs.msg import String
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
class Test(BoxLayout):
text_is = 'text before button press'
def Pub(self):
publish = self.ids.test_text_pub.text
try:
ROSNode.new_text_is.publish(publish)
except rospy.ROSInterruptException: pass
class ROSNode(Widget):
def Callback(publish):
print(publish.data) #check if Callback has been called
test.text_is = publish.data
new_text_is = rospy.Publisher('new_text_is', String, queue_size=10)
rospy.Subscriber('new_text_is', String, Callback)
rospy.init_node('talker', anonymous=True)
class TestApp(App):
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
test.kv
#:kivy 1.0
<Test>:
BoxLayout:
orientation: 'vertical'
Button:
id: test_button
text: 'publish'
on_press: root.Pub()
TextInput:
id: test_text_pub
text: 'text after button press'
TextInput:
id: test_text_sub
text: root.text_is
If you want your text to automatically update, you need to use Kivy properties.
from kivy.properties import StringProperty
class Test(BoxLayout):
text_is = StringProperty('text before button press')
Kivy properties support data binding, so any updates to the property will propagate through to any bound widget. Binding happens automatically in kv, so when you do this:
text: root.text_is
..you're telling Kivy that when root.text_is is updated, update my text also.
So I found the solution:
I had to add an on_release event to the button.
on_release: test_text_sub.text = root.text_is