I have a question about kivy and it runs weird on anaconda
First weird thing: I have to add into the kivy file then run it and delete the <> from the MenuScreen in the kivy code and run the file again in order for my code to runs like I want. if I don't do this the code will give FactoryException: Unknown class <MenuScreen>
Second weird thing is that after I end kivy and run it again it doesn't let me change screen on the app, I have to restart the shell and do everything like the (first weird thing above) for it to switch screen and run properly.
Here is the code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
kv = Builder.load_file('mymain.kv')
class MainWindow(Screen):
pass
class SecondWindow(Screen):
point = ObjectProperty(None)
note = ObjectProperty(None)
def pressed(self):
''' this fuction is the function of the good action button'''
point1 = self.point.text
Note1 = self.note.text
try:
point1 = int(point1)
print("You just got: ",point1)
print('Note of this action: ')
print(Note1)
except:
if point1 == '':
print('please enter something')
else:
print("point should be a number")
#print(point1, Note1)
self.point.text = ""
self.note.text = ''
#return grid
def increased(self):
''' this function is to increase the mark'''
point2 = self.point.text
try:
point2 = int(float(point2))+1
print('increase point is', point2)
#self.point.text = str(point2)
except:
point2 = 1
print('your point is: ','1')
self.point.text = str(point2)
def decreased(self):
''' this function decrease the point'''
point3 = self.point.text
try:
point3 = int(float(point3))-1
print('decrease point is', point3)
#self.point.text = str(point3)
except:
point3 = -1
print('your point is: ', '-1')
self.point.text = str(point3)
class MenuScreen(ScreenManager):
pass
class MyMainApp(App):
def build(self):
return kv
if __name__ == '__main__':
MyMainApp().run()
Here is the mymain.kv
MenuScreen:
MainWindow:
SecondWindow:
<MainWindow>:
name: "main"
GridLayout:
cols: 1
Button:
text: 'Good'
on_release: app.root.current = 'second'
Button:
text: 'Bad' #not doing anything yet
<SecondWindow>:
name: 'second'
point: point
note: note
GridLayout:
cols: 1
size: root.width, root.height
GridLayout:
cols: 3
Label:
text: 'Good Point: '
TextInput:
id: point
multiline: False
GridLayout:
cols: 1
Button:
text: 'up'
on_press: root.increased()
Button:
text: 'down'
on_press: root.decreased()
Label:
text: 'Note: '
TextInput:
id: note
multiline: True
Button:
text: ' submit'
on_press: root.pressed()
please help me. Idk why kivy runs like this. Thank you
The problem is that your mymain.kv is getting loaded twice. Kivy will automatically load a kv file if it is named correctly (as your is - See the documentation), and you are also loading it via a Builder.load_string() call. The fix is to remove the line:
kv = Builder.load_file('mymain.kv')
and simplify your App class to:
class MyMainApp(App):
pass
# def build(self):
# return kv
in mymain.kv change:
MenuScreen:
MainWindow:
SecondWindow:
to
<MenuScreen>:
MainWindow:
SecondWindow:
Related
I'm trying to create a chatbot. As soon as I click the button it creates a new widget but if the input is too long it goes off screen. I'm not able to figure out how to keep all the code in the screen.
Python code
from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.widget import Widget
import time
class MyFL(FloatLayout):
input = ObjectProperty(None)
def btn(self):
self.add_widget(Label( text= '>$' + self.input.text, pos=(-650, 100)))
class MyApp(App):
def build(self):
return MyFL()
if __name__ == "__main__":
MyApp().run()
Kv code
<FloatLayout>:
input:input
TextInput:
id: input
multiline: False
hint_text: "Say Something"
size_hint: 1, 0.05
font_size: 20
Button:
size_hint: 0.05, 0.05
pos_hint: {'center_x': 0.5, 'y': 0.06 }
on_press: root.btn()
Since Label doesn't do line wrapping, I suggest using a readonly TextInput. In your python you can define a custom TextInput:
class MyTextInput(TextInput):
pass
and use it in your btn() method:
def btn(self):
self.add_widget(MyTextInput(text='>$' + self.input.text, pos_hint={'y':0.15}))
Plus a rule in your 'kv` for the new class:
<MyTextInput>:
size_hint: 1, None
height: self.minimum_height
readonly: True
I am not sure if it works perfectly in all case (but I tested, might be ok)
I do in this way:
In main.py
import kivy
from kivy.uix.label import Label
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.clock import Clock
# I use this to load kivy string
# easier for me to manipulate : )
kivy.lang.Builder.load_string("""
#:kivy 2.0.0
<MyFL>:
input: input
TextInput:
id: input
multiline: False
hint_text: "Say Something"
size: root.width, root.height *0.05
font_size: 20
focus: True
on_text_validate: # this allow you to add widget when hit enter in the textinput
root.btn() # by calling the func btn()
# Button:
# size_hint: 0.05, 0.05
# pos_hint: {'center_x': 0.5, 'y': 0.06 }
# on_release:
# root.btn()
""")
class MyFL(Widget): # I changed the class to inherit widget instead of floatlayout, just the positioning is different, you can change this 🤔😅
def btn(self):
label = Label(text = '>$ ' + self.input.text, center = self.center)
label.multiline = True
label.font_size = 25
self.add_widget(label)
def update(self, dt): # newly added #
# pass
for i in self.children: # loop through all children
if i != self.ids.input and i.texture_size[0] > self.width: # if children is label
# see how many line it should split
lines = round(i.texture_size[0] / self.width) + 1
for split in range(lines): # split the text
text = i.text.split(" ")
index = int(len(text)*split / lines)
text.insert(index, "\n")
text = " ".join(text)
i.text = text
class MyApp(App):
main = MyFL()
def build(self):
Clock.schedule_interval(self.main.update, 0.1) # schedule the clock to make update
return self.main
if __name__ == "__main__":
MyApp().run()
Explanations are in the code above, this is not the perfect answer but I hope it helps.
I know this is a duplicate, but no answers for this have worked for me.
I am trying to add a widget which serves as an input line with a prompt.
I am getting a name error stating that 'prompt_text' in line 2 of client.kv is not defined.
I believe this is an issue to do with the StringProperty() not being run.
Here is my python file:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.widget import Widget
import kivy.properties as kyProps
import ChatClient
CLIENT = None
VERSION = open("Version.txt").read()
class LoginWindow(Screen):
namee = kyProps.ObjectProperty(None)
password = kyProps.ObjectProperty(None)
def loginBtn(self):
global CLIENT
CLIENT = ChatClient(self.namee.ti.text, self.password.ti.text, VERSION)
if CLIENT.didConFail:
errorMsg = f"Failed to connect. Reason: {CLIENT.connectFailedReason}"
pop = Popup(title='Login Error', content=Label(text=errorMsg))
pop.open()
elif "REFUSED" in CLIENT.finalSetUpMsg:
pop = Popup(title='Login Error', content=Label(text=CLIENT.finalSetUpMsg))
pop.open()
else:
sm.current = "main"
class MainWindow(Screen):
pass
class StyleEditorWindow(Screen):
pass
class PMWindow(Screen):
pass
class ChatWidget(Widget):
pass
class InputLineWidget(Widget):
prompt_text = kyProps.StringProperty("")
ti = kyProps.ObjectProperty(None)
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("client.kv")
sm = WindowManager()
screens = [LoginWindow(name="login"), MainWindow(name="main"), StyleEditorWindow(name="styleEditor"), PMWindow(name="pm")]
for screen in screens:
sm.add_widget(screen)
sm.current = "login"
class CpClientApp(App):
def build(self):
return sm
if __name__ == "__main__":
CpClientApp().run()
And here is my client.kv file:
<InputLineWidget>:
prompt_text: prompt_text
ti: ti
GridLayout:
cols: 2
size: root.width, root.height
Label:
text: prompt_text
TextInput:
id: ti
multiline: false
<ChatWidget>:
<LoginWindow>:
namee: namee
password: password
size: root.width, root.height
GridLayout:
cols: 1
InputLineWidget:
id: namee
prompt_text: "Name: "
InputLineWidget:
id: password
prompt_text: "Password: "
Button:
text: "Log In"
on_release:
root.manager.transition.direction = "up"
root.loginBtn()
<MainWindow>:
<StyleEditorWindow>:
<PMWindow>:
I've tried many solutions based on others encountering similar issues but none have worked.
Im running python 3.7 on Windows 10 using Kivy 2.0.0
Any help would be greatly appreciated. Thank you.
The line:
prompt_text: prompt_text
creates an ObjectProperty named prompt_text that contains a reference to an object that has an id of prompt_text. But since that id is not defined in the <InputLineWidget> rule of your kv file, you get the noted error.
If you want the prompt_text to be used as the text for the label, change the kv to:
<InputLineWidget>:
ti: ti
GridLayout:
cols: 2
size: root.width, root.height
Label:
text: root.prompt_text
TextInput:
id: ti
multiline: false
I'm trying to make a program that puts x amount of TextInputs based on the user's input. I can do that in the py file, but how do I do this in the kv file? I also need to be able to reference these TextInputs later on.
This is what I have so far:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.textinput import TextInput
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
text_inputs = [TextInput(multiline=False) for i in range(int(self.count_input.text))]
for text_input in text_inputs:
self.manager.ids.get_input.ids.grid.add_widget(text_input)
# Switch to next screen
self.manager.current = "get_input"
class GetInput(Screen):
pass
kv_file = Builder.load_string("""
ScreenManager:
GetCount:
name: "get_count"
id: get_count
GetInput:
name: "get_input"
id: get_input
<GetCount>:
count_input: count_input
FloatLayout:
Label:
text: "count"
size_hint: 1, 0.05
pos_hint: {"top":0.9}
TextInput:
id: count_input
size_hint: 0.8, 0.05
pos_hint: {"top":0.7, "x":0.1}
multiline: False
Button:
text: "Next"
on_release: root.next()
size_hint: 0.8, 0.05
pos_hint: {"top":0.5, "x":0.1}
<GetInput>:
FloatLayout:
GridLayout:
id: grid
cols: 1
""")
class MainApp(App):
def build(self):
return kv_file
if __name__ == "__main__":
app = MainApp()
app.run()
The problem is, the TextInputs are being added and created by the py code, how can I create the widget in kv and then add it in py?
I'm currently working to update a bunch of labels with a button press, which I was able to get an answer with through Kivy: How to refernce kv ID in Python?
However, now that I'm in my actual app, I need the functionality to be able to take a user input value and update existing labels. I've modified the example slightly where I'm just taking a user input for a starting number, adding one with each button click and displaying the running total to the right of all of this.
The code I have now is shown below, but the addition of 1 doesn't seem to be processing (it's just staying with the same total after a click). Any ideas why? Thanks very much!
This is the display before the button is clicked (running total is just the user input):
My Python file:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
import random
class TestingWidget(BoxLayout):
# This is the kv id of the Label I would like to update
starting_number = StringProperty('Put your Starting Number here')
running_total = StringProperty(str(0))
#default text set
# This is the action I would like to happen when the button is pressed
def button_pressed(self):
self.running_total = str(int(self.running_total) + 1)
class TestButtonApp(App):
def build(self):
return TestingWidget()
if __name__ == '__main__':
TestButtonApp().run()
My kv file:
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
TextInput:
id: starting_number
hint_text: root.starting_number
Button:
id: add_one_button
text: 'Add 1 to Starting Number'
on_press: root.button_pressed()
Label:
id: running_total
text: starting_number.text
the problem is that Label is bound with TextInput, and if you change Label as you are doing it the TextInput updates it again giving the feeling that it does not change. What you should do is change the text with the on_text event as shown below:
*.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
class TestingWidget(BoxLayout):
starting_number = StringProperty("")
running_total = StringProperty(str(0))
def button_pressed(self):
if self.running_total != "":
self.running_total = str(int(self.running_total) + 1)
def text_changed(self, instance, value):
self.running_total = value
class TestButtonApp(App):
def build(self):
return TestingWidget()
if __name__ == '__main__':
TestButtonApp().run()
*.kv
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
TextInput:
id: starting_number
hint_text: "Put your Starting Number here"
on_text: root.text_changed(*args)
text: root.starting_number
Button:
id: add_one_button
text: 'Add 1 to Starting Number'
on_press: root.button_pressed()
Label:
id: running_total
text: root.running_total
The solution is to only change your kv File and no changes required to your Python Script. The changes are as follow:
kv File
1. TextInput: Add on_text Event
The textinput’s text is stored in its TextInput.text property. To run a callback when the text changes, do the following.
on_text:
root.running_total = self.text
2. Label: Reference StringProperty, root.running_total
Replace:
text: starting_number.text
with:
text: root.running_total
testbutton.kv
#:kivy 1.10.0
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
TextInput:
id: starting_number
hint_text: root.starting_number
on_text:
root.running_total = self.text
Button:
id: add_one_button
text: 'Add 1 to Starting Number'
on_press: root.button_pressed()
Label:
id: running_total
text: root.running_total
Output
Short version:
How can I alter button text displayed onscreen from the main.py file of a Kivy application?
Longer version:
I'm putting togther a multiple-choice game using Kivy.
I have the gameplay working as I want it to at the python level: when the user clicks on the correct answer button they are awarded points and the answers attached to the buttons are changed. Under the bonnet everything seems to be fine.
My problem is that I can't work out how to update the text on the buttons displayed onscreen from the main.py file. The game plays fine, but the text displayed on the buttons never alters. How do I fix my code to do this?
Below is a simplified version of what I'm tring to do:
My main.py file:
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.properties import ObjectProperty
from functools import partial
import random
vocabDict = {'latte': 'milk', 'mela': 'apple', 'melograno': 'pomegranate', 'fragola': 'strawberry', 'acqua': 'water'}
vocabList = ['acqua', 'latte', 'mela', 'melograno', 'fragola']
currentWord = 'acqua'
score = 0
class ScreenOne(Screen):
pass
class ScreenTwo(Screen):
def __init__(self,**kwargs):
super(ScreenTwo, self).__init__(**kwargs)
Screen.currentWord = self.getCurrentWord()
Screen.questionText = Screen.currentWord
Screen.vocabDict = self.getVocabDict()
Screen.currentScore = self.getCurrentScore()
Screen.possibleAnswerList = [Screen.currentWord]
self.playHand(Screen.currentWord,Screen.vocabDict,Screen.currentScore)
def getCurrentWord(self):
return currentWord
def getVocabDict(self):
return vocabDict
def getCurrentScore(self):
return score
def playHand(self,currentWord,vocabDict,score):
possibleAnswerList = [currentWord]
currentWord = currentWord
vocabDict = vocabDict
currentScore = score
while len(possibleAnswerList) < 3:
potentialChoice = random.choice(vocabDict.keys())
if potentialChoice not in possibleAnswerList:
possibleAnswerList.append(potentialChoice)
random.shuffle(possibleAnswerList)
# How do I visualize these changes on screen?
Screen.button1Text = vocabDict[possibleAnswerList[0]]
Screen.button2Text = vocabDict[possibleAnswerList[1]]
Screen.button3Text = vocabDict[possibleAnswerList[2]]
Screen.possibleAnswerList = possibleAnswerList
print "Screen.button1Text = " + Screen.button1Text
print "Screen.button2Text = " + Screen.button2Text
print "Screen.button3Text = " + Screen.button3Text
def button1Pressed(instance):
print "Screen.possibleAnswerList[0] = " + Screen.possibleAnswerList[0]
print "Screen.currentWord = " + Screen.currentWord
if Screen.possibleAnswerList[0] == Screen.currentWord:
print "Correct!"
Screen.currentScore += 1
print Screen.currentScore
instance.playHand(Screen.currentWord,Screen.vocabDict,Screen.currentScore)
else:
print "Incorrect!"
def button2Pressed(instance):
if Screen.possibleAnswerList[1] == Screen.currentWord:
print "Correct!"
Screen.currentScore += 1
print instance.currentScore
instance.playHand(Screen.currentWord,Screen.vocabDict,Screen.currentScore)
else:
print "Incorrect!"
def button3Pressed(instance):
if instance.possibleAnswerList[2] == currentWord:
print "Correct!"
instance.currentScore += 1
print instance.currentScore
instance.playHand(Screen.currentWord,Screen.vocabDict,Screen.currentScore)
else:
print "Incorrect!"
class Manager(ScreenManager):
screen_one = ObjectProperty(None)
screen_two = ObjectProperty(None)
class ScreensApp(App):
def build(self):
m = Manager(transition=NoTransition())
return m
if __name__ == "__main__":
ScreensApp().run()
My screens.kv file:
#:kivy 1.8.0
<ScreenOne>:
BoxLayout:
orientation: "vertical"
size: root.size
spacing: 20
padding: 20
Label:
text: "Main Menu"
Button:
text: "Button 1"
on_release: root.manager.current = "screen2"
<ScreenTwo>:
BoxLayout:
orientation: "vertical"
size: root.size
spacing: 20
padding: 20
Label:
id: label
text: root.questionText
Button:
id: button1
text: root.button1Text
on_release: root.button1Pressed()
Button:
id: button2
text: root.button2Text
on_release: root.button2Pressed()
Button:
id: button3
text: root.button3Text
on_release: root.button3Pressed()
<Manager>:
id: screen_manager
screen_one: screen_one
screen_two: screen_two
ScreenOne:
id: screen_one
name: "screen1"
manager: screen_manager
ScreenTwo:
id: screen_two
name: "screen2"
manager: screen_manager
As should be pretty evident, I'm a total beginner at Kivy, so I'd really appreciate it if you could show me exactly what I need to change, including the specific syntax that should be used.
Thanks in advance for your time and wisdom.
You need to make button1/2/3Text a StringProperty such as
from kivy.properties import StringProperty # <---- (:
class ScreenTwo(Screen):
button1Text = StringProperty ('some text')
button2Text = StringProperty ('some text2')
button3Text = StringProperty ('some text3')
def __init__(...):
...
#notice that Screen was replaced by "self"
# How do I visualize these changes on screen?
self.button1Text = vocabDict[possibleAnswerList[0]]
self.button2Text = vocabDict[possibleAnswerList[1]]
self.button3Text = vocabDict[possibleAnswerList[2]]
Please also replace Screen with self in all other places as well since you are putting variables on a kivy class and its weird ;)
I hope this will help you