When pressing a key the pop-up window opens by pressing the button, it closes, but when the key is pressed again, calling the pop-up window gives an error
WidgetException('Cannot add %r, it already has a parent %r'
import json
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.popup import Popup
def read_json(file):
FileJson = open(file)
ObjJsom = json.load(FileJson)
return ObjJsom
data = read_json('Task.json')
counter = 0
task_Headline = data['Tasks'][counter]['Headline']
label = Label(text="Label test for StackoverFlow")
ConBox = BoxLayout(orientation="vertical")
clButt = Button(text="Close", size_hint=(1, 0.1))
ConBox.add_widget(label)
ConBox.add_widget(clButt)
def btn(instance):
show_popup(ConBox)
def show_popup(conten):
show = conten
popupWindow = Popup(title="Popup Window", content=show)
clButt.bind(on_press=popupWindow.dismiss)
popupWindow.open()
class Test(App):
def build(self):
butt = Button(text='Press')
butt.bind(on_press=btn)
return butt
if __name__ == '__main__':
Test().run()
When you create the Popup with:
popupWindow = Popup(title="Popup Window", content=show)
it adds the show to the Popup instance. That set the parent property of show. When the Popup is dismissed an a new Popup is created with the line above, it fails because the show instance still thinks its parent is the old Popup (even though it has been dismissed). And any widget can only have one parent. The fix is to remove the show instance from the old Popup like this:
def show_popup(conten):
# make sure the content has no parent
if conten.parent is not None:
conten.parent.remove_widget(conten)
show = conten
popupWindow = Popup(title="Popup Window", content=show)
clButt.bind(on_press=popupWindow.dismiss)
popupWindow.open()
Related
I am using kivy and I am using Windows 10, Python 3.7.
I want to print out the name of each button when I press each button.
However, only the name of the last button is printed.
Below is the Python code
import kivy
kivy.require('2.0.0')
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
import os
Window.clearcolor = (.6, 1, .8, 1)
Window.size = (563, 1001)
Window.top, Window.left = 30, 800
path_list = ["music1","music2","music3"]
music = ""
class TestApp(App):
def build(self):
global music
gl = GridLayout(cols=1, size_hint_y=None)
for music in path_list:
bt = Button(text=music, size_hint_y=None, height=50)
gl.add_widget(bt)
bt.bind(on_press=self.music_f)
sv = ScrollView(size_hint=(1, 1))
sv.add_widget(gl)
return sv
def music_f(self,name):
global music
name = music
print("pray "+str(name))
TestApp().run()
Bad design:
You use a music variable which will take the last value from the path_list due to the for loop, then you use that value to assign it to the name, this has no logic... Once the button is created with the text=music, you already have the value assign to the that button. As you bind on_press with music_f, the name should be the button, I would change that to button.
In music_f you can access the name by calling button.text
def music_f(self,button):
print("pray "+str(button.text))
I have a question that i can't seem to find an anwser.
I have this label:
l = Label(text='Some Text', font_size=100)
I have also binded text input to 'l' that looks like this:
t = TextInput(font_size=80, size_hint_y=None, height=200, text='Time', halign='right')
t.bind(text=l.setter('text'))
So when i type something to my text input box, it instantly updates the label and shows result on screen.
But i want to update that text only when user presses 'Add' button.
I am new to kivy and i am still experiment with it.
Any help would be great.
Thanks!
#edit here is my code:
https://gist.githubusercontent.com/Gacut/d765231c2696831af8c8a3315fdabbfd/raw/e1bdcdd8aca588b1c9268e76f7a47fa7576d54cb/gistfile1.txt
and what i am trying to do, is this kind of app, but for android:
https://wumpa.app/
The
t.bind(text=l.setter('text'))
will do exactly what you describe, so that is not what you want. Instead, use the Button on_release property to call a method that does what you want. The simplest way to do that is to save references to the Widgets involved (t and lista), and use those references in the new method. Like this:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
class WumpaTime(App):
def build(self):
layout = BoxLayout(orientation='vertical')
title = Label(text='Wumpa Time Countdown', size_hint_y=None, height=100, )
self.t = TextInput(font_size=80, size_hint_y=None, height=200, halign='right')
self.lista = Label(font_size=100)
box = BoxLayout()
box2 = BoxLayout()
bremove = Button(text="Remove", size_hint=(None, None), size=(100, 100))
badd = Button(text="Add", size_hint=(None, None), size=(100, 200), on_release=self.update_label)
#t.bind(text=lista.setter('text'))
box.add_widget(self.t)
box.add_widget(badd)
layout.add_widget(title)
layout.add_widget(self.lista)
layout.add_widget(box)
return layout
def update_label(self, button_instance):
self.lista.text = self.t.text
WumpaTime().run()
I'm trying to output text from button presses and add it to a textbox each time it's pressed as well as delete the text when the delete button is pressed - in normal python.
I've been trying to use kivy.uix.textinput, but I'm not sure on how to output the value from the buttons as well as delete it.
This is what I've done so far.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
class CodeScreen(Screen):
def __init__(self):
super(CodeScreen, self).__init__(name='code_screen')
main_grid_lay = GridLayout(cols=2,cols_minimum={0:640, 1:175})
self.add_widget(main_grid_lay)
#Code ouput display
display_code = TextInput(text='Code!', readonly = True, id=output)
main_grid_lay.add_widget(display_code)
#Options Buttons
box_lay = BoxLayout(orientation='vertical')
main_grid_lay.add_widget(box_lay)
delete_button = Button(
text='Delete',
size_hint_x= None,
width=160,
id='' #unsure on how to delete
)
box_lay.add_widget(delete_button)
base_left = Button(
text='Base Left',
#on_release= b_left(),
#id='left',
)
base_right = Button(
text='Base Right',
on_release=self.degree_popup,
#on_release= b_right(),
)
#b_left = self.ids.output.text='left'
#b_left = self.ids.output.text='right'
box_lay.add_widget(base_left)
box_lay.add_widget(base_right)
# The app class
class MyMain(App):
def build(self):
return CodeScreen()
# Runs the App
if __name__ == '__main__':
MyMain().run()
It currently sends an error and is probably because of the ids. Not fully sure how ids work without using kv language. Any help is appreciated.
The id only makes sense in .kv, in python they are useless. In this case the solution is to access the objects in the methods connected to on_release but for this they must be class attributes.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
class CodeScreen(Screen):
def __init__(self):
super(CodeScreen, self).__init__(name="code_screen")
self.display_code = TextInput(text="Code!", readonly=True)
self.delete_button = Button(
text="Delete", size_hint_x=None, width=160, on_release=self.delete_clicked
)
self.base_left = Button(text="Base Left", on_release=self.left_clicked)
self.base_right = Button(text="Base Right", on_release=self.right_clicked)
main_grid_lay = GridLayout(cols=2, cols_minimum={0: 640, 1: 175})
main_grid_lay.add_widget(self.display_code)
self.add_widget(main_grid_lay)
box_lay = BoxLayout(orientation="vertical")
box_lay.add_widget(self.delete_button)
main_grid_lay.add_widget(box_lay)
box_lay.add_widget(self.base_left)
box_lay.add_widget(self.base_right)
def delete_clicked(self, instance):
self.display_code.text = ""
def left_clicked(self, instance):
self.display_code.text += "left"
def right_clicked(self, instance):
self.display_code.text += "right"
# The app class
class MyMain(App):
def build(self):
return CodeScreen()
# Runs the App
if __name__ == "__main__":
MyMain().run()
Using Kivy 1.10.0 with Python 2.7.9 I am trying to get the TextInput value entered by user when Button (my_button2) is clicked .And although I have been able to get this working with GridLayout it seems like the method I am using is not working with ScreenManager with BoxLayout . Error received is : AttributeError: 'ScreenTwo' object has no attribute 'inpt' when my_button2
After clicking 'Next Screen ' button , it takes me to page where user enters text value , and 'print' button should print it
Please see below :
import kivy
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
class ScreenOne(Screen):
def __init__ (self,**kwargs):
super (ScreenOne, self).__init__(**kwargs)
my_box1 = BoxLayout(orientation='vertical')
my_button1 = Button(text="Next Screen ",size_hint_y=None,size_y=100)
my_button1.bind(on_press=self.changer)
my_box1.add_widget(my_button1)
self.add_widget(my_box1)
def changer(self,*args):
self.manager.current = 'screen2'
class ScreenTwo(Screen):
def __init__(self,**kwargs):
super (ScreenTwo,self).__init__(**kwargs)
layout = BoxLayout(orientation='vertical')
self.add_widget(layout)
inpt = TextInput(text="Some text ",size_y=50)
layout.add_widget(inpt)
my_button2 = Button(text="Print ")
my_button2.bind(on_press=self.click)
layout.add_widget(my_button2)
Home_btn = Button(text="Back")
Home_btn.bind(on_press=self.home)
layout.add_widget(Home_btn)
def click(self,my_button2):
entered_value = self.inpt.text
print entered_value
def home(self,*args):
self.manager.current = 'screen1'
class TestApp(App):
def build(self):
my_screenmanager = ScreenManager()
screen1 = ScreenOne(name='screen1')
screen2 = ScreenTwo(name='screen2')
my_screenmanager.add_widget(screen1)
my_screenmanager.add_widget(screen2)
return my_screenmanager
if __name__ == '__main__':
TestApp().run()
second screen
When you use self you are trying to access members of the class, but in your case inpt it is not since it is a variable any, if you want to be a member of the class you must put forward self, in your case change:
inpt = TextInput(text="Some text ",size_y=50)
layout.add_widget(inpt)
to:
self.inpt = TextInput(text="Some text ",size_y=50)
layout.add_widget(self.inpt)
Note: I recommend you read OOP basics, if you are not going to have many of these problems.
Following code opens window with label on top and label turns to textinput once you click on it.
But, once you start typing and insert first key (any key), text gets shortened, and you lose part of the text suddenly. For example: you click on label > textinput appears > you type '1' > text becomes 'Press here and then try 1'.
How to change below code to stop text disappearing?
import kivy
kivy.require('1.10.1')
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.base import runTouchApp
from kivy.properties import BooleanProperty, ObjectProperty
#https://github.com/kivy/kivy/wiki/Editable-Label
class EditableLabel(Label):
edit = BooleanProperty(False)
textinput = ObjectProperty(None, allownone=True)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and not self.edit:
self.edit = True
return super(EditableLabel, self).on_touch_down(touch)
def on_edit(self, instance, value):
if not value:
if self.textinput:
self.remove_widget(self.textinput)
return
self.textinput = t = TextInput(
text=self.text, size_hint=(None, None),
font_size=self.font_size, font_name=self.font_name,
pos=self.pos, size=self.size, multiline=False)
self.bind(pos=t.setter('pos'), size=t.setter('size'))
self.add_widget(self.textinput)
t.bind(on_text_validate=self.on_text_validate, focus=self.on_text_focus)
def on_text_validate(self, instance):
self.text = instance.text
self.edit = False
def on_text_focus(self, instance, focus):
if focus is False:
self.text = instance.text
self.edit = False
if __name__ == '__main__':
root = FloatLayout()
lbl = 'Press here and then try to edit (type a character), but text gets shortened suddenly.'
label = EditableLabel(text=lbl, size_hint_y=None, height=50, pos_hint={'top': 1})
root.add_widget(label)
runTouchApp(root)
According to the docs:
The selection is automatically updated when the cursor position changes. You can get the currently selected text from the TextInput.selection_text property.
and in your case when you click on the Label and appear in TextInput the cursor changes position so a text is selected. And when a text is selected and you write something it is replaced, that's why the text disappears.
The solution is to clean the selection:
from kivy.clock import Clock
class EditableLabel(Label):
[...]
def on_edit(self, instance, value):
if not value:
if self.textinput:
self.remove_widget(self.textinput)
return
self.textinput = t = TextInput(
text=self.text, size_hint=(None, None),
font_size=self.font_size, font_name=self.font_name,
pos=self.pos, size=self.size, multiline=False)
self.bind(pos=t.setter('pos'), size=t.setter('size'))
self.add_widget(self.textinput)
t.bind(on_text_validate=self.on_text_validate, focus=self.on_text_focus)
Clock.schedule_once(lambda dt: self.textinput.cancel_selection())