How to switch text of a label in kivy - python

I am creating myself a GPA calculator using python kivy and I am trying to switch the text of a label but it is not working. I could use some help. I am only going to show the code that I require to change the text, but if you need it all, I am happy to send it through.
.py file:
from kivy.core.window import Window
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.popup import Popup
class main(Screen):
def add_class(self):
sm.current = 'add'
sm.transition.direction = 'left'
def remove_class(self):
sm.current = 'remove'
sm.transition.direction = 'right'
def update(self):
#from the screen that adds the courses
courses = AddClass.courses
marks = []
for key in courses:
marks.append(courses[key])
total = 0.0
for i in marks:
total += i
total /= len(marks)
#print(total)
self.ids.gpa.text = f'{total}/4.0'
class MyApp(App):
def build(self):
#main screen
sm.add_widget(main(name='main'))
#screen that adds the marks
sm.add_widget(AddClass(name='add'))
#screen that removes a certain mark
sm.add_widget(RemoveClass(name='remove'))
sm.current = 'main'
return sm
if __name__ == '__main__':
MyApp().run()
.kv file:
<main>
FloatLayout:
Button:
text: 'Your GPA'
font_size: 50
size_hint: 0.4, 0.4
pos_hint: {'x': 0.3, 'y': 0.6}
id: gpa
background_color: 0, 0, 0
on_release: print(self.text)
Button:
text: 'Remove Class/Course'
font_size: 28
id: remove_class
size_hint: 0.4, 0.4
pos_hint: {'y': 0.1, 'x': 0.05}
on_release: root.remove_class()
Button:
text: 'Add Class/Course'
font_size: 30
id: add_class
size_hint: 0.4, 0.4
pos_hint: {'y': 0.1, 'x': 0.55}
on_release: root.add_class()
Help is much appreciated!

Text of a label can be a kivy property, which can be later changed and since it is a kivy property it will automatically update everywhere. Here is an example of .py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
import random
class YourWidget(Widget):
random_number = StringProperty()
def __init__(self, **kwargs):
super(YourWidget, self).__init__(**kwargs)
self.random_number = str(random.randint(1, 100))
def change_text(self):
self.random_number = str(random.randint(1, 100))
class YourApp(App):
def build(self):
return YourWidget()
if __name__ == '__main__':
YourApp().run()
and .kv example
<YourWidget>:
BoxLayout:
size: root.size
Button:
id: button1
text: "Change text"
on_release: root.change_text()
Label:
id: label1
text: root.random_number
When you click the button, it will call the change_text() function, which will randomly change the text of the label to a random integer between 1 and 100.

Related

OCR using Kivy and Screenmanager issue passing variable between screens

I am building an OCR application for visually impaired users. I'm wanting the app to open straight away onto the camera screen, when the user takes a pic on button press I want ocr process to occur and display the output on the text screen in a lbl or txtbox and have a TTS read out what the text says. My issue is that i am having trouble obtaining the output of ocr and displaying it, I'm not familiar with screenmanager or python. ideally the opencv and tesseract process would occur in same function as the capture however i cant get the output recognised on the following screen. heres some code, any suggestions and help appreciated!
# Importing the libraries
import cv2
import pytesseract
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
pytesseract.pytesseract.tesseract_cmd = r'D:/pytesseract/tesseract.exe'
# voice_text = ""
# for i in ocrtext.split():
# voice_text += i + ' '
# voice_text = voice_text[:-1]
# voice_text
# engine = pyttsx3.init()
# engine.setProperty("rate", 145)
# voices = engine.getProperty('voices')
# engine.setProperty('voice', voices[0].id)
# engine.say(voice_text)
# engine.runAndWait()
class CameraScreen(Screen):
def capture(self):
camera = self.ids['camera']
camera.export_to_png("./picforocr.png")
image = cv2.imread("./picforocr.png")
ocrtext = pytesseract.image_to_string(image)
class TextScreen(Screen):
pass
GUI = Builder.load_string("""
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
CameraScreen:
name: "camera_screen"
id: camera_screen
TextScreen:
name: "text_screen"
id: text_screen
<CameraScreen>:
orientation: 'vertical'
GridLayout:
cols: 1
Camera:
id: camera
resolution: (800, 800)
Button:
text: 'OCR!'
size_hint_y: None
height: '48dp'
on_press:
root.capture()
# root refers to <CameraScreen>
# app refers to TestCamera, app.root refers to the GridLayout: at the top
app.root.ids['screen_manager'].transition.direction = 'left'
app.root.ids['screen_manager'].current = 'text_screen'
<TextScreen>:
Label:
id: ocr_output
text:
Camerascreen.ocrtext
font_size: 20
Button:
text: "Do OCR Again"
size_hint_y: None
height: '48dp'
font_size: 50
on_press:
app.root.ids['screen_manager'].transition.direction = 'right'
app.root.ids['screen_manager'].current = 'camera_screen'
""")
class MyOCRApp(App):
def build(self):
return GUI
if __name__ == "__main__":
MyOCRApp().run()
This is basic code for passing texts from one class to another and also TextInput data. This is python code:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition
class FirstPage(FloatLayout):
def switch_screen(self):
secondpage = self.login_name = self.ids.login_name.text
myapp.secondpage.update_name1(secondpage)
secondpage = self.password_name = self.ids.password_name.text
myapp.secondpage.update_name2(secondpage)
myapp.screen_manager.transition = SlideTransition(direction='left', duration=.25)
myapp.screen_manager.current = 'SecondPage'
class SecondPage(FloatLayout):
def switch_back(self):
myapp.screen_manager.transition = SlideTransition(direction='right', duration=.25)
myapp.screen_manager.current = 'FirstPage'
def update_name1(self, name_login):
self.ids.name_login.text = (f'{name_login}:')
def update_name2(self, name_password):
self.ids.name_password.text = (f'{name_password}:')
class MyApp(App):
def build(self):
self.screen_manager = ScreenManager()
self.firstpage = FirstPage()
screen = Screen(name='FirstPage')
screen.add_widget(self.firstpage)
self.screen_manager.add_widget(screen)
self.secondpage = SecondPage()
screen = Screen(name='SecondPage')
screen.add_widget(self.secondpage)
self.screen_manager.add_widget(screen)
return self.screen_manager
myapp = MyApp()
myapp.run()
And this is kivy code:
FirstPage:
<FirstPage>:
TextInput:
id: login_name
size_hint: .65, .08
multiline: False
font_size: 20
pos_hint: {'x': .2, 'y': .57}
TextInput:
id: password_name
size_hint: .65, .08
multiline: False
font_size: 20
pos_hint: {'x': .2, 'y': .47}
Button:
text: "First"
size_hint: .1, .1
on_release: root.switch_screen()
<SecondPage>:
Button:
text: "Second"
size_hint: .1, .1
on_release: root.switch_back()
Label:
id: name_login
text: "Login"
size_hint: .2, .2
pos_hint: {'x': .2, 'y': .57}
Label:
id: name_password
text: "Password"
size_hint: .2, .2
pos_hint: {'x': .2, 'y': .47}

