Kivy display speed for data calculation visualization - python

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 !

Related

Reset previous screen upon changing screen or clicking on a button

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.

Kivy update matplotlib plot

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()

Image display blank upon being updated

I am using a timer to update the image on the UI after every second. After starting the UI the first image is being displayed. After the timer thread kicks in to display the next image, the widget is displayed blank (i cannot see the image). I can see the image if i start the index from a different value. I do not have an issue with the images. I am unable to update the image on the UI screen. Can anyone help me figure out the issue?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.image import AsyncImage
from kivy.properties import StringProperty, ListProperty, ObjectProperty
import json, timer
import threading
data = json.load(open('demotest.json'))
Builder.load_file('dv_demo.kv')
class DVDemo(Widget):
message = StringProperty()
image = StringProperty('')
sms = ListProperty()
idx = 0
def __init__(self, **kwargs):
super(DVDemo, self).__init__(**kwargs)
self.update_data_index(self.idx)
def schedule_screens(self):
self.idx = self.idx + 1
if self.idx >= len(data):
time.sleep(500)
temp = threading.Thread(target=self.update_data_index, args=(self.idx,))
threading.Timer(1, temp.start).start()
def update_data_index(self, idx):
# self.image = data[idx]['picture_url']
self.message = data[idx]['message']
self.sms = data[idx]['sms']
message_layout = self.ids.message_panel
message_btn = Button(text=self.message, background_color=[0, 1, 1, 1])
message_layout.add_widget(message_btn)
sms_layout = self.ids.sms_panel
sms_layout.clear_widgets()
for sms_msg in self.sms:
sms_btn = Button(text=sms_msg['sms'], background_color=[0, 0, 2, 1])
sms_layout.add_widget(sms_btn)
self.ids.image_object.source = data[idx]['picture_url']
self.schedule_screens()
class DVDemoApp(App):
def build(self):
self.title = 'DV Demo App'
return DVDemo()
if __name__ == "__main__":
DVDemoApp().run()
<ColoredLabel#Label>:
text_size: self.size
halign: 'left'
valign: 'top'
padding: 4, 4
bold: True
color: (.6, .6, .6, 1)
canvas.before:
Color:
rgb: (.9, .9, .9)
Rectangle:
pos: self.pos
size: self.width - sp(2), self.height - sp(2)
<DVDemo>:
FloatLayout:
id: demo_panels
size: root.width - 50, root.height - 50
pos: root.x + 25, root.y + 25
BoxLayout:
id: message_panel
pos_hint: {"x":0, "top":1}
size_hint: 0.5, 1
orientation: 'vertical'
canvas.before:
Color:
rgb: (.9, .9, .9)
Rectangle:
pos: self.pos
size: self.width - sp(2), self.height - sp(2)
#ColoredLabel:
# id: message_panel
# text: root.message
# pos_hint: {"x":0, "top":1}
# size_hint: 0.5, 1
BoxLayout:
id: image_panel
#text: "Image"
pos_hint: {"x":0.5, "top":1}
size_hint: 0.5, 0.5
Image:
id: image_object
source: root.image
center_x: self.parent.center_x
center_y: self.parent.center_y
size: self.parent.height,self.parent.width
BoxLayout:
id: sms_panel
pos_hint: {"x":0.5, "top":0.5}
size_hint: 0.5, 0.5
orientation: 'vertical'
canvas.before:
Color:
rgb: (.9, .9, .9)
Rectangle:
pos: self.pos
size: self.width - sp(2), self.height - sp(2)
Setting the GUI properties must be done on the main thread. Conveniently, there is a Clock.schedule_once() method that schedules a call on the main thread after a delay. So, I would suggest modifying part of the DVDemo class as:
class DVDemo(Widget):
message = StringProperty()
image = StringProperty('')
sms = ListProperty()
idx = 0
def __init__(self, **kwargs):
super(DVDemo, self).__init__(**kwargs)
self.update_data_index()
def schedule_screens(self):
self.idx = self.idx + 1
Clock.schedule_once(self.update_data_index, 5)
def update_data_index(self, *args):
idx = self.idx
.
.
.
The above code uses Clock.schedule_once() and eliminates the passing of idx among methods, since it is an attribute of the DVDemo instance.

Is there a focus lost event dispatcher for a kivy TextInput?

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.

Kivy Treeview Position Issues

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

Categories