Show data on targeted widget in kivy-python - python-3.x

I am new for Kivy-pyhton. I'm using python3 for using kivy-tool
I've developed a small application in kivy-tool
I am making an application to take data from user, save in a file, then on click of any button, I want to show data on widget
code for the application is:-
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
import os
from kivy.uix.scrollview import ScrollView
from kivy.properties import ObjectProperty
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
Builder.load_string('''
<MenuScreen>:
BoxLayout:
Button:
text: 'Add New Employee'
on_press: root.manager.current = 'add_staff'
Button:
text: 'View Employee Profile'
on_press: root.manager.current = 'read_staff'
Button:
text: 'Salary report'
<read_New_staff>:
# nam: str(name_input)
# job: job_input
GridLayout:
cols: 2
Label:
id: label
font_size: 24
bold: True
height: root.height * .9
size_hint_y: None
# text: "file Content show here....."
text_size: self.width, None
height: self.texture_size[1]
valign: 'middle'
halign: 'center'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
Button:
text: 'Show Data'
on_press: app.show()
<Lay>:
cols: 1
label: label
padding: 20
Label:
height: root.height * .1
size_hint_y: None
text: 'Read what is below'
font_size: 24
bold: True
ScrollView:
height: root.height * .85
size_hint_y: None
Label:
id: label
font_size: 24
bold: True
height: root.height * .9
size_hint_y: None
text: 'we will rock you'
text_size: self.width, None
height: self.texture_size[1]
# valign: 'middle'
# halign: 'center'
<Add_new_staff>:
# nam: str(name_input)
# job: job_input
GridLayout:
cols: 2
Label:
text: 'Product Name'
TextInput:
id: name_input
Label:
text: 'Price'
TextInput:
id: price_input
Label:
text: 'Priority'
TextInput:
id: priority_input
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
Button:
text: 'Save'
on_press: app.save(name_input.text, price_input.text,priority_input.text)
''')
class MenuScreen(Screen):
pass
class Add_new_staff(Screen):
pass
class read_New_staff(Screen):
pass
class Lay(GridLayout):
label = ObjectProperty()
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(Add_new_staff(name='add_staff'))
sm.add_widget(read_New_staff(name='read_staff'))
class TestApp(App):
def build(self):
l = Lay()
fob = open('/home/vyclean14/Desktop/Rohit_kivy/test.txt','r')
content = fob.read()
fob.close()
l.label.text = content
self.hello = Button(text = "hello")
self.hello.bind(on_press = self.show)
return sm #l
def save(self, name,price,priority):
fob = open('/home/vyclean14/Desktop/Rohit_kivy/test.txt','a')
fob.write("Product Name:- " + name + "\n")
fob.write("Price:- " + price + "\n")
fob.write("Priority:- " + priority + "\n")
fob.write("--------------------\n")
fob.close()
def show(self, *args):
l = Lay()
fob = open('/home/vyclean14/Desktop/Rohit_kivy/test.txt','r')
content = fob.read()
fob.close()
# l.label.text = content
print(content)
if __name__ == '__main__':
TestApp().run()
# NewApp().run()
Main View:
As user click on Add employee, His entered data added in .txt file
As I click on 'View Employee Detail' Show Data opens.
On Click of show data I want to show data in 1st quadrant
How can I achive this
Thank You

Related

Adding Kivy recycleview to screenmanager breaks example

