I'm trying to have kivy select the text of a TextInput widget on focus but when I try it seems to select it when it unfocuses and retains the selection. Any ideas how I can select it on focus and on unfocus deselect? I've attached my code below if someone wants to have a play around.
kv file:
<TextInput>:
size_hint: 0.9, 0.5
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
multiline: False
<Button>:
text: "Press Me"
size_hint: (0.1, 0.5)
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
<MainLayout>:
canvas.before:
Color:
rgba: 0.15, 0.15, 0.16, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
padding: 10
BoxLayout:
padding: 10
TextInput:
text: "Directory"
Button:
text: "Browse"
on_press: root.browse_btn()
BoxLayout:
padding: 10
TextInput:
text: "Prefix"
on_focus: self.select_all()
TextInput:
text: "File"
on_focus: self.select_all()
TextInput:
text: "Suffix"
on_focus: self.select_all()
BoxLayout:
padding: 10
Button:
id: button_one
text: "Confirm"
on_press: root.confirm_btn()
Button:
text: "Cancel"
on_press: root.cancel_btn()
python file:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.config import Config
Config.set('graphics', 'resizable', 0)
class MainLayout(BoxLayout):
button_id = ObjectProperty(None)
def browse_btn(self):
print("Hey")
def confirm_btn(self):
print("Confirm")
def cancel_btn(self):
print("Cancel")
class BatchRenameApp(App):
def build(self):
self.title = "Batch File Rename"
Window.size = (750, 250)
return MainLayout()
if __name__ == '__main__':
app = BatchRenameApp()
app.run()
Well hidden in the TextInput documentation:
Selection is cancelled when TextInput is focused. If you need to show
selection when TextInput is focused, you should delay (use
Clock.schedule) the call to the functions for selecting text
(select_all, select_text).
So, in your kv, start by importing Clock:
#: import Clock kivy.clock.Clock
Then you can use it in a TextInput rule:
TextInput:
text: "Prefix"
on_focus: Clock.schedule_once(lambda dt: self.select_all()) if self.focus else None
The if self.focus makes sure the select_all only happens when the TextInput gains focus.
Related
I keep getting a Name Error reporting "name TripButton is not defined" although I have a button called TripButton defined in the same .kv file.
#:kivy 1.11.1
<TripButton#Button>:
size_hint: (0.15,0.15)
pos_hint: {'y':0.84}
text:"Test Text"
<MyPopup>:
size_hint: 0.5, 0.5
auto_dismiss: False
title: 'New Trip'
BoxLayout:
orientation: 'vertical'
TextInput:
id: trip_name
multiline: False
BoxLayout:
size_hint_y: None
height: 30
Button:
text: 'Submit'
on_release:
root.parent.parent.add_widget(TripButton(text=trip_name.text, size_hint=(1,0.1)))
root.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
<FirstScreen>:
name: 'first'
FloatLayout:
BoxLayout:
size_hint: (0.95, 0.95)
pos_hint: {'center_x':0.5, 'center_y':0.5}
orientation: "vertical"
canvas.before:
Color:
rgba: (1, 0, 0, 1) # Red color
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
TripButton:
Button:
text: 'Add Trip'
font_size: 12
size_hint: (0.1, 0.1)
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
I tried declaring the TripButton class above and below the MyPopup class and that didn't work. I compared my code with the kivy documentation for dynamic classes and the from what I can tell I'm matching the syntax. I tried importing the Button class in both the python file and the .kv file and that hasn't worked.
To clarify all of this is in the same .kv file in the same directory as the python file. the app runs until I click the submit button in MyPopup then the program crashes and I get the NameError. This is what I have in my python file.
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.popup import Popup
class FirstScreen(Screen):
pass
class MyPopup(Popup):
pass
class MyApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(FirstScreen(name='first'))
sm.current = 'first'
return sm
def open_popup(self):
p = MyPopup()
p.open()
def add_button(self):
pass
if __name__ == '__main__':
MyApp().run()
What am I doing wrong? What else I can try? Thanks for your time.
To access classes defined within kv file use kivy.factory.Factory
Add id attribute to your button's BoxLayout, so replace following part of kv file:
BoxLayout:
TripButton:
with
BoxLayout:
id: trip_buttons
TripButton:
then replace:
root.parent.parent.add_widget(TripButton(text=trip_name.text, size_hint=(1,0.1)))
with:
app.root.current_screen.ids.trip_buttons.add_widget(kivy.factory.Factory.TripButton(text=trip_name.text, size_hint=(1,0.1)))
You also duplicated following lines within last button definition on first screen. Replace:
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
with
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
My problem is when I want to display the value given by the user in the Text input text field.
When I want to get the text value from the input I get an empty value and I cannot display anything in the label field .
Here is my code :
The kv file :
MainGridLayout:
<MainGridLayout>:
cols: 1
rows: 2
BoxLayout:
orientation: 'vertical'
TextInput:
id: my_text_input
text: root.text_input_1
hint_text :'Operations'
multiline:False
pos_hint: {'center_x': 0.5, 'center_y': 0.705}
size_hint: 0.95, 0.5
font_size: '30dp'
Label:
text: root.Display_text
font_size: '45dp'
StackLayout:
Button:
text:"+"
spacing: .2, .2
size: 85, 85
size_hint: None, None
on_press: root.on_button_plus()
The py file :
from kivy.app import App
from kivy.properties import StringProperty
from kivy.uix.gridlayout import GridLayout
# different layouts:
class MainGridLayout(GridLayout):
text_input_1 = StringProperty()
Display_text = StringProperty()
def on_button_plus(self):
print(f'{self.text_input_1}')
# run application:
class CalculatorApp(App):
pass
CalculatorApp().run()
I have created a function in MainGridLayout, on_new_text, that takes as a parameter a TextInput and updates text_input_1 with its value.
In the kv file I have set the on_text property of the TextInput to the on_new_text function and gave the TextInput as a parameter. Every time you modify the text input, the on_new_text function will be called, modifying the text_input_1 property.
You can find more properties like on_text in the kivy documentation.
The kv file:
MainGridLayout:
<MainGridLayout>:
cols: 1
rows: 2
BoxLayout:
orientation: 'vertical'
TextInput:
id: my_text_input
hint_text :'Operations'
multiline:False
pos_hint: {'center_x': 0.5, 'center_y': 0.705}
size_hint: 0.95, 0.5
font_size: '30dp'
on_text: root.on_new_text(self)
Label:
text: root.Display_text
font_size: '45dp'
StackLayout:
Button:
text:"+"
spacing: .2, .2
size: 85, 85
size_hint: None, None
on_press: root.on_button_plus()
The py file:
from kivy.app import App
from kivy.properties import StringProperty
from kivy.uix.gridlayout import GridLayout
# different layouts:
class MainGridLayout(GridLayout):
text_input_1 = StringProperty()
Display_text = StringProperty()
def on_new_text(self, text_input):
self.text_input_1 = text_input.text
def on_button_plus(self):
print(f'{self.text_input_1}')
class CalculatorApp(App):
pass
CalculatorApp().run()
Actually, you don't need the variables text_input_1 and Display_text. To display the text from the my_text_input on the label you can add text: my_text_input.text instead of text: root.Display_text. Now, when the text of my_text_input changes, the label's text will change automatically and you don't need any functions. In on_button_plus() method you can do the same and print my_text_input's text using its text property (from all MainGridLayout's ids you take only my_text_input and print its text). The line text: root.text_input_1 unnecessary, too.
Here is my editted version of your code:
The .py file
from kivy.app import App
from kivy.properties import StringProperty
from kivy.uix.gridlayout import GridLayout
class MainGridLayout(GridLayout):
def on_button_plus(self):
print(f'{self.ids.my_text_input.text}')
class CalculatorApp(App):
pass
CalculatorApp().run()
The .kv file
MainGridLayout:
<MainGridLayout>:
cols: 1
rows: 2
BoxLayout:
orientation: 'vertical'
TextInput:
id: my_text_input
hint_text :'Operations'
multiline:False
pos_hint: {'center_x': 0.5, 'center_y': 0.705}
size_hint: 0.95, 0.5
font_size: '30dp'
Label:
text: my_text_input.text # change the text automatically
font_size: '45dp'
StackLayout:
Button:
text:"+"
spacing: .2, .2
size: 85, 85
size_hint: None, None
on_press: root.on_button_plus()
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
.
.
.
I have a app in kivy with a screen manager and a popup within it. The popup works until the point I put a button with the close function into the popup window. At this point i get the message:
PopupException: Popup can have only one widget as content
There is another post on this topic but it does not seem to work.
The python code
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.lang import Builder
from kivy.uix.popup import Popup
class CustomPopup(Popup):
pass
class MainScreen(Screen):
pass
class ContentScreen(Screen):
def open_popup(self):
the_popup = CustomPopup()
the_popup.open()
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("am.kv")
class AMApp(App):
def build(self):
return presentation
if __name__ == "__main__":
AMApp().run()
The kivy file is below. The issue seems to come in the button function when calling the custompop
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
ContentScreen:
<CustomPopup>:
size_hint: .5 , .5
auto_dismiss: False
title: "The Popup"
Button:
text: "Close"
on_press: root.dismiss()
<MainScreen>:
name: "Welcome"
Button:
text: "First Screen"
size_hint: 1, .5
font_size: 40
pos_hint: {'center_x': 0.5, 'center_y': 0.7}
on_release: app.root.current = "other"
Button:
text: 'Welcome Mr and Mrs Shaw'
size_hint: 1, .5
font_size: 25
pos_hint: {'center_x': 0.5, 'center_y': 0.3}
on_release: app.root.current = "other"
<ContentScreen>:
name: "other"
BoxLayout:
orientation: "vertical"
size_hint_x: .22
Button:
text: "open Popup"
on_press: root.open_popup()
The code posted above, runs fine on Linux Buster, Kivy 1.11.0-dev and 1.10.1, and Python 3.7.3rc1
Solution
Try adding a layout e.g. BoxLayout into CustomPopup to solve the PopupException.
Example
The following example illustrates a popup window with a message and a button.
Snippets
<CustomPopup>:
size_hint: .5 , .5
auto_dismiss: False
title: "The Popup"
BoxLayout:
orientation: 'vertical'
Label:
text: "Hello Kivy"
Button:
text: "Close"
on_press: root.dismiss()
Can anyone tell me how to add a date picker or kivy calendar on date TextBox?
I have two file test.py and test.kv file.
test.py
import kivy
import sqlite3 as lite
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.label import Label
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (400, 125)
class Testing(Screen):
pass
class Testing(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
if __name__ == '__main__':
Testing().run()
test.kv
Testing:
BoxLayout:
orientation: "vertical"
padding : 20, 20
BoxLayout:
orientation: "horizontal"
padding: 10, 10
spacing: 10, 10
size_hint_x: .55
Label:
text: "Date"
text_size: self.size
valign: 'middle'
size_hint_x: .2
TextInput:
size_hint_x: .3
BoxLayout:
orientation: "horizontal"
padding : 10, 0
spacing: 10, 10
size_hint: .5, .7
pos_hint: {'x': .25, 'y':.25}
Button:
text: 'Ok'
on_release:
root.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
.kv file
#:import MDTextField kivymd.uix.textfield.MDTextField
MDTextField:
id: set_date
hint_text: 'Start Date'
write_tab: False
on_focus: root.setDate()
.py file
from kivymd.uix.picker import MDDatePicker
def setDate(self,dobj):
self.ids.set_date.text = str(dobj)
pass
def fromDate(self):
self.foc = self.ids.set_date.focus
if(self.foc==True):
MDDatePicker(self.setFDate).open()
else:
print("not")