Kivy update matplotlib plot - python

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

Related

Kivy display speed for data calculation visualization

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 !

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: Can I freely change the position of text within a button? and add another text in the button?

t3.py
#-*- coding: utf-8 -*-
__version__ = "1.0"
import kivy
import os
kivy.require('1.10.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.core.window import Window
Window.size = (540, 960)
class StationTest(Screen):
def __init__(self, **kwargs):
super(StationTest, self).__init__(**kwargs)
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("t3.kv")
class Test2App(App):
def build(self):
return presentation
Test2App().run()
t3.kv
# -*- coding: utf-8 -*-
#:kivy 1.10.0
#:import NoTransition kivy.uix.screenmanager.NoTransition
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
#:import Button kivy.uix.button.Button
ScreenManagement:
transition: SlideTransition(direction='left')
StationTest:
<StationTest>:
name: 'StationTest'
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'image/background.png' #backgroundimage
header: _header
ScrollView:
FloatLayout:
size_hint_y: None
height: 500
BoxLayout:
id: _header
orientation: 'vertical'
size_hint: 1, 0.10
pos_hint: {'top': 1.0}
anchor: _anchor
canvas:
Color:
rgba: 0.8, 0.6, 0.4, 1.0
Rectangle:
pos: self.pos
size: self.size
Label:
text: "STATION > STATION"
font_size: 40
BoxLayout
id: _anchor
size_hint_y: 0.3
canvas.before:
Color:
rgba: 0.3, 0.5, 0.8, 1.0
Rectangle:
pos: self.pos
size: self.size
Label:
text: "TEST1234"
BoxLayout:
orientation: 'vertical'
#size_hint: 1, 0.35
size: 1,0.35
spacing: 10
pos_hint:{"center_x":0.5,"y":-0.6}
padding: 0, -200, 0, 0
GridLayout:
cols: 1
rows: 10
spacing: 0
padding: [0,100]
on_parent:
for i in range(10): txt = 'abcdef'; self.add_widget(Button(text = txt, text_size=(cm(2), cm(2)), background_color=(255,255,255,1),
pos=self.pos, id=txt, color=(0,0,0,1))) # 0~1.0
i want add '123456' text apart from 'abcdef' in one button. i can code txt = 'abcdef' + '123456' but this is not i wanted. I would like to add the texts in the button and apply the desired options to each text, but it is not easy.
on_parent:
for i in range(10): txt = 'abcdef'; self.add_widget(Button(text = txt, text_size=(cm(2), cm(2)), background_color=(255,255,255,1),
pos=self.pos, id=txt, color=(0,0,0,1))) # 0~1.0
So I have two questions.
1.Can I put two texts that can move independently in one button?
2.Is it possible to set the position of the text freely within the button?
Can I put two texts that can move independently in one button? No, you cannot
Is it possible to set the position of the text freely within the button? No, you cannot
Solution
Create custom button with inheritance of ButtonBehavior and BoxLayout. Inside the BoxLayout, there are three Label widgets.
Snippet
<CustomLabel#Label>:
text_size: root.width, None
size: self.texture_size
valign: 'center'
halign: 'right'
<CustomButton>:
color: 0, 0, 0, 1 # black color text
orientation: 'vertical'
canvas.before:
Rectangle:
size: self.size
pos: self.pos
size_hint: 1, None
height: 60
padding: 5, 5, 5, 5
background_color: [0.50196, 0.50196, 0.50196, 1] if self.state == 'normal' else [0, 1, 1, 1]
AnchorLayout:
canvas.before:
Color:
rgba: [1, 1, 1, 1] if root.background_color is None else root.background_color
Rectangle:
size: self.size
pos: self.pos
AnchorLayout:
anchor_x: 'left'
anchor_y: 'top'
CustomLabel:
text: root.route
color: root.color
halign: 'left'
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
CustomLabel:
text: root.stations
color: root.color
AnchorLayout:
canvas.before:
Color:
rgba: [1, 1, 1, 1] if root.background_color is None else root.background_color
Rectangle:
size: self.size
pos: self.pos
anchor_x: 'right'
anchor_y: 'bottom'
CustomLabel:
text: root.commute
color: root.color
Example
main.py
#-*- coding: utf-8 -*-
__version__ = "1.0"
import os
import kivy
kivy.require('1.11.0')
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.core.window import Window
Window.size = (540, 960)
from kivy.uix.button import ButtonBehavior
from kivy.properties import StringProperty
def oopath(ndid, uor):
path = os.path.join(os.path.dirname(__file__), ndid + '.txt')
return path
##############################################################################################################
class StationTest(Screen):
def __init__(self, **kwargs):
super(StationTest, self).__init__(**kwargs)
oo = oopath('TESTTEST', 0)
self.rpandgv(oo)
def rpandgv(self, path):
with open(path) as businfo:
Businfo = []
for line in businfo:
Businfo.append(line.rstrip())
self.businfolist = Businfo
self.lenbil = int(len(Businfo))
self.numberoflist = int(len(Businfo)/3)
def populate_businfo(self, instance):
for x in range(0, self.lenbil, 3):
instance.add_widget(CustomButton(route=self.businfolist[x], stations=self.businfolist[x+1], commute=self.businfolist[x+2]))
class ScreenManagement(ScreenManager):
pass
class CustomButton(ButtonBehavior, BoxLayout):
route = StringProperty('')
stations = StringProperty('')
commute = StringProperty('')
def on_release(self):
print("\troute={0}, stations={1}, commute={2}".format(self.route, self.stations, self.commute))
presentation = Builder.load_file("test.kv")
class Test2App(App):
def build(self):
return presentation
Test2App().run()
test.kv
# -*- coding: utf-8 -*-
#:kivy 1.11.0
#:import NoTransition kivy.uix.screenmanager.NoTransition
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
#:import Button kivy.uix.button.Button
ScreenManagement:
transition: SlideTransition(direction='left')
StationTest:
<StationTest>:
name: 'StationTest'
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'image/background.png' #backgroundimage
header: _header
ScrollView:
FloatLayout:
size_hint_y: None
height: 500
BoxLayout:
id: _header
orientation: 'vertical'
size_hint: 1, 0.10
pos_hint: {'top': 1.0}
anchor: _anchor
canvas:
Color:
rgba: 0.8, 0.6, 0.4, 1.0
Rectangle:
pos: self.pos
size: self.size
Label:
text: "STATION > STATION"
font_size: 40
BoxLayout
id: _anchor
size_hint_y: 0.3
canvas.before:
Color:
rgba: 0.3, 0.5, 0.8, 1.0
Rectangle:
pos: self.pos
size: self.size
Label:
text: "TEST1234"
BoxLayout:
orientation: 'vertical'
#size_hint: 1, 0.35
size: 1,0.35
spacing: 10
pos_hint:{"center_x":0.5,"y":-0.6}
padding: 0, -200, 0, 0
GridLayout:
cols: 1
rows: 10
spacing: 0
padding: [0,100]
on_parent:
root.populate_businfo(self)
<CustomLabel#Label>:
text_size: root.width, None
size: self.texture_size
valign: 'center'
halign: 'right'
<CustomButton>:
color: 0, 0, 0, 1 # black color text
orientation: 'vertical'
canvas.before:
Rectangle:
size: self.size
pos: self.pos
size_hint: 1, None
height: 60
padding: 5, 5, 5, 5
background_color: [0.50196, 0.50196, 0.50196, 1] if self.state == 'normal' else [0, 1, 1, 1]
AnchorLayout:
canvas.before:
Color:
rgba: [1, 1, 1, 1] if root.background_color is None else root.background_color
Rectangle:
size: self.size
pos: self.pos
AnchorLayout:
anchor_x: 'left'
anchor_y: 'top'
CustomLabel:
text: root.route
color: root.color
halign: 'left'
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
CustomLabel:
text: root.stations
color: root.color
AnchorLayout:
canvas.before:
Color:
rgba: [1, 1, 1, 1] if root.background_color is None else root.background_color
Rectangle:
size: self.size
pos: self.pos
anchor_x: 'right'
anchor_y: 'bottom'
CustomLabel:
text: root.commute
color: root.color
Output

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

Positioning multiple widgets in a BoxLayout in Kivy.

I have two widgets in a horizontal boxlayout. Afaik they will both stretch to fill half of the boxlayout. How do I go about resizing the widgets and repositioning within the boxlayout? Is it worth putting each widget in its own boxlayout so that it isn't affected by the widget beside it? Here is my code so far:
from kivy.app import App
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ShoppingListScreen(Screen):
pass
class theScreenManager(ScreenManager):
pass
root_widget = Builder.load_string('''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
theScreenManager:
ShoppingListScreen:
<ShoppingListScreen>:
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
size_hint: 1, .3
Button:
text: '<'
size_hint: .1, 1
font_size: 75
background_normal: ""
background_color: 0.18, .5, .92, 1
on_release: app.root.current = 'main'
Label:
text: 'Login'
halign: 'left'
font_size: 50
canvas.before:
Color:
rgb: 0.18, .5, .92
Rectangle:
pos: self.pos
size: self.size
Widget:
size_hint: .1, 1
canvas.before:
Color:
rgb: 0.18, .5, .92
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'horizontal'
padding_left: 250
Label:
text: 'Username:'
font_size: 25
color: 0.18, .5, .92, 1
size_hint: .2, .5
pos_hint: {'top': 1}
TextInput:
size_hint: .3, .1
Button:
text: 'Register'
font_size: 35
size_hint: 1, .3
background_normal: ""
background_color: 0.18, .5, .92, 1
#on_release: app.root.current = 'main'
Label:
text: 'Support'
color: 0.18, .5, .92, 1
halign: 'left'
font_size: 25
size_hint: 1, .3
canvas.before:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
''')
class temprecipeapp(App):
def build(self):
return root_widget
if __name__ == "__main__":
temprecipeapp().run()
If you run this file you will see the username and textinput box are in skewered positions. I am trying to center them and put them both beside each other. How would I go about doing so?
Maybe this will help:
I just import GridLayout and change part with Label:Usename and TextInput from BoxLayout to GridLayout.
from kivy.app import App
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ShoppingListScreen(Screen):
pass
class theScreenManager(ScreenManager):
pass
root_widget = Builder.load_string('''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
theScreenManager:
ShoppingListScreen:
<ShoppingListScreen>:
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
size_hint: 1, .3
Button:
text: '<'
size_hint: .1, 1
font_size: 75
background_normal: ""
background_color: 0.18, .5, .92, 1
on_release: app.root.current = 'main'
Label:
text: 'Login'
halign: 'left'
font_size: 50
canvas.before:
Color:
rgb: 0.18, .5, .92
Rectangle:
pos: self.pos
size: self.size
Widget:
size_hint: .1, 1
canvas.before:
Color:
rgb: 0.18, .5, .92
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols: 2
rows: 1
size_hint: 1, .1
canvas.before:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'Username:'
font_size: 25
size_hint: .3, 1
color: 0.18, .5, .92, 1
pos_hint: {'center': 1}
TextInput:
size_hint: .7, 1
font_size: 25
Button:
text: 'Register'
font_size: 35
size_hint: 1, .3
background_normal: ""
background_color: 0.18, .5, .92, 1
#on_release: app.root.current = 'main'
Label:
text: 'Support'
color: 0.18, .5, .92, 1
halign: 'left'
font_size: 25
size_hint: 1, .3
canvas.before:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
''')
class temprecipeapp(App):
def build(self):
return root_widget
if __name__ == "__main__":
temprecipeapp().run()

Categories