I'm trying to combine this recycleview example with this screenmanager example so that the recycleview example can be one of the screens in my app. The app runs but all the control buttons display at the bottom of the UI (They're supposed to be at the top) and are either disabled or obscured, disallowing input. Consequently, the recycleview cannot be populated and/or viewed.
Here's my attempt:
import asyncio
from random import sample, randint
from string import ascii_lowercase
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
kv = '''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
#:import BoxLayout kivy.uix.boxlayout.BoxLayout
#:import RecycleDataViewBehavior kivy.uix.recycleview.views.RecycleDataViewBehavior
ScreenManagement:
transition: FadeTransition()
ContactsScreen:
name: 'contacts'
MessengerScreen:
name: 'messenger'
<Row#RecycleKVIDsDataViewBehavior+BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
Label:
id: name
Label:
text: root.value
<ContactsScreen>:
canvas:
Color:
rgba: 0.3, 0.3, 0.3, 1
Rectangle:
size: self.size
pos: self.pos
rv: rv
orientation: 'vertical'
GridLayout:
cols: 3
rows: 2
size_hint_y: None
height: dp(108)
padding: dp(8)
spacing: dp(16)
Button:
text: 'Populate list'
on_press: root.populate()
Button:
text: 'Sort list'
on_press: root.sort()
Button:
text: 'Clear list'
on_press: root.clear()
BoxLayout:
spacing: dp(8)
Button:
text: 'Insert new item'
on_press: root.insert(new_item_input.text)
TextInput:
id: new_item_input
size_hint_x: 0.6
hint_text: 'value'
padding: dp(10), dp(10), 0, 0
BoxLayout:
spacing: dp(8)
Button:
text: 'Update first item'
on_press: root.update(update_item_input.text)
TextInput:
id: update_item_input
size_hint_x: 0.6
hint_text: 'new value'
padding: dp(10), dp(10), 0, 0
Button:
text: 'Remove first item'
on_press: root.remove()
RecycleView:
id: rv
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
viewclass: 'Row'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(2)
<MessengerScreen>:
Button:
on_release: app.root.current = 'contacts'
text: 'back to the home screen'
font_size: 50
'''
class ContactsScreen(Screen):
def populate(self):
self.rv.data = [
{'name.text': ''.join(sample(ascii_lowercase, 6)),
'value': str(randint(0, 2000))}
for x in range(50)]
def sort(self):
self.rv.data = sorted(self.rv.data, key=lambda x: x['name.text'])
def clear(self):
self.rv.data = []
def insert(self, value):
self.rv.data.insert(0, {
'name.text': value or 'default value', 'value': 'unknown'})
def update(self, value):
if self.rv.data:
self.rv.data[0]['name.text'] = value or 'default new value'
self.rv.refresh_from_data()
def remove(self):
if self.rv.data:
self.rv.data.pop(0)
class MessengerScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_string(kv)
class MainApp(App):
def build(self):
return presentation
def main_runner(self):
async def run_wrapper():
# we don't actually need to set asyncio as the lib because it is
# the default, but it doesn't hurt to be explicit
await self.async_run(async_lib='asyncio')
print('App done')
#self.other_task.cancel()
return asyncio.gather(run_wrapper())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(MainApp().main_runner())
loop.close()
The trouble seems to come from the <Row#RecycleKVIDsDataViewBehavior+BoxLayout> directive but I can't seem to fix it no matter where I place it.
You can position the Buttons using pos_hint, like this:
GridLayout:
id: grid
pos_hint: {'center_x': 0.5, 'top': 1}
Then, you need to size the RecycleView so that it is not over the Buttons, like this:
RecycleView:
id: rv
size_hint_y: None
height: root.height - grid.height

Kivy: Popup can have only one widget as content

I am having an issue with using a popup in my .kv file. I understand that a popup can only have one widget as it's content, however if I am only passing a GridLayout as a child that includes a Label and Button, shouldn't this work?
Here is my Python code:
import kivy, LabelB
from kivy.app import App
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.properties import ObjectProperty, StringProperty
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
Builder.load_file('main.kv')
class CustomPopup(Popup):
pass
class MenuScreen(Screen):
def open_popup(self):
the_popup = CustomPopup()
the_popup.open()
class SurveyScreen(Screen):
pass
sm = ScreenManager(transition=FadeTransition())
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SurveyScreen(name='survey'))
class MainApp(App):
def build(self):
return sm
if __name__ == '__main__':
MainApp().run()
Here is my .kv file:
<CustomPopup>:
title: 'Terms of Service'
size_hint: .5, .5
auto_dismiss: False
GridLayout:
cols: 1
Label:
size_hint: .9, .9
halign: 'center'
valign: 'middle'
text: 'Insert terms of service text here'
text_size: self.width, None
Button:
text: 'Close'
on_release: root.dismiss()
<MenuScreen>:
FloatLayout:
canvas.before:
Rectangle:
source: 'menu.png'
size: self.size
pos: self.pos
Label:
pos_hint: {'x': .7, 'y': .85}
text_size: self.size
font_name: 'Arial'
font_size: 26
text: 'Sample'
bold: True
Button:
text: 'Take Survey'
size_hint: .2, .1
pos_hint: {'x': .15, 'y': .1}
on_release:
root.manager.transition.duration = 0.5
root.manager.current = 'survey'
Button:
text: 'Terms of Service'
size_hint: .2, .1
pos_hint: {'x': .6-self.size_hint_x, 'y': .1}
on_release: root.open_popup()
Button:
text: 'Quit'
size_hint: .2, .1
pos_hint: {'x': .85-self.size_hint_x, 'y': .1}
on_release: app.stop()
<SurveyScreen>:
GridLayout:
cols: 1
padding: 20
spacing: 10
Label:
text: 'WELCOME!'
font_size: 20
Label:
text: 'Some boring text'
The error is as follows: 'Popup can have only one widget as content'
Am I missing something obvious here? Thanks in advance.
Yes, it should work as you say, your code is correct.
The problem is that the loading of the .kv file is duplicated. As your App subclass is called MainApp, main.kv is loaded automatically if it is in same directory (Doc: How to load kv). On the other hand, you explicitly upload the file using Builder.load_file ('main.kv').
You must remove the call to Builder or rename MainApp/main.kv.
If you delete the call to Builder.load_file you must create the ScreenManager instance once the .kv is loaded. You can do something like:
class MainApp (App):
def build (self):
sm = ScreenManager (transition = FadeTransition ())
sm.add_widget (MenuScreen (name = 'menu'))
sm.add_widget (SurveyScreen (name = 'survey'))
return sm

How to prevent multiple instances of the Root-Widget in Kivy

OK, I try to make my first App in Kivy and I have a Problem with multiple Instances of my Root Widget.
I need to add widgets to my app while running. (to display a spreadsheet)
But because of the multiple instances of the Root Widget, I can't access the widgets with the kivy ids lookup object.
https://kivy.org/docs/guide/lang.html
I made three Buttons to display the problem in the console
Button -1-, -2-, and -3-
Please explain me why there are multiple instances and how I can prevent it, so the code will work.
Thanks for your help!
File --> Match.py
#!python
from __future__ import print_function
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
import gc
class my_gc():
def search_g(self):
for obj in gc.get_objects():
if isinstance(obj, Root):
try:
print(obj.ids)
except:
print('fail')
#Root Widget
class Root(BoxLayout):
sm = ObjectProperty(None)
sc0 = ObjectProperty(None)
sc1 = ObjectProperty(None)
sc2 = ObjectProperty(None)
sc3 = ObjectProperty(None)
mylbl0 = StringProperty('screen 0')
mylbl1 = StringProperty('screen 1')
mylbl2 = StringProperty('screen 2')
mylbl3 = StringProperty('screen 3')
def find_ids(self):
print(self.ids)
class Screen_Manager(ScreenManager):
pass
class Screen_0(Screen):
pass
class Screen_1(Screen):
pass
class Screen_2(Screen):
pass
class Screen_3(Screen):
pass
class Match(App):
rootwidget = Root()
sm = Screen_Manager()
gc = my_gc()
def build(self):
pass
Match().run()
File --> Match.kv
#Root Widget (BoxLayout)
Root:
sm: sm
sc1: sc0
sc1: sc1
sc1: sc2
sc1: sc3
BoxLayout:
spacing: '20dp'
orientation: 'vertical'
Screen_Manager:
id: sm
Screen_0:
id: sc0
name: 'sc0'
manager: 'sm'
BoxLayout:
id: box_0-0
Label:
id: lbl_0
text: app.rootwidget.mylbl0
Label:
id: lbl_0-1
text: root.mylbl0
Screen_1:
id: sc1
name: 'sc1'
manager: 'sm'
Label:
id: lbl1
text: root.mylbl1
Screen_2:
id: sc2
name: 'sc2'
manager: 'sm'
Label:
id: lbl2
text: root.mylbl2
Screen_3:
id: sc3
name: 'sc3'
manager: 'sm'
Label:
id: lbl3
text: root.mylbl3
#Tab-Buttons
BoxLayout:
size_hint: 1 , None
height: '60dp'
orientation: 'horizontal'
Button:
text: '-0-'
on_press: root.sm.current = sc0.name
Button:
text: '-1-'
on_press: root.sm.current = sc1.name
Button:
text: '-2-'
on_press: root.sm.current = sc2.name
Button:
text: '-3-'
on_press: root.sm.current = sc3.name
Button:
text: '<'
size_hint: None , 1
width: '60dp'
on_press: root.sm.current = root.sm.previous()
Button:
text: '>'
size_hint: None , 1
width: '60dp'
on_press: root.sm.current = root.sm.next()
Button:
text: '-b1-'
size_hint: None , 1
width: '60dp'
#on_press: root.search_g() #<-- doesn't work
on_press: app.gc.search_g() #<-- works
Button:
text: '-b2-'
size_hint: None , 1
width: '60dp'
on_press: root.find_ids() #<-- doesn't work
on_press: app.rootwidget.find_ids() #<-- works
Button:
text: '-b3-'
size_hint: None , 1
width: '60dp'
on_press: print(root.mylbl1) #<-- works
on_press: print(app.rootwidget.mylbl1) #<-- works
Mutilple Instance - Root Widget
As per your code, you don't have mutiple instances of the root widget. Your root widget is Root: as per the kv file, match.kv.
root.search_g()
It doesn't work because your root which is the class Root(BoxLayout) does not have the search_g() method.
root.find_ids - works
When I commented off app.rootwidget.find_ids, the call to root.find_ids works i.e. it printed the ids. The keyword root is available only in rule definitions and represents the root widget of the rule (the first instance of the rule). When your kv file is parsed, kivy collects all the widgets tagged with id’s and places them in self.ids dictionary type property.
app.rootwidget.find_ids
It works but there were no ids because it is referencing to the method find_ids in the initiated object, class Root. The rootwidget is just a variable by name and it is a different object than the one from Root:
From the attached screen shot, you will notice that the object locations for root.find_ids and app.rootwidget.find_ids are different.
Adding Widgets Dynamically
To add widgets dynamically into your app while it is running, you have to do the following. For an example, please refer to my other post at Kivy/python : when i click row then it shows error IndexError: list index out of range
Python Script - match.py
Add an import statement as follow:
from kivy.properties import ObjectProperty
Declare a variable (e.g. container) or ObjectProperty type e.g. in the class Root
class Root(BoxLayout):
container = ObjectProperty(None)
Remove all widgets added dynamically before adding
Add widgets dynamically.
kv File - match.kv
Define a layout e.g. BoxLayout or GridLayout in your kv file.
Give an id to the layout e.g. container in your kv file.
Hook up the ObjectProperty to the id
Root:
container: container
...
# Container for adding widgets dynamically
BoxLayout:
id: container
Solution
The solution is to declare an ObjectProperty, rootwidget as follow:
snippet
class Match(App):
rootwidget = ObjectProperty(None)
title = "Solution App"
gc = my_gc()
def build(self):
self.rootwidget = Root()
return self.rootwidget
match.py
#!python
from __future__ import print_function
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
import gc
class my_gc():
def search_g(self):
print("my_gc.search_g called")
for obj in gc.get_objects():
if isinstance(obj, Root):
try:
print("obj={0}, ids={1}".format(obj, obj.ids))
for key, value in obj.ids.items():
print(" key={0}, value={1}".format(key, value))
except:
print('fail')
# Root Widget
class Root(BoxLayout):
sm = ObjectProperty(None)
sc0 = ObjectProperty(None)
sc1 = ObjectProperty(None)
sc2 = ObjectProperty(None)
sc3 = ObjectProperty(None)
mylbl0 = StringProperty('screen 0')
mylbl1 = StringProperty('screen 1')
mylbl2 = StringProperty('screen 2')
mylbl3 = StringProperty('screen 3')
def find_ids(self):
print("Root.find_ids called")
print("self={0}, self.ids={1}".format(self, self.ids))
for key, value in self.ids.items():
print(" key={0}, value={1}".format(key, value))
class Screen_Manager(ScreenManager):
pass
class Screen_0(Screen):
pass
class Screen_1(Screen):
pass
class Screen_2(Screen):
pass
class Screen_3(Screen):
pass
class Match(App):
rootwidget = ObjectProperty(None)
title = "Solution App"
gc = my_gc()
def build(self):
self.rootwidget = Root()
return self.rootwidget
if __name__ == "__main__":
Match().run()
match.kv
#Root Widget (BoxLayout)
<Root>:
sm: sm
sc1: sc0
sc1: sc1
sc1: sc2
sc1: sc3
BoxLayout:
spacing: '20dp'
orientation: 'vertical'
Screen_Manager:
id: sm
Screen_0:
id: sc0
name: 'sc0'
manager: 'sm'
BoxLayout:
id: box_0-0
Label:
id: lbl_0
text: root.mylbl0
Label:
id: lbl_0-1
text: root.mylbl0
Screen_1:
id: sc1
name: 'sc1'
manager: 'sm'
Label:
id: lbl1
text: root.mylbl1
Screen_2:
id: sc2
name: 'sc2'
manager: 'sm'
Label:
id: lbl2
text: root.mylbl2
Screen_3:
id: sc3
name: 'sc3'
manager: 'sm'
Label:
id: lbl3
text: root.mylbl3
#Tab-Buttons
BoxLayout:
id: secondBoxLayout
size_hint: 1 , None
height: '60dp'
orientation: 'horizontal'
Button:
text: '-0-'
on_press: root.sm.current = sc0.name
Button:
text: '-1-'
on_press: root.sm.current = sc1.name
Button:
text: '-2-'
on_press: root.sm.current = sc2.name
Button:
text: '-3-'
on_press: root.sm.current = sc3.name
Button:
text: '<'
size_hint: None , 1
width: '60dp'
on_press: root.sm.current = root.sm.previous()
Button:
text: '>'
size_hint: None , 1
width: '60dp'
on_press: root.sm.current = root.sm.next()
Button:
text: '-b1-'
size_hint: None , 1
width: '60dp'
on_press:
print("\non_press: {}".format(self.text))
# root.search_g() #<-- doesn't work
print(" app.gc_search_g()")
app.gc.search_g() #<-- works
Button:
text: '-b2-'
size_hint: None , 1
width: '60dp'
on_press:
print("\non_press: {}".format(self.text))
print(" root.find_ids()")
root.find_ids() #<-- works
print("\n app.root.find_ids()")
app.rootwidget.find_ids() #<-- works
Button:
text: '-b3-'
size_hint: None , 1
width: '60dp'
on_press:
print("\non_press: {}".format(self.text))
print(root.mylbl1) #<-- works
print(app.rootwidget.mylbl1) #<-- works
Output