How to keep label from going off the screen in kivy when we type big text?

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.

NumericProperty does not change value after clearing screen

I'm having this small issue with Kivy currently. I'm working on a POS system and I am keeping track of the total with a variable named total that is a NumericProperty in the MyApp class. When I add a function that resets it to 0, the total variable stays 0 and will not add again. After a bit of troubleshooting, I've figured out that if I exclude the line self.clear_widgets(), it works as intended however I do need this line. Is there any reason why this happens?
I have provided a simple example code below to demonstrate the issue. Any help would be appreciated!
.py file
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty
class MainScreen(Screen):
def add(self, *args):
App.get_running_app().total += 1
def set_0(self, *args):
App.get_running_app().total = 0
self.clear_widgets()
self.add_widget(Label(text = str(App.get_running_app().total), font_size = 50))
self.add_widget(Button(text = 'Add 1', size_hint = (0.2,0.1), pos_hint = {'x': 0.3, 'y': 0.2}, on_release = self.add))
class WindowManager(ScreenManager):
pass
class MyApp(App):
total = NumericProperty(0)
def build(self):
return WindowManager()
if __name__ == '__main__':
MyApp().run()
.kv
<WindowManager>:
MainScreen
<MainScreen>:
FloatLayout:
Label:
text: str(app.total)
font_size: 50
Button:
text: 'Add 1'
size_hint: 0.2,0.1
pos_hint: {'x': 0.3, 'y': 0.2}
on_release:
root.add()
Button:
text: 'Set to 0'
size_hint: 0.2,0.1
pos_hint: {'x': 0.5, 'y': 0.2}
on_release:
root.set_0()
I figured out a way around this issue. I created another FloatLayout with an id of list inside my MainScreen and placed only the widgets I needed to remove inside this FloatLayout. So when I add the line self.ids.list.clear_widgets(), it doesn't affect the total variable.
.py
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty
class MainScreen(Screen):
def add(self, *args):
App.get_running_app().total += 1
def set_0(self, *args):
App.get_running_app().total = 0
self.ids.list.clear_widgets()
class WindowManager(ScreenManager):
pass
class MyApp(App):
total = NumericProperty(0)
def build(self):
return WindowManager()
if __name__ == '__main__':
MyApp().run()
.kv
<WindowManager>:
MainScreen
<MainScreen>:
FloatLayout:
id: list
size_hint: 0.16,0.81
FloatLayout:
Label:
text: str(app.total)
font_size: 50
Button:
text: 'Add 1'
size_hint: 0.2,0.1
pos_hint: {'x': 0.3, 'y': 0.2}
on_release:
root.add()
Button:
text: 'Set to 0'
size_hint: 0.2,0.1
pos_hint: {'x': 0.5, 'y': 0.2}
on_release:
root.set_0()

Add a widget that was created in kv from py

Is there a way to reference a custom widget from the py file?
I made a widget in kv, but I want to reference it from py, then add it again to another widget in kv.
I tried doing this using id but I got an error (KeyError: 'words_entry').
This is what I tried:
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
import os
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
text_inputs = [self.ids.words_entry 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
<WordEntry#TextInput>:
id: words_entry
multiline: False
size_hint: (self.width, None)
<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>:
ScrollView:
GridLayout:
size_hint_y: None
height: self.minimum_height
id: grid
cols: 1
""")
class MainApp(App):
def build(self):
return kv_file
if __name__ == "__main__":
app = MainApp()
app.run()
In this code, I want to add WordEntry to the GridLayout in GetInput from py (the reason is that I need to add multiple depending on the user's input).
You can use Factory to create an instance of a class that has been defined in kv. So your GetCount class can be:
from kivy.factory import Factory
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
for _ in range(int(self.count_input.text)):
new_word_entry = Factory.WordEntry()
self.manager.ids.get_input.ids.grid.add_widget(new_word_entry)
# Switch to next screen
self.manager.current = "get_input"

How can I add an amount of widgets in the kv file based on user input?

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?

Categories

Resources