How to store to list in kivy? - python

I am making kivy app, something like project manager. I've got problem, i'll try to describe it.
In my app you can create new "project" by pressing button, then open the project and create some task. I need to store "project" into list or dictioniares. I've been thinking about this problem whole week and I can't solve. Do you have any ideas? I include my py. file and my kv. file here.
If you have some improvments to my codes, come here with them.
My python file:
from kivy.app import App
import sys
from kivy.storage.jsonstore import JsonStore
from kivy.uix.screenmanager import ScreenManager,Screen, SlideTransition, RiseInTransition
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.uix.checkbox import CheckBox
from kivy.core.window import Window
from kivy.graphics import Color, Ellipse, Rectangle, Line
from kivy.uix.spinner import Spinner
from kivy.uix.stencilview import StencilView
Window.size = (400, 650)
Window.clearcolor = (0,0,0,0)
kv = Builder.load_file('main.kv')
c = 0
class MS(Screen, Widget):
def on_exit(self):
sys.exit()
class SES(Screen):
pass
class OPS(Screen, BoxLayout):
themes = ["DARK","LIGHT"]
def __init__(self, **kwargs):
super(OPS, self).__init__(**kwargs)
label = Label(text="COLOR THEME:", size_hint=(.2,.1), pos_hint={"x":.25,"top":.9})
self.add_widget(label)
tgbt = ToggleButton(text="DARK", size_hint=(.2, .1), pos_hint={"x":.5,"top":.9},background_normal="",background_color =(0,0,0,0))
self.add_widget(tgbt)
tgbt2 = ToggleButton(text="LIGHT", size_hint=(.2, .1), pos_hint={"x": .7, "top": .9},background_normal="", background_color= (1,1,1,1))
self.add_widget(tgbt2)
class PRS(Screen, BoxLayout):
widgets1 = []
l1x = .1
l1top = .87
txt1x = .4
txt1top = .88
ck1x = .64
ck1top = .88
chtop = 0.88
deltop = 0.88
projects = []
def changer(self,*args):
self.manager.current = 'task'
def changer2(self, *args):
self.manager.current = 'main'
def new_pr(self, *args):
txt1 = TextInput(multiline=False,size_hint=(0.3,0.05), pos_hint={'x':self.txt1x,"top":self.txt1top})
self.add_widget(txt1)
label1 = Label(text="YOUR PROJECT NAME:",size_hint=(0.2,0.04),pos_hint={"x":self.l1x,"top":self.l1top})
self.add_widget(label1)
check1 = CheckBox(active=False, size_hint=(0.2,0.05), pos_hint={"x":self.ck1x,"top":self.ck1top})
self.add_widget(check1)
print(self.projects)
self.widgets1.append(check1)
self.widgets1.append(label1)
self.widgets1.append(txt1)
self.l1top = self.l1top - .07
self.txt1top = self.txt1top - .07
self.ck1top = self.ck1top - .07
spinner = Spinner(text="OPTIONS", values="DELETE",background_normal="",background_color =(200/255.0, 194/255.0, 136/255.0,1))
button = Button(text="OPEN",color=(200 / 255.0, 194 / 255.0, 136 / 255.0, 1), size_hint=(0.1,0.05), pos_hint={"x":0.77, "top":self.chtop})
self.add_widget(button)
self.widgets1.append(button)
button2 = Button(text = "DELETE",color=(200 / 255.0, 194 / 255.0, 136 / 255.0, 1), size_hint=(0.13,0.05), pos_hint={"x":0.87,"top":self.deltop})
self.add_widget(button2)
self.widgets1.append(button2)
self.chtop = self.chtop - 0.07
self.deltop = self.deltop - 0.07
button.bind(on_press= self.changer)
button2.bind(on_release= self.remover)
def remover(self, *args):
for i in self.widgets1:
self.remove_widget(i)
self.l1top = .88
self.deltop = .88
self.chtop = .88
self.ck1top = .88
self.txt1top = .88
def __init__(self, **kwargs):
super(PRS, self).__init__(**kwargs)
bt1 = (Button(text="NEW PROJECT",pos_hint={"x":.0,"top":1},size_hint=(.5,.1)))
self.add_widget(bt1)
bt2 = (Button(text="BACK",pos_hint={"x":.5,"top":1},size_hint=(.5,.1)))
self.add_widget(bt2)
bt1.bind(on_release=self.new_pr)
bt2.bind(on_release=self.changer2)
def clear_screen(self):
self.canvas.clear()
class TASKS(Screen, BoxLayout):
task1 = []
tinpx = 0.25
tinptop = .83
chkx = 0.76
chktop = .84
showed = False
def note(self, *args):
self.showed = True
if self.showed == True:
textinput = TextInput(multiline=True,size_hint=(.4,.5), pos_hint={"x":0.56,"top":self.chktop-0.1})
self.add_widget(textinput)
while self.showed == False:
self.remove_widget(textinput)
break
def change(self, *args):
self.showed = False
def changer(self,*args):
self.manager.current = 'project'
def new_task(self, *args):
txt1 = TextInput(multiline=False, size_hint=(.2,.04), pos_hint={"x":self.tinpx, "top":self.tinptop})
self.add_widget(txt1)
chck1 = CheckBox(active=False, size_hint = (.2, .05), pos_hint = {"x":self.chkx,"top":self.chktop})
self.add_widget(chck1)
notes = Button(text="NOTES", color=(200 / 255.0, 194 / 255.0, 136 / 255.0, 1), size_hint=(.2,.05), pos_hint={"x":0.56,"top":self.chktop})
notes.bind(on_press=self.note)
notes.bind(on_release=self.change)
self.add_widget(notes)
self.tinptop = self.tinptop - .07
self.chktop = self.chktop - .07
def __init__(self, **kwargs):
super(TASKS, self).__init__(**kwargs)
self.add_widget(Label(text="TASK NAME", size_hint=(.2,.04), pos_hint={"x":0.25,"top":.88}))
self.add_widget(Label(text="COMPLETE", size_hint = (.2,.04), pos_hint={"x":0.75,"top":.88}))
bt1 = Button(text="NEW TASK",color=(0,0,0),size_hint=(.5,.1), pos_hint = {"x":0, "top":1},background_normal="",background_color =(200/255.0, 194/255.0, 136/255.0,1))
self.add_widget(bt1)
homebt = Button(text="BACK",color=(0,0,0),size_hint=(.5, .1), pos_hint={"x":0.5,"top":1},background_normal="",background_color =(200/255.0, 194/255.0, 136/255.0,1))
self.add_widget(homebt)
homebt.bind(on_release=self.changer)
bt1.bind(on_release=self.new_task)
class Help(Screen, Widget):
pass
class MApp(App):
title = "PROJECT MANAGER"
def build(self):
wid = PRS(size_hint=(None, None), size=Window.size)
sm = ScreenManager()
sm.add_widget(MS(name = "main"))
sm.add_widget(SES(name = "second"))
sm.add_widget(OPS(name = "options"))
sm.add_widget(PRS(name="project"))
sm.add_widget(TASKS(name="task"))
sm.add_widget(Help(name="help"))
return sm
if __name__ == "__main__":
MApp().run()
My kv. file:
<MS>:
GridLayout:
cols:7
GridLayout:
cols:2
Button:
text : "NEW"
color:0,0,0
on_release:app.root.current="project"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
text : "OPTIONS"
color:0,0,0
on_release:app.root.current="options"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
text : "LOAD"
color:0,0,0
on_release:app.root.current="second"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
text : "HELP"
color:0,0,0
on_release:app.root.current="help"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
text : "Next"
color:0,0,0
on_release:app.root.current="second"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
id:bt1
text : "Exit"
color:0,0,0
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
on_release:app.stop()
<SES>:
name:"second"
Button:
text:"back"
color:0,0,0
on_release:
app.root.current="main"
<OPS>:
name:"options"
BoxLayout:
Button:
size_hint:.5,.2
pos_hint : {"x":.5,"top":.15}
text: "HOME"
color:0,0,0
on_release:app.root.current="main"
<PRS>:
name:"project"
<TASKS>:
name:"task"
<Help>:
name:"help"
GridLayout:
Label:
text:"COMING SOON!!!"
color:0,0,0
Thaks for help, time and effort.
Micheno

Here is simple example of my problem.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class Test(BoxLayout):
x1 = 0.2
top1 = 0.9
widgets = []
def adder(self, *args):
lb1 = Label(text="HELLO WORLD", pos_hint={"x":self.x1,"top":self.top1})
self.add_widget(lb1)
self.x1 = self.x1 - 0.05
self.top1 = self.top1 - 0.05
self.widgets.append(lb1)
def remover(self, *args):
for i in self.widgets:
self.remove_widget(i)
self.x1=0.2
self.top1 = 0.9
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
bt1 = Button(text="ADD", pos_hint={"x":.1,"top":1}, size_hint=(0.2,0.4))
self.add_widget(bt1)
removebt = Button(text = "REMOVE",pos_hint={"x":.4,"top":1}, size_hint=(0.2,0.4))
self.add_widget(removebt)
removebt.bind(on_release=self.remover)
bt1.bind(on_release=self.adder)
class APP(App):
def build(self):
return Test()
if __name__ == "__main__":
APP().run()
If i want to delete only one label it will delete all. Just try to copy and run it

Just remove the last Label in the list:
def remover(self, *args):
if len(self.widgets) > 0:
self.remove_widget(self.widgets.pop(-1))

OK i could delete last that's improvement, but my problem is like this. What if I with button "ADD" I add text and button to remove the text and the button itself. Here is my new example with yours improvment.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
Window.size = (400, 650)
class Test(BoxLayout):
x1 = 0.1
top1 = 0.9
widgets = []
def adder(self, *args):
lb1 = Label(text="HELLO WORLD", pos_hint={"x":self.x1,"top":self.top1})
self.add_widget(lb1)
removebt = Button(text = "REMOVE",pos_hint={"x":self.x1-0.1,"top":self.top1}, size_hint=(0.5,0.1))
self.add_widget(removebt)
self.widgets.append(removebt)
removebt.bind(on_release=self.remover)
self.widgets.append(lb1)
self.top1 = self.top1 - 0.05
def remover(self, *args):
if len(self.widgets) > 0:
self.remove_widget(self.widgets.pop(-1))
self.x1=0.2
self.top1 = 0.9
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
bt1 = Button(text="ADD", pos_hint={"x":.1,"top":1}, size_hint=(1,0.4))
self.add_widget(bt1)
bt1.bind(on_release=self.adder)
class APP(App):
def build(self):
return Test()
if __name__ == "__main__":
APP().run()

Related

kivy transition between screens

i made a gui with python kivy with a starting screen that has a button in it which generates new screens when pressed, and it also generates new buttons so its easier to pick the screen i need to get in focus but i can't get it working properly cause i can't generate buttons on the newly made screens
when they aren't in focus, the script only generates buttons for the screen in focus it seems
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
sm = ScreenManagement(transition=FadeTransition())
class newtab(Screen):
def __init__(self, **kwargs):
super(newtab, self).__init__(**kwargs)
self.bt1=(Button(text="New tab", size_hint =(.1, .1) ,pos_hint ={'center_x':.1, 'center_y':.94}))
self.add_widget(self.bt1)
self.bt1.bind(on_release=self.new_tab)
self.txt1 = TextInput(text='',size_hint =(.1, .1), pos_hint ={'center_x':.2, 'center_y':.75}, multiline=True)
self.add_widget(self.txt1)
def transition(self, instance):
self.manager.current = (instance.text)
for item in sm.screen_names:
self.bt2=(Button(text=item, size_hint =(.1, .1) ,pos_hint ={'center_x':(.1*sm.screen_names.index(item)+.1), 'center_y':.84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
def new_tab(self, *args):
n = len(self.manager.screen_names) ##number of screens+1
screen = newtab(name="screen {}".format(n)) #make new screen and give it number
self.manager.add_widget(screen)
self.manager.current = "screen 0"
for item in sm.screen_names:
self.bt2=(Button(text=item, size_hint =(.1, .1) ,pos_hint ={'center_x':(.1*sm.screen_names.index(item)+.1), 'center_y':.84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
sm.add_widget(newtab(name='screen 0'))
class Application(App):
def build(self):
return sm
if __name__ == "__main__":
Application().run()
I suppose that below is what you expected to get. But I agree with #ApuCoder that you may look for TabbedPanel functionality.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
sm = ScreenManagement(transition=FadeTransition())
class newtab(Screen):
def __init__(self, **kwargs):
super(newtab, self).__init__(**kwargs)
self.bt1 = (Button(text="New tab", size_hint=(.1, .1), pos_hint={'center_x': .1, 'center_y': .94}))
self.add_widget(self.bt1)
self.bt1.bind(on_release=self.new_tab)
self.txt1 = TextInput(text='', size_hint=(.1, .1), pos_hint={'center_x': .2, 'center_y': .75}, multiline=True)
self.add_widget(self.txt1)
# add all present screen buttons to newly created screen
for i, screen in enumerate(sm.screens):
self.bt2 = (Button(text=screen.name, size_hint=(.1, .1), pos_hint={'center_x': (.1 * i + .1), 'center_y': .84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
# add this newly created screen button to all screens
for screen in sm.screens + [self]:
screen.bt2 = (Button(text=self.name, size_hint=(.1, .1), pos_hint={'center_x': (.1 * len(sm.screens) + .1), 'center_y': .84}))
screen.add_widget(screen.bt2)
screen.bt2.bind(on_release=screen.transition)
def transition(self, instance):
self.manager.current = instance.text
def new_tab(self, *args):
n = len(self.manager.screen_names) ##number of screens+1
screen = newtab(name="screen {}".format(n)) # make new screen and give it number
self.manager.add_widget(screen)
self.manager.current = "screen 0"
sm.add_widget(newtab(name='screen 0'))
class Application(App):
def build(self):
return sm
if __name__ == "__main__":
Application().run()

Why is my navigation code not working? kivy python

I have a screen manager ThirdWindow which is supposed to change the screen when B1 is clicked basically a navigation thing. Please note ThirdWindow is a a different class. callback is called when B1 is clicked. it changes the screen with the help of ThirdWindow I think. I used self.manager.current it didn't worked, I also tried using it by making ThirdWindow a def in class Dre but the results were same. The print statement is not getting printed that is inside def callback(self, instance) so I think the def is not getting called on_click at all. How can I fix that? Thanks
import kivy
from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.stacklayout import StackLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.graphics import Rectangle
from kivy import platform
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainWindow(Screen):
pass
class ThirdWindow(ScreenManager):
def load(self):
sm = ScreenManager
sm.add_widget(Dre(name='Dre'))
sm.add_widget(SecondWindow(name='SecondWindow'))
self.sm.current = 'Dre'
class Dre(RelativeLayout):
def __init__(self, **kwargs):
super(Dre, self).__init__(**kwargs)
self.color = [254/255, 102/255, 37/255, 1]
self.H_color = [254/255, 102/255, 37/255]
self.sound_theme = None
self.init_audio()
self.kv = Builder.load_file('Levels.py')
if platform in ('linux', 'win', 'macosx'):
with self.canvas.before:
self.bg = Rectangle(size=self.size, source='Neo.png')
self.bind(pos=self.update_bg)
self.bind(size=self.update_bg)
else:
with self.canvas.before:
self.bg = Rectangle(size=self.size, source='Neon.png')
self.bind(pos=self.update_bg)
self.bind(size=self.update_bg)
def update_bg(self, *args):
if platform in ('linux', 'win', 'macosx'):
self.bg.pos = self.pos
self.bg.size = self.size
self.add_widget(Label(text='D R E A M S',
pos_hint={'center_x': .5, 'center_y': .8},
font_size='60dp', font_name='Roboto-Bold.ttf', color=self.H_color))
B1 = Button(text='P L A Y', font_name='Roboto-Bold.ttf', size_hint=(.2, .15),
pos_hint={'center_x': .5, "center_y": .3}, background_color = self.color, background_normal='')
# B1.bind(on_press=return self.kv)
self.add_widget(B1)
else:
self.bg.pos = self.pos
self.bg.size = self.size
self.add_widget(Label(text='D R E A M S',
pos_hint={'center_x': .5, 'center_y': .8},
font_size='30dp', font_name='Roboto-Bold.ttf', color=self.H_color))
B1 = Button(text='P L A Y', font_name='Roboto-Bold.ttf', size_hint=(.2, .15),
pos_hint={'center_x': .5, "center_y": .3}, background_color=self.color, background_normal='',
on_press=self.callback)
self.add_widget(B1)
def callback(self, instance):
print('working')
self.manager.current = 'SecondWindow'
def init_audio(self):
self.sound_theme = SoundLoader.load('Bg_theme.mp3')
self.sound_theme.volume = 1
self.sound_theme.loop = True
if self.sound_theme:
self.sound_theme.play()
print('okay')
else:
print('not okay')
class SecondWindow(Screen):
def __init__(self, **kwargs):
super(SecondWindow, self).__init__(**kwargs)
self.color = [254 / 255, 102 / 255, 37 / 255, 1]
if platform in ('linux', 'win', 'macosx'):
with self.canvas.before:
self.bg = Rectangle(size=self.size, color=self.color)
class LabApp(App):
def build(self):
return Dre()
if __name__ == '__main__':
LabApp().run()```
if you provide any solution please provide it in .py file.
Many issues with your code.
First, you have defined a ScreenManager class (ThirdWindow), but you have not created an instance of it or included it in your GUI. In order to use a ScreenManager to manage Screens, you must add it to your GUI. I would suggest modifying your LabApp class to use ThirdWindow:
class LabApp(App):
def build(self):
return ThirdWindow()
and modify ThirdWindow to replace the load() method with an __init__() method:
class ThirdWindow(ScreenManager):
def __init__(self, **kwargs):
super(ThirdWindow, self).__init__(**kwargs)
self.add_widget(Dre(name='Dre'))
self.add_widget(SecondWindow(name='SecondWindow'))
Since the Dre class is being used as a Screen, it must be redefined as a Screen:
class Dre(Screen):
and you can add the callback to the creation of the B1 Button:
B1 = Button(text='P L A Y', font_name='Roboto-Bold.ttf', size_hint=(.2, .15), on_release=self.callback,
pos_hint={'center_x': .5, "center_y": .3}, background_color = self.color, background_normal='')

Kivy - Saving object's values changed in Popup, so that after closing it, when I open the popup again it opens with new values

I want to change some button labels of a class in a popup and retain the new label after reopening the popup. If you run my app, you can see that after pressing "Press me" button, a popup appears with a button with a label "Default", after pressing on it, it changes the label to "New". I want to be able to close the popup, press the "Press me" button and see the button in a popup with a label "New".
My .py file
from kivy.uix.floatlayout import FloatLayout
from kivy.core.window import Window
from kivy.uix.popup import Popup
from kivy.app import App
from kivy.uix.widget import Widget
import time
Window.clearcolor = (1, 1, 1, 1)
Window.size = (800, 480)
class MyGrid(Widget):
def btn(self):
show_popup(T, "Window")
class T(FloatLayout):
pass
def show_popup(tab, name):
show = tab()
popupWindow = Popup(title = name, content = show, size_hint = (None,None), size = (800,384), auto_dismiss = True)
popupWindow.open()
return popupWindow
class TimeApp(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
TimeApp().run()
my .kv file
<MyGrid>
Button:
text: "Press me"
on_press: root.btn()
<T>:
Button:
pos_hint: {"center_x": 0.5, "center_y": 0.5}
text: "Default"
on_press: self.text = "New"
One way that you can do it, is by keeping a reference to the popup.
The py side:
Window.clearcolor = (1, 1, 1, 1)
Window.size = (800, 480)
class MyGrid(Widget):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.popupWindow = Popup(content=T(), size_hint=(None, None),
size=(800, 384), auto_dismiss=True)
def btn(self):
self.show_popup("Window")
def show_popup(self, name):
self.popupWindow.title = name
self.popupWindow.open()
class T(FloatLayout):
pass
class TimeApp(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
TimeApp().run()

Why I have to click twice to scroll? Scrollable Label in kivy, python

I have a following problem: I need to write an app, where I will show proper answers for every question. I wrote with kivy some code and I'm struggling with one thing. I created a page. There is a button for showing answers, but after one press I only see a part of my answers and I can't scroll. But, when I press a button second time, everything is good. Could you tell me why is that? How to repair it? I would like to see all answers after pressing a button once and be able to scroll.
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.scrollview import ScrollView
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from kivy.config import Config
Config.set('graphics', 'resizable', True)
import os
import sys
class MyApp(App):
def build(self):
self.screen_manager = ScreenManager()
self.answers = Answers()
screen = Screen(name = "Answers")
screen.add_widget(self.answers)
self.screen_manager.add_widget(screen)
return self.screen_manager
class Answers(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rows = 3
self.label = Label(text = "Answers: ", font_size = 40)
self.add_widget(self.label)
self.button = Button(text="Show answers")
self.button.bind(on_press=self.showanswers)
self.add_widget(self.button)
self.scroll = ScrollableLabel(height = Window.size[1]*0.75, size_hint_y = None)
self.add_widget(self.scroll)
def showanswers(self, instance):
f = open("text.txt", "r")
lines = f.readlines()
ScrollableLabel.update(self.scroll, lines)
myapp.screen_manager.current = "Answers"
class ScrollableLabel(ScrollView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.layout = GridLayout(cols = 1, size_hint_y = None)
self.add_widget(self.layout)
self.lines = Label(size_hint_x = 1, size_hint_y = None, text_size = (self.width, None))
self.scroll_to_point = Label()
self.scroll_to_point.bind(texture_size=self.scroll_to_point.setter('size'))
self.layout.add_widget(self.lines)
self.layout.add_widget(self.scroll_to_point)
def update(self, lines):
self.lines.text = '\n'
for i in range(len(lines)):
self.lines.text += '\n ' +str(i+1) + ". " + lines[i]
self.layout.height = self.lines.texture_size[1]
self.lines.height = self.lines.texture_size[1]
self.lines.text_size = (self.lines.width*0.75, None)
self.scroll_to(self.scroll_to_point)
f = open("text.txt", 'a+')
for i in range(30):
f.write("Important text \n")
f.close()
myapp = MyApp()
myapp.run()
I think your widget heights are not updating correctly, and to correct that requires doing some binding. Since the 'kv' language automatically does bindings, I have provided an answer that uses 'kv':
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.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
from kivy.config import Config
Config.set('graphics', 'resizable', True)
class MyApp(App):
def build(self):
self.screen_manager = ScreenManager()
self.answers = Answers()
screen = Screen(name = "Answers")
screen.add_widget(self.answers)
self.screen_manager.add_widget(screen)
return self.screen_manager
class Answers(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rows = 3
self.label = Label(text = "Answers: ", font_size = 40)
self.add_widget(self.label)
self.button = Button(text="Show answers")
self.button.bind(on_press=self.showanswers)
self.add_widget(self.button)
self.scroll = ScrollableLabel(height = Window.size[1]*0.75, size_hint_y = None)
self.add_widget(self.scroll)
def showanswers(self, instance):
f = open("text.txt", "r")
lines = f.readlines()
self.scroll.update(lines)
myapp.screen_manager.current = "Answers"
class ScrollableLabel(ScrollView):
def update(self, lines):
self.ids.lines.text = '\n'
for i in range(len(lines)):
self.ids.lines.text += '\n ' +str(i+1) + ". " + lines[i]
Builder.load_string('''
<ScrollableLabel>:
size_hint_y: None
GridLayout:
cols: 1
size_hint_y: None
height: self.minimum_height # adjust height to handle the Label
Label:
id: lines
size_hint_y: None
height: self.texture_size[1] # set height based on text
''')
f = open("text.txt", 'a+')
for i in range(30):
f.write("Important text \n")
f.close()
myapp = MyApp()
myapp.run()
I believe the two instances of size_hint_y: None and the corresponding height rules are the key.
Note that I also changed:
ScrollableLabel.update(self.scroll, lines)
to:
self.scroll.update(lines)

How to center text horizontally in a Kivy text input?

I want to center a single line of text in Kivy text input.
I'm going to use padding
widget.padding = [ (self.textinput.width - width of line) / 2, 20, 0, 0]
but i can't find the width of the line. How can I calculate or access the width of the line?
There is an internal TextInput._get_text_width method you can use to calculate proper padding:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
Builder.load_string('''
<MyWidget>:
TextInput:
multiline: False
on_text: root.update_padding(args[0])
padding_x: self.width/2 # initial padding
''')
class MyWidget(FloatLayout):
def update_padding(self, text_input, *args):
text_width = text_input._get_text_width(
text_input.text,
text_input.tab_width,
text_input._label_cached
)
text_input.padding_x = (text_input.width - text_width)/2
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
The solution above almost worked for me. Sometimes the padding wouldn't be updated correctly. Here is my slight change, setting text_width as a NumericProperty:
In Kivy:
<CenteredTextInput#TextInput>:
multiline: False
on_text: root.update_padding()
padding_x: (self.width - self.text_width) / 2
In Python:
class CenteredTextInput(TextInput):
'''
A centered TextInput.
'''
text_width = NumericProperty()
'''The text width
'''
def update_padding(self, *args):
'''
Update the padding so the text is centered
'''
self.text_width = self._get_text_width(
self.text,
self.tab_width,
self._label_cached
)
You could make a textinput behind a button, and make the button visualize as the text input.
When pushing the button, put the focus to the textinput, and update the buttons text.
I have made an example here.
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.app import App
from kivy import require
require('1.9.1')
class MyWidget(BoxLayout):
def __init__(self,**kwargs):
super(MyWidget,self).__init__(**kwargs)
self.orientation = "vertical"
self.cur = False
self.textinput = TextInput(text='',halign="center",multiline=False)
self.textinput.bind(text=self.on_text)
self.button = Button(background_normal="",background_color=[0,0,0.1,1],font_size="40sp")
self.button.bind(on_release=self.button_click)
self.my_float_layout = FloatLayout()
self.my_float_layout.add_widget(self.textinput)
self.my_float_layout.add_widget(self.button)
self.add_widget(Label(text="type text below",font_size="40sp"))
self.add_widget(self.my_float_layout)
Clock.schedule_interval(self.cursor, 0.5)
def cursor(self,dt): # function to visualize a cursor
if self.textinput.focus:
cur_pos = self.textinput.cursor[0]
if not self.cur:
self.button.text = self.textinput.text[:cur_pos] + "|" + self.textinput.text[cur_pos:]
self.cur = True
else:
self.button.text = self.textinput.text[:cur_pos] + " " + self.textinput.text[cur_pos:]
self.cur = False
elif self.cur:
self.button.text = self.textinput.text + " "
self.cur = False
def on_text(self, *args): # function to set the button text
self.button.text = self.textinput.text
def button_click(self,*args): # function to focus the input
self.textinput.focus = True
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == "__main__":
MyApp().run()

Categories