Access data from Kivy Popoup Input

I was trying to look at different examples online to solve this issue but Still cannot figure out. I have an app that calls a popup class. The popup has an input field that does some manipulation to the text in the input field. The manipulated data is stored in a variable inside of the popup class. Now how can the parent widget access the data obtained from this widget. Here is a short example of the code.
KV
<ScreenManagement>:
ScreenManager:
id: manager
size_hint_y: 93
Screen:
name: "Case_info_screen"
id: sc1
FloatLayout:
BoxLayout:
orientation: 'vertical'
BoxLayout:
size_hint_y: 10
canvas.before:
Color:
rgb: 0.17, 0.17, 0.17 # your color here
Rectangle:
pos: self.pos
size: self.size
Label:
text_size: self.size
font_size: 20
valign: 'middle'
height: "75dp"
size_hint_y: None
color: 1,1,1,1
size_hint_x: 75
text: " Case Info"
RelativeLayout:
size_hint_x: 25
Button:
text_size: self.size
halign: 'center'
valign: 'middle'
size_hint: 0.70, 0.6
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
text: 'Automatic Filler'
on_press: root.formfiller()
BoxLayout:
size_hint_y: 5
spacing: 35
LabelCases:
text: ' Name: '
width: '125dp'
size_hint_x: None
TextInput:
id: first_name_input
width: '300dp'
height: '40dp'
size_hint_x: None
font_size: 18
hint_text: 'First Name'
TextInput:
id: last_name_input
width: '300dp'
height: '40dp'
size_hint_x: None
font_size: 18
hint_text: 'Last Name'
<Formfillerpop>:
title: 'Automatic Filler'
size_hint: None, None
size: 600, 600
BoxLayout:
orientation: 'vertical'
Label:
size_hint_y: 20
text: 'Please Paste your text'
markup: True
TextInput:
id: sentence
size_hint_y: 65
BoxLayout:
size_hint_y: 10
orientation: 'horizontal'
Button:
text: 'Analyze'
on_press: root.on_analyze(sentence.text)
Button:
text: 'Close'
on_press: root.closepop()
Python:
class Formfillerpop(Popup):
selection = StringProperty
id = ObjectProperty
def on_analyze(self, selection):
analyze = TextExtractor(selection)
names = analyze.name()
def closepop(self):
self.dismiss()
class ScreenManagement(FloatLayout):
def formfiller(self, *args):
Formfillerpop().open()
class IOApp(App):
def build(self):
return ScreenManagement()
if __name__ == '__main__':
IOApp().run()
Ultimately I want to take the txt from names in the popup and then autopopulate the first name and last name in the main app with the analyzed data
Sorry if this is basic. I am fairly new to Kivy.
You can access popup content using its content property. You can use it to read text property of read underlying TextInput. For example this code binds this property to local popup_text StringProperty, which means that any change in the popup text input will be reflected there:
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
Builder.load_string('''
<CustomPopup>:
size_hint: .5, .5
auto_dismiss: False
title: 'Hello world'
BoxLayout:
text_input: text_input
orientation: 'vertical'
TextInput:
id: text_input
Button:
text: 'dismiss'
on_press: root.dismiss()
''')
class CustomPopup(Popup):
pass
class TestApp(App):
popup_text = StringProperty()
def build(self):
l = BoxLayout()
l.add_widget(Button(
text='show_popup', on_press=self.show_popup
))
l.add_widget(Button(
text='print popup text', on_press=self.print_popup_text
))
return l
def show_popup(self, *args):
p = CustomPopup()
p.content.text_input.bind(text=self.setter('popup_text'))
p.open()
def print_popup_text(self, *args):
print(self.popup_text)
if __name__ == '__main__':
TestApp().run()

