I am trying to create a layout with 2 sections that have a label over each and a view below (TreeView on the left and a window showing details on the right, e.g. "click on this node and see details"). I've got the labels sized and positioned, but when I add the TreeView, instead of fitting under the label, it pushes both labels over, no matter the position.
Here's the current result:
My main.py file--
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.treeview import TreeView, TreeViewLabel
class MainScreen(BoxLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
bridge_view = TreeView(root_options=dict(text='Clients'),
hide_root=False, indent_level=4,
pos_hint={"x": 0, "y": 0},
size_hint=(0.1, 0.5))
clients = {'client1': 'Connection A',
'client2': 'Connection B'}
for client in clients.keys():
node = bridge_view.add_node(TreeViewLabel(text=client))
bridge_name = clients[client]
bridge = bridge_view.add_node(TreeViewLabel(text=bridge_name), node)
self.add_widget(bridge_view)
class ConnectionApp(App):
def build(self):
self.title = 'My Support App'
return MainScreen()
if __name__ == '__main__':
ConnectionApp().run()
This is my kv file--
<Label>:
font_size: 30
<MainScreen>:
Label:
text: "Connections"
size_hint: 0.1, 0.5
pos_hint: {"left": 0.2, "top": 1.2}
color: 1,0,1,1
outline_color: 1,0,1,1
Label:
text: "Modules"
size_hint: 0.1, 0.5
pos_hint: {"right": 0.2, "top": 1.2}
Another thing that seems strange to me was how I had to use pos_hint to get my label spacing to work. As I understand it, these values should be on a scale between 0-1. A tutorial I read indicated as much--
Pos_hint gives a hint at the position, which is measured relatively
between 0 and 1, where 1 is "completely" something and 0 is "not"
something.
Does anyone know why it took using a value greater than 1 for "top" to get these labels at the top? I'm guessing this may be a hint as to why my layout isn't showing up correctly.
TreeView under Labels
The solution is to use GridLayout as child of BoxLayout (root widget, MainScreen), Labels and TreeViews as child of GridLayout. Added an ObjectProperty, container to hook it up to the GridLayout widget (place holder for the TreeView widget) created in the kv file. Please to the example for details.
Example - TreeView under Labels
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.treeview import TreeView, TreeViewLabel
from kivy.properties import ObjectProperty
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.10.0
<Label>:
font_size: 30
<MainScreen>:
container: tree_view
orientation: 'vertical'
GridLayout:
cols: 2
size_hint: 0.5, 0.1
Label:
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
size: self.size
pos: self.pos
text: "Connections"
color: 1,0,1,1
outline_color: 1,0,1,1
Label:
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
size: self.size
pos: self.pos
text: "Modules"
GridLayout:
cols: 1
id: tree_view
''')
class MainScreen(BoxLayout):
container = ObjectProperty(None)
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
bridge_view = TreeView(root_options=dict(text='Clients'),
hide_root=False, indent_level=4,
pos_hint={"x": 0, "y": 0},
size_hint=(0.1, 0.5))
clients = {'client1': 'Connection A',
'client2': 'Connection B'}
for client in clients.keys():
node = bridge_view.add_node(TreeViewLabel(text=client))
bridge_name = clients[client]
bridge = bridge_view.add_node(TreeViewLabel(text=bridge_name), node)
self.container.add_widget(bridge_view)
class DemoApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
DemoApp().run()
Output - TreeView under Labels
pos_hint: {'top': 1}
The reason you have to use a value greater than 1 for top because the Label widget's height is 0.5 (size_hint: 0.1, 0.5). The solution to use pos_hint: {'top': 1} is to reduce size_hint_y as shown in the snippets.
In my example, Label's canvas color were added for visualization/demonstration.
Snippets
Label:
...
size_hint: 0.1, 0.1
pos_hint: {"top": 1}
Example - pos_hint
kv file
#:kivy 1.10.0
<Label>:
font_size: 30
<MainScreen>:
Label:
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
size: self.size
pos: self.pos
text: "Connections"
size_hint: 0.1, 0.1
pos_hint: {"left": 0.2, "top": 1}
color: 1,0,1,1
outline_color: 1,0,1,1
Label:
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
size: self.size
pos: self.pos
text: "Modules"
size_hint: 0.1, 0.1
pos_hint: {"right": 0.2, "top": 1}
Output - pos_hint
Related
I'm trying to add labels (in this case LightL and DarkL) dynamically within another (grid)layout widget within python.
I tried to use IDs, but I'm unsure how to implement it using add_widget.
The childlabels have a backgroundcolor (rectangle) and I would like all the newly created to be in the same format.
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.screenmanager import Screen
from kivy.app import App
import kivy
kv = Builder.load_string("""
<BackgroundColor#Widget>
background_color: 1, 1, 1, 1
canvas.before:
Color:
rgba: root.background_color
Rectangle:
size: self.size
pos: self.pos
<LightL#Label+BackgroundColor>
background_color: 0.15, 0.15, 0.15, 1
size_hint_x: 0.0725
<DarkL#Label+BackgroundColor>
background_color: 0.1, 0.1, 0.1, 1
<IndexScreen>:
BoxLayout:
id: DynBox
GridLayout:
cols: 4
LightL:
id: LightLid
text: ""
DarkL:
id: DarkLid
text: ""
BoxLayout:
orientation: 'vertical'
Label:
text:'topright'
Label:
text:'bottomright'
""")
class IndexScreen(Screen):
pass
class MainApp(App):
xn = NumericProperty()
def build(self):
self.root = kv
self.AddLabel()
return self.root
def AddLabel(self, *arg):
for xn in range(0, 8):
App.DarkLid.add_widget(Builder.load_string('''
Label:
text:
'#D: {}'.format(self.xn)
'''))
App.LightLid.add_widget(Builder.load_string('''
Label:
text:
'#L: {}'.format(self.xn)
'''))
return
if __name__ == "__main__":
MainApp().run
I think the problem is with your kv definition of LightL and DarkL. It works if you simplify those definitions like this:
<LightL#Label>:
background_color: 0.15, 0.15, 0.15, 1
canvas.before:
Color:
rgba: root.background_color
Rectangle:
size: self.size
pos: self.pos
<DarkL#Label>:
background_color: 0.1, 0.1, 0.1, 1
canvas.before:
Color:
rgba: root.background_color
Rectangle:
size: self.size
pos: self.pos
Then, modifying your AddLabel() method:
def AddLabel(self, *arg):
for xn in range(0, 8):
self.root.ids.DynBox.add_widget(Builder.load_string('''
Label:
text:
''' + '\'{}\''.format(xn)
))
self.root.ids.DynBox.add_widget(Builder.load_string('''
Label:
text:
''' + '\'{}\''.format(xn)
))
The format(xn) cannot be inside the triple quotes, otherwise it is not executed. It appeared that you were trying to dynamically add the new Labels to the existing DarkL or LightL, but they are not widget containers, so I changed that to the BoxLayout just to demonstrate that it works.
For my game, I would like to to reset a screen when the user changes screen.
I have tried many things such as using the Clock and making a update function, but it never really work. The layout does not change even though I explicitly change it in python.
For example, if I have button that becomes disabled upon release, when I change screen, it should not be disabled anymore This something that has been on my mind for a long time and don't quite get it.
I have three small files for this main.py, screen_manager.kv and main.kv. Sorry for this noob question
main.py
from kivy.app import App
from kivy.clock import Clock
from kivy.config import Config
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.widget import Widget
# Setup the window
Config.set('graphics', 'resizable', False)
width = 550
height = 550
Window.size = (width, height)
class OptionWindow(Screen):
pass
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class Quiz(Widget):
def __init__(self, **kwargs):
super(Quiz, self).__init__(**kwargs)
def update(self, dt):
pass
kv = Builder.load_file('screen_manager.kv')
class Application(App):
CATEGORY = ''
def build(self):
game = Quiz()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return kv
if __name__ == '__main__':
Application().run()
screen_manager.kv
# File name: screen_manager.kv
#:include main.kv
WindowManager:
OptionWindow:
SecondWindow:
<OptionWindow>:
name: 'first'
Button:
text: "Reset SecondWindow"
on_release:
app.root.current = 'second'
root.manager.transition.direction = "right"
<SecondWindow>:
name: 'second'
Quiz:
FloatLayout:
size: root.width, root.height
pos: 0, 0
Button:
text: "Go back"
pos_hint: {'x': 0.4, 'y': 0.2}
size_hint: 0.6, 0.6
on_release:
app.root.current = 'first'
root.manager.transition.direction = "right"
main.kv
<Quiz>:
FloatLayout:
id: thelayout
size: root.width, root.height
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Button:
text: 'press me'
pos_hint: {'x': 0.0, 'y': 0.2}
size_hint: 0.3, 0.3
on_release: self.disabled = True
Thanks for you help
Your code provides for disabling the Button, but there is nothing in your code to re-enable it. Once a Button is disabled, it stays that way until something changes changes it. You can add something to your SecondWindow to re-enable the button every time the SecondWindow is displayed. You can use on_enter or on_pre_enter to trigger something to happen whenever that Screen is displayed. like this:
<SecondWindow>:
name: 'second'
on_pre_enter: quiz.ids.butt.disabled = False
Quiz:
id: quiz
FloatLayout:
size: root.width, root.height
pos: 0, 0
Button:
text: "Go back"
pos_hint: {'x': 0.4, 'y': 0.2}
size_hint: 0.6, 0.6
on_release:
app.root.current = 'first'
root.manager.transition.direction = "right"
Note the added id for the Quiz and the added on_pre_enter:. Also needed is a way to reference the Button. I have done that by adding an id to that Button:
<Quiz>:
FloatLayout:
id: thelayout
size: root.width, root.height
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Button:
id: butt
text: 'press me'
pos_hint: {'x': 0.0, 'y': 0.2}
size_hint: 0.3, 0.3
on_release: self.disabled = True
Now, everytime before the SecondWindow is displayed, the on_pre_enter: is triggered, and the `Button`` is re-enabled.
For a university project, I need to create an application to display interactive plot containing a lot of points (order of 100,000 to 1,000,000 points).
So I have to process the data and then display it. Building for the moment the interface with kivy / kivy garden, I am testing the calculation time and the display speed. To do that, I simply compute a for loop when refreshing the graph.
The problem is in the case of 500,000 points for example, the display time is approximatly 8-9 sec, which is quite high because tools for zooming, move the graph or some user input and some calculations are missing ! Do you have any advice or do you know how to optimize the following code to speed up a bit the app ?
Here is the code :
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
import numpy as np
import time
from kivy.core.window import Window
Window.clearcolor = (1, 1, 1, 1)
Window.size = (960, 540)
kv = """
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
<Manager>:
transition: SlideTransition()
StartPage:
name: 'StartPage'
SecondPage:
name: 'SecondPage'
<StartPage>:
canvas:
Color:
rgba: 0.1, 0.1, 1, 0.7
Rectangle:
pos: 0, 11*root.height/12
size: root.width, root.height/12
Rectangle:
pos: 0, 0
size: root.width, root.height/12
BoxLayout:
orientation: 'vertical'
size: root.width, root.height
Button:
size_hint: 0.15, 0.08
pos_hint: {'left': 0, 'top': 1 }
text: 'Go to Second Page'
on_release:
root.manager.current = 'SecondPage'
Button:
size_hint: 0.15, 0.08
pos_hint: {'right': 1, 'bottom': 1 }
text: 'Quit'
on_release: app.stop()
Label:
text: 'Start Screen'
font_size: '20sp'
color: 0,0,0,1
pos_hint:{'x': 0.42, 'y': 0.46 }
<SecondPage>:
box: box
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
id: box
size_hint: 0.8, 0.75
pos_hint:{'x': 0, 'y': 0.1 }
Button:
text:'Start Menu'
font_size: 20
bold: True
size_hint: 0.15, 0.08
pos_hint: {'left': 0, 'top': 1 }
color: 1, 1, 1, 1
on_press: root.manager.current = 'StartPage'
Button:
text:'Update'
font_size: 20
bold: True
size_hint: 0.15, 0.08
pos_hint: {'x':0.8, 'y': 0.5 }
color: 1, 1, 1, 1
on_press: root.Update()
"""
Builder.load_string(kv)
# Start Page
class StartPage(Screen):
pass
#Second Page
class SecondPage(Screen):
box = ObjectProperty(None)
def add_plot(self, N):
phase = np.random.normal(-np.pi/2, +np.pi/2)
noise = np.random.normal(0, 1, N)
nbr = np.linspace(0,1,N)
func = 0.01*noise+np.sin(nbr/0.05+phase)
plt.plot(nbr,func,label='Line1',color='r')
plt.ylabel('Sinus')
plt.xlabel('Range')
plt.grid(True)
self.fig1 = plt.gcf()
return self.fig1
def on_pre_enter(self, *args):
plt.cla()
self.box.clear_widgets()
self.fig1 = SecondPage.add_plot(self,10)
self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
def Update(self):
start = time.process_time()
N = int(2**19)
# test = 0.0
for i in range(N):
test = i/10+np.cos(i/(i+1)*2)
print("Number of points in the plot : ",N)
print("boucle for : ",test)
print("\nElapsed Time : ", time.process_time() - start)
plt.cla()
self.box.clear_widgets()
self.fig1 = SecondPage.add_plot(self,N)
self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
class Manager(ScreenManager):
pass
class MyNewApp(App):
title = "Matplolib - plot Update"
def build(self):
return Manager()
MyNewApp().run()
Thank you very much !
Starting to learn Kivy, I want to create a simple application consisting of a start page (very simple) and a second page where I plot data with a button to delete the old plot and display a new plot.
After some problems to control the size and position of the plot widget, I can show the 2 pages and the buttons. The problem is when I click on the "Update button", a new plot is displayed, but a second figure is added next to the old one. The same kind of problem appears when I return to the second page several times. Do you have any idea on how to reuse the old figure and update/reset it or how to fix this ?
Here is my code :
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
import numpy as np
## Here for providing colour to the background
from kivy.core.window import Window
## Setting the window size
Window.clearcolor = (1, 1, 1, 1) # White background
Window.size = (960, 540)
kv = """
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
<Manager>:
transition: SlideTransition()
StartPage:
name: 'StartPage'
SecondPage:
name: 'SecondPage'
<StartPage>:
canvas:
Color:
rgba: 0.1, 0.1, 1, 0.7
Rectangle:
pos: 0, 11*root.height/12
size: root.width, root.height/12
Rectangle:
pos: 0, 0
size: root.width, root.height/12
BoxLayout:
orientation: 'vertical'
size: root.width, root.height
Button:
size_hint: 0.15, 0.08
pos_hint: {'left': 0, 'top': 1 }
text: 'Go to Second Page'
on_release:
root.manager.current = 'SecondPage'
Button:
size_hint: 0.15, 0.08
pos_hint: {'right': 1, 'bottom': 1 }
text: 'Quit'
on_release: app.stop()
Label:
text: 'Start Screen'
font_size: '20sp'
color: 0,0,0,1
pos_hint:{'x': 0.42, 'y': 0.46 }
<SecondPage>:
box: box
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
id: box
size_hint: 0.8, 0.75
pos_hint:{'x': 0, 'y': 0.1 }
Button:
text:'Start Menu'
font_size: 20
bold: True
size_hint: 0.15, 0.08
pos_hint: {'left': 0, 'top': 1 }
color: 1, 1, 1, 1
on_press: root.manager.current = 'StartPage'
Button:
text:'Update'
font_size: 20
bold: True
size_hint: 0.15, 0.08
pos_hint: {'x':0.8, 'y': 0.5 }
color: 1, 1, 1, 1
on_press: root.Update()
"""
Builder.load_string(kv)
# Start Page
class StartPage(Screen):
pass
#Second Page
class SecondPage(Screen):
box = ObjectProperty(None)
def add_plot(self, N):
phase = np.random.normal(-np.pi/2, +np.pi/2)
noise = np.random.normal(0, 1, N)
nbr = np.linspace(0,1,N)
func = 0.01*noise+np.sin(nbr/0.1+phase)
plt.plot(nbr,func,label='Line1',color='r')
plt.ylabel('Sinus')
plt.xlabel('Range')
plt.grid(True)
self.fig1 = plt.gcf()
return self.fig1
def on_pre_enter(self, *args):
self.fig1 = SecondPage.add_plot(self,1000)
self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
def Update(self):
self.box.remove_widget(FigureCanvasKivyAgg(self.fig1))
self.fig1 = SecondPage.add_plot(self,1000)
self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
class Manager(ScreenManager):
pass
class MyNewApp(App):
title = "Matplolib - plot Update"
def build(self):
return Manager()
MyNewApp().run()
Thank you !
One method that does work which is a relatively easy fix: if you clear all the child widgets with self.box.clear_widgets() instead of specifying the widget, then call plt.cla() to remove your previous data from the plot, should do what you're after.
Edited code below:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
import matplotlib.pyplot as plt
import numpy as np
## Here for providing colour to the background
from kivy.core.window import Window
## Setting the window size
Window.clearcolor = (1, 1, 1, 1) # White background
Window.size = (960, 540)
kv = """
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
<Manager>:
transition: SlideTransition()
StartPage:
name: 'StartPage'
SecondPage:
name: 'SecondPage'
<StartPage>:
canvas:
Color:
rgba: 0.1, 0.1, 1, 0.7
Rectangle:
pos: 0, 11*root.height/12
size: root.width, root.height/12
Rectangle:
pos: 0, 0
size: root.width, root.height/12
BoxLayout:
orientation: 'vertical'
size: root.width, root.height
Button:
size_hint: 0.15, 0.08
pos_hint: {'left': 0, 'top': 1 }
text: 'Go to Second Page'
on_release:
root.manager.current = 'SecondPage'
Button:
size_hint: 0.15, 0.08
pos_hint: {'right': 1, 'bottom': 1 }
text: 'Quit'
on_release: app.stop()
Label:
text: 'Start Screen'
font_size: '20sp'
color: 0,0,0,1
pos_hint:{'x': 0.42, 'y': 0.46 }
<SecondPage>:
box: box
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
id: box
size_hint: 0.8, 0.75
pos_hint:{'x': 0, 'y': 0.1 }
Button:
text:'Start Menu'
font_size: 20
bold: True
size_hint: 0.15, 0.08
pos_hint: {'left': 0, 'top': 1 }
color: 1, 1, 1, 1
on_press: root.manager.current = 'StartPage'
Button:
text:'Update'
font_size: 20
bold: True
size_hint: 0.15, 0.08
pos_hint: {'x':0.8, 'y': 0.5 }
color: 1, 1, 1, 1
on_press: root.Update()
"""
Builder.load_string(kv)
# Start Page
class StartPage(Screen):
pass
#Second Page
class SecondPage(Screen):
box = ObjectProperty(None)
def add_plot(self, N):
phase = np.random.normal(-np.pi/2, +np.pi/2)
noise = np.random.normal(0, 1, N)
nbr = np.linspace(0,1,N)
func = 0.01*noise+np.sin(nbr/0.1+phase)
plt.plot(nbr,func,label='Line1',color='r')
plt.ylabel('Sinus')
plt.xlabel('Range')
plt.grid(True)
self.fig1 = plt.gcf()
return self.fig1
def on_pre_enter(self, *args):
self.fig1 = SecondPage.add_plot(self,1000)
self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
def Update(self):
#self.box.remove_widget(FigureCanvasKivyAgg(self.fig1))
plt.cla()
self.box.clear_widgets()
self.fig1 = SecondPage.add_plot(self,1000)
self.box.add_widget(FigureCanvasKivyAgg(self.fig1))
class Manager(ScreenManager):
pass
class MyNewApp(App):
title = "Matplolib - plot Update"
def build(self):
return Manager()
MyNewApp().run()
Problem:
Is there a way to fire an event if a multiline = True TextInput lost its focus?
Background:
I have tried on_touch_up function. But it returns the instances of all my TextInputs, not just the current widget. I tried text_validate_unfocus = False, with the same result.
Code:
import kivy
kivy.require("1.10.1")
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty, NumericProperty
from kivy.uix.textinput import TextInput
from kivy.uix.bubble import Bubble
from kivy.lang import Builder
from kivy.storage.jsonstore import JsonStore
Builder.load_string('''
#: import Window kivy.core.window.Window
<Button>:
background_normal: ''
<Label>:
canvas.before:
Color:
rgba: (0,0.59,0.36,1)
Rectangle:
pos: self.pos
size: self.size
<TextInput>:
hint_text: 'Nuwe nota'
font_size: self.height / 4.5 if self.focus else self.height / 3
background_normal: ''
background_active: ''
foreground_color: (0,0.61,0.36,1) if self.focus else (0.71,0.75,0.71,1)
unfocus_on_touch: False
canvas.after:
Color:
rgb: (0,0,0,1)
Line:
points: self.pos[0] , self.pos[1], self.pos[0] + self.size[0], self.pos[1]
size_hint_y: None
height: Window.height / 6 if self.focus else Window.height / 12
<ChoiceBubble>:
orientation: 'horizontal'
size_hint: (None, None)
size: (160, 120)
pos_hint: {'top': 0.2, 'right': 0.8}
arrow_pos: 'top_left'
BubbleButton:
text: 'Save'
BubbleButton:
text: 'Encrypt..'
BubbleButton:
text: 'Delete'
on_release: root.del_txt_input()
<Notation>:
canvas:
Color:
rgba: (0,0.43,0.37,1)
Rectangle:
pos: self.pos
size: self.size
Label:
pos_hint: {'top': 1, 'right': 0.8}
size_hint: [0.8, None]
height: Window.height / 15
Button:
color: (0,0,0,1)
pos_hint: {'top': 1, 'right': 0.9}
size_hint: [0.1, None]
height: Window.height / 15
Image:
source: 'gear_2.png'
center_y: self.parent.center_y
center_x: self.parent.center_x
size: self.parent.width /1.5, self.parent.height/ 1.5
allow_stretch: True
Button:
color: (0,0,0,1)
pos_hint: {'top': 1, 'right': 1}
size_hint: [0.1, None]
height: Window.height / 15
on_release: root.add_input()
Image:
source: 'plus_text12354.png'
center_y: self.parent.center_y
center_x: self.parent.center_x
size: self.parent.width /1.5, self.parent.height/ 1.5
allow_stretch: True
ScrollView:
size_hint_y: None
size: Window.width, Window.height
pos_hint: {'top': 0.92, 'right': 1}
GridLayout:
id: text_holder
cols: 1
pos_hint: {'top': 0.92, 'right': 1}
padding: 4
size_hint_x: 1
size_hint_y: None
height: self.minimum_height
''')
class ChoiceBubble(Bubble):
pass
class TextInput(TextInput):
got_txt = ObjectProperty(None)
def on_touch_up(self, touch):
if not self.collide_point(*touch.pos):
self.text_validate_unfocus = False
note = Notation()
note.show_bubble
self.got_txt=note.que_txt_input(self)
return super(TextInput, self).on_touch_up(touch)
class Notation(FloatLayout):
which_txt = ObjectProperty(None)
new_txt = ObjectProperty(None)
cnt = NumericProperty(0)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.the_file=JsonStore('txt_input.json')
self.cnt = self.the_file.count()
lst = self.the_file.keys
def add_input(self):
txt_hld = self.ids.text_holder
self.cnt += 1
self.new_txt = TextInput(id=str(self.cnt))
self.the_file.put(str(self.cnt), the_id=str(self.cnt), the_input='')
txt_hld.add_widget(self.new_txt)
def que_txt_input(self, instance):
self.which_txt = instance
print(instance.text, instance)
return instance
def del_txt_input(self):
print(self.which_txt)
def the_file(self, notestore):
self.notestore = notestore
def show_bubble(self):
self.add_widget(ChoiceBubble())
def get_store(self):
the_keys = list(self.the_file.keys)
print(the_keys)
return the_keys
class theNoteApp(App):
title = 'My Notes'
def build(self):
return Notation()
if __name__ == '__main__':
theNoteApp().run()
Desired Result:
Once the focus is lost I want a bubble widget to be added to the top of my root class. This will give the user the option to save, encrypt or delete the TextInput id and text that just lost its focus, to a JSON file.
Problems
Multiple instances of object, Notation. One from build() method (return Notation()) in App class, and another instance created in on_touch_up() method (note = Notation()) whenever on_touch_up event is fired. Those instances created in on_touch_up() method does not has a visible view i.e. it won't show up in the window.
AttributeError: 'ChoiceBubble' object has no attribute 'del_txt_input'
Text in ChoiceBubble not visible i.e. the default text color is white on white background.
Solutions
Use App.get_running_app().root to get the instantiated root.
Replace root.del_txt_input() with app.root.del_txt_input()
Add color: 0, 0, 0, 1 to class rule, <Label>:
Use on_focus event to display BubbleButton
Snippets - kv
<Label>:
color: 0, 0, 0, 1
...
<ChoiceBubble>:
...
BubbleButton:
text: 'Delete'
on_release: app.root.del_txt_input()
Snippets - py file
class TextInput(TextInput):
got_txt = ObjectProperty(None)
def on_focus(self, instance, value):
if not value: # defocused
note = App.get_running_app().root
note.show_bubble()
self.got_txt = note.que_txt_input(self)
Output
The on_focus event will fire when the focus boolean changes.
I have tried on_touch_up function. But it returns the instances of all my TextInputs, not just the current widget.
This is because you didn't write any code that would limit it to the widget you care about, you can do this if you want to.