Kivy Scrollable Label and text file, label wont update

Hy, the problem is that with the current code, which is at this point preety much nothing but a text editor, when ever I try to make a scrollable label in the kv language and call it on the main screen at the push of a button, I get no error, theres just nothing there. I should mention that the text is taken from a stored file, and the only version that works is with a regular label. This is the code, I know its a bit long but its preety easy to understand so stay with me. Any sort of input is greatly apreciated and I thank you for taking the time.
#kivy.require("1.8.0")
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import Line
from kivy.uix.gridlayout import GridLayout
kv = '''
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManager:
transition: FadeTransition()
MainScreen:
AddScreen:
AppendScreen:
<ScatterTextWidget>:
orientation: 'vertical'
TextInput:
id: main_input
font_size: 14
size_hint_y: None
height: root.height - botones_layout.height
font_color: [0.1,0.3,0.9,1]
focus: True
foreground_color: [0.2,0.5,0.9,1]
cursor_color: [0,0,1,1]
BoxLayout:
id: botones_layout
orientation: 'horizontal'
height: 30
Button:
id: home_button
text: "Back Home"
Button:
id: save_button
text: "Save to file"
on_press: root.saveToFile("Archive.txt", main_input.text)
<AppendTextWidget>:
orientation: 'vertical'
TextInput:
text: root.text
id: main_input
font_size: 14
size_hint_y: None
height: root.height - botones_layout.height
font_color: [0.1,0.3,0.9,1]
focus: True
foreground_color: [0.2,0.5,0.9,1]
cursor_color: [0,0,1,1]
BoxLayout:
id: botones_layout
orientation: 'horizontal'
height: 30
Button:
id: home_button
text: "Back Home"
on_release: app.root.current = "main"
Button:
id: save_button
text: "Save"
on_press: root.append(main_input.text)
#This does not work <--- <--- <---
<ScrollableLabel>:
Label:
text: root.text
font_size: 15
text_size: self.width, None
color: [0,255,0,1]
padding_x: 20
size_hint_y: None
pos_hint: {"left":1, "top":1}
height: self.texture_size[1]
<MainScreen>:
name: "main"
FloatLayout:
# This does work
Label:
text: root.text
font_size: 15
text_size: self.width, None
color: [0,255,0,1]
padding_x: 20
size_hint_y: None
pos_hint: {"left":1, "top":1}
height: self.texture_size[1]
ActionBar:
pos_hint: {'top':1}
ActionView:
use_separator: True
ActionPrevious:
title: "Text"
with_previous: False
ActionOverflow:
ActionButton:
text: "New"
on_release: app.root.current = "add"
ActionButton:
text: "Update"
on_press: root.clicked()
ActionButton:
text: "Add"
on_release: app.root.current = "append"
<AddScreen>:
name: "add"
FloatLayout:
ScatterTextWidget
<AppendScreen>:
name: "append"
FloatLayout:
AppendTextWidget
'''
class ScatterTextWidget(BoxLayout):
def saveToFile(self,name,text):
f = open(name, "w")
f.write("\n\n\n" + " " + ">>>" + text + "test"*500)
f.close()
class AppendTextWidget(BoxLayout):
text = StringProperty("")
def append(self,text):
with open("Archive.txt", "a") as f:
f.write("\n" + " " + ">>>" + text)
f.close()
class ScrollableLabel(ScrollView):
text = StringProperty('')
pass
class MainScreen(Screen):
text = StringProperty("")
def clicked(self):
with open("Archive.txt", "r") as f:
contents = f.read()
self.text = contents
pass
class AddScreen(Screen):
pass
class AppendScreen(Screen):
pass
class MyApp(App):
def build(self):
return Builder.load_string(kv)
if __name__ == '__main__':
MyApp().run()
Why it works:
Your text in MainScreen is updated from file, then passed to Label and the text is loaded. ScrollableLabel.text stays unchanged.
Why it doesn't work as you expect:
There's no communication between your classes, therefore only text changed is an actual self.text = MainScreen.text
How to make it work:
Either use something on a global range, i.e. variable in your App class, then a = App.get_running_app() and access variable through a.<variable> or use __init__ to initialize your ScrollableLabel inside the MainScreen and pass it the text
So it's basically a duplicate of this and that one is a duplicate of an older unsimplified question.

Resources