How to code for a variable in the Kivy label? - kivy

I'm trying to create a simple Kivy function that counts and updates the display in the label as either a variable or a variable that has been converted to string. Using Python 3.7 and Kivy 1.10.1
I've been reading previous questions related to labels, but they don't seem to fix my issue. Thanks.
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
import time
class SomeData():
num = 0
while num < 1000:
time.sleep(1)
num+=1
class FirstScreen (Screen):
runTouchApp(Builder.load_string('''
ScreenManager:
FirstScreen:
<FirstScreen>
BoxLayout:
orientation: 'vertical'
GridLayout:
cols: 3
spacing: '10dp'
Button:
Button:
Button:
Label:
size_hint_y: None
text: "Below is a scroll of numbers."
ScrollView:
Label:
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]
halign: 'left'
valign: 'top'
text: (num)
'''))
The file never creates the Kivy screen and num variable is considered an error in the text label.

Here is a version for your code that will, at least, display:
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.screenmanager import Screen
#import time
# class SomeData:
# num = 0
# while num < 1000:
# time.sleep(1)
# num+=1
class FirstScreen (Screen):
num = NumericProperty(7)
runTouchApp(Builder.load_string('''
ScreenManager:
FirstScreen:
<FirstScreen>
BoxLayout:
orientation: 'vertical'
GridLayout:
cols: 3
spacing: '10dp'
Button:
Button:
Button:
Label:
size_hint_y: None
text: "Below is a scroll of numbers."
ScrollView:
Label:
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]
halign: 'left'
valign: 'top'
text: str(root.num)
'''))
The SomeData class is commented out, since it does nothing but delay the display. Also, note that changing the value of num in a loop will not create a list of numbers, but will just change the number that is displayed in the Label. There is no need to import the time too. So, commented it.

Related

drag file onto bounding area of kivy widget

I want to display an Image when I drag a .png into a specific area of my Kivy window. I've been trying to visualize the bounding area of my widgets and layouts using
canvas.before:
Color:
rgb: 1, 0, 0
Rectangle:
pos: self.pos
size: self.size
However I'm not convinced I understand this yet, because of the behavior I get with the following:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Nov 20 08:42:50 2022
#author: erik
"""
import kivy
kivy.require('2.1.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.core.window import Window
Builder.load_string('''
<MyLayout>:
padding: 20,20, 20, 20
id: img_box
orientation: 'vertical'
size_hint_min_x: self.minimum_width
size_hint_min_y: self.minimum_height
canvas.before:
Color:
rgb: 1, 0, 0
Rectangle:
pos: self.pos
size: self.size
Splitter:
sizable_from: 'bottom'
id: dig_img_spltr
canvas.before:
Color:
rgb: 1, 1, 0
Rectangle:
pos: self.pos
size: self.size
#keep_within_parent: True
rescale_with_parent: True
Image:
id: dig_img
Button:
text: 'hello'
size_hint: .6,.6
pos_hint: {'center_x': .5, 'center_y':.5}
''')
class MyLayout(BoxLayout):
digimgfilePath = StringProperty('')
def __init__(self, **kwargs):
super(MyLayout, self).__init__(**kwargs)
Window.bind(on_drop_file=self._on_file_drop)
def _on_file_drop(self, window, filename, x, y):
'''
Documentataion for on_drop_file
doesn't show window parameter. I
found this out with locals()
'''
print(f'x: {x}')
print(f'y: {y}')
x_lower_bound = self.ids.dig_img_spltr.pos[0]
x_upper_bound = self.ids.dig_img_spltr.pos[0] + self.ids.dig_img_spltr.width
y_lower_bound = self.ids.dig_img_spltr.pos[1]
y_upper_bound = self.ids.dig_img_spltr.pos[1] + self.ids.dig_img_spltr.height
print(f'xlb {x_lower_bound}')
print(f'xub {x_upper_bound}')
print(f'ylb {y_lower_bound}')
print(f'yub {y_upper_bound}')
print()
#if x_lower_bound < x < x_upper_bound and y_lower_bound < y < y_upper_bound:
if self.ids.dig_img_spltr.collide_point(x,y):
self.digimgfilePath = filename.decode("utf-8") # convert byte to string
self.ids.dig_img.source = self.digimgfilePath
self.ids.dig_img.reload() # reload image
class sliderdropApp(App):
def build(self):
return MyLayout()
if __name__ == '__main__':
sliderdropApp().run()
What I want, and expect, is for a image (.png for example) to be displayed when I drop the file into the area above the splitter. But I can't make sense of the area where collide_point returns True. It returns True when I drop the file within some un-explainable margin above and below the splitter. After I do get an image to display, the splitter canvas does to turn yellow above the splitter. Is this yellow area defined by the canvas not the same area of the splitter? Why doesn't collide_point return True when I drop on the area colored by the splitter's canvas?
The y dimension from the on_drop_file event is inverted from the window coordinates. If I send (x, Window.size[1] - y) to collide_point, it works as I expect and intent it to.

Touch Input in Kivy

I have a simple app with 2 buttons and their attached callback functions. When a button is pressed, I need to get and use the spos info. I'm using the following code
Python:
class HomeScreen(Screen):
print('Start')
port = "COM7"
bluetooth = serial.Serial(port, 9600)
print("Connected to HC-06")
def button_press_down(self, *args):
print("button pressed #location:")
self.bluetooth.write(b"2")
def button_press_up(self, *args):
print("button pressed #location:")
self.bluetooth.write(b"3")
def on_touch_down(self, touch):
print(touch)
def on_touch_move(self, touch):
pass
class SecondScreen(Screen):
pass
class WindowManager(ScreenManager):
pass
kv=Builder.load_file('touch.kv')
class MainApp(App):
def build(self):
return kv
if __name__ == "__main__":
TestApp = MainApp()
TestApp.run()
.kv file:
#:import utils kivy.utils
WindowManager:
HomeScreen:
SecondScreen:
<HomeScreen>:
name: "button press"
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#ffffff")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#ffffff")
Rectangle:
size: self.size
pos: self.pos
rows: 1
cols: 2
padding:10
Button:
text: "Button_1"
id:send_data_bluettooh
on_press:
root.button_press_down()
Button:
text: "Button_2"
id:send_data_bluettooh_2
on_press:
root.button_press_up()
<SecondScreen>:
name: "SecondScreen"
I can get the spos by overwriting the "on_touch_down" fct. The problem there is that my fct button_press_down() is no longer called when I press button 1. Is there any way I can use the spos info and still keep the callback function button_press_down() of my button?
You can get the touch.spos by extending the Button class so that it remembers that information. Here is a modified version of your code that does that:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
class MyButt(Button):
last_touch_down = ListProperty([])
last_touch_up = ListProperty([])
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.last_touch_down = touch.spos
return super(MyButt, self).on_touch_down(touch)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
self.last_touch_up = touch.spos
return super(MyButt, self).on_touch_up(touch)
class HomeScreen(Screen):
print('Start')
port = "COM7"
bluetooth = serial.Serial(port, 9600)
print("Connected to HC-06")
def button_press_down(self, button):
print("button pressed #location:", button.last_touch_down)
self.bluetooth.write(b"2")
def button_press_up(self, button):
print("button pressed #location:", button.last_touch_down)
self.bluetooth.write(b"3")
# def on_touch_down(self, touch):
# print(touch)
# return super(HomeScreen, self).on_touch_down(touch)
#
# def on_touch_move(self, touch):
# pass
class SecondScreen(Screen):
pass
class WindowManager(ScreenManager):
pass
kv=Builder.load_file('touch.kv')
class MainApp(App):
def build(self):
return kv
if __name__ == "__main__":
TestApp = MainApp()
TestApp.run()
And replacing Button with MyButt in the kv:
#:import utils kivy.utils
WindowManager:
HomeScreen:
SecondScreen:
<HomeScreen>:
name: "button press"
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#ffffff")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#ffffff")
Rectangle:
size: self.size
pos: self.pos
rows: 1
cols: 2
padding:10
MyButt:
text: "Button_1"
id:send_data_bluettooh
on_press:
root.button_press_down(self)
MyButt:
text: "Button_2"
id:send_data_bluettooh_2
on_press:
root.button_press_up(self)
<SecondScreen>:
name: "SecondScreen"

How can make a menu with two or more options when I `click` on `FileBox` object?

How can make a menu with two or more options when I click on FileBox object in below code? Plesae have a look on the attached pic if you don't know what I mean.
CODE
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
"""
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
John must have answered while I was looking at the question. John's answer is perfectly reasonable. My answer is a bit different because it depends on Popup.
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.core.window import Window
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
<MenuPopup>:
BoxLayout:
orientation: "vertical"
Button:
text: "Delete"
on_press: print("Delete")
Button:
text: "Copy"
on_press: print("Copy")
"""
class MenuPopup(Popup):
pass
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
print(touch.pos)
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
popup = MenuPopup(title='Menu',
auto_dismiss=True,
size_hint=(None, None),
size=(180, 180),
pos_hint={'x': touch.pos[0] / Window.width,
'y':(touch.pos[1] - self.height)/ Window.height})
popup.open()
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
Update
If you don't like the frame by Popup you can also use the class on which Popup is based. It is called ModalView. You also need to delete the title since ModalView has no title.
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.modalview import ModalView
from kivy.uix.label import Label
from kivy.core.window import Window
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
<MenuPopup>:
BoxLayout:
orientation: "vertical"
Button:
text: "Delete"
on_press: print("Delete")
Button:
text: "Copy"
on_press: print("Copy")
"""
class MenuPopup(ModalView):
pass
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
print(touch.pos)
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
popup = MenuPopup(
auto_dismiss=True,
size_hint=(None, None),
size=(180, 180),
pos_hint={'x': touch.pos[0] / Window.width,
'y':(touch.pos[1] - self.height)/ Window.height})
popup.open()
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
Typically, a DropDown is attached to a Button, but that is not necessary. You can create the DropDown as described in the documentation and instead of binding it to a Button to open it, you can just call open() in your on_touch_down() method. Here is a modified version of your code that does that:
from kivy.config import Config
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
"""
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
# create the DropDown
self.dropdown = DropDown(auto_dismiss=False)
# add Buttons
btn = Button(text='Delete', size_hint_y=None, height=44)
btn.bind(on_release=self.function_called_from_drop_menu)
self.dropdown.add_widget(btn)
btn = Button(text='Copy', size_hint_y=None, height=44)
btn.bind(on_release=self.function_called_from_drop_menu)
self.dropdown.add_widget(btn)
# open the DropDown
self.dropdown.open(self)
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
# dismiss the DropDown
self.dropdown.dismiss()
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()

Change nested property in KV code

In my KV file, I have created a Spinner widget on my window with about 10 items in it. However when I open the dropdown, it fills the whole vertical space of the window.
I've found a few solutions online, but they only show it with direct Python code.
Example:
spinner = Spinner(text='Test', values=('Hi', 'how', 'are', 'you', '?'))
spinner.dropdown_cls.max_height = 100
Here is my current KV code:
Spinner:
size_hint_x: None
width: 300
font_size: 30
text: "Static"
values: "Static", "Breathing", "Spectrum Cycle", "Rainbow", "Wipe", "Bullet", "Strobe", "Starlight", "Nightlight"
My question is how do I set the 'dropdown_cls.max_height' property in my KV code?
Solution
In the kv file, do the following. Please refer to snippets and example for details.
DropDown max_height
Create a class rule, <MyDropdown#DropDown>: and add max_height: 100.
<MyDropdown#DropDown>:
max_height: 100
Spinner fills whole vertical window space
Replace size_hint_x: None with size_hint: (None, None) to prevent Spinner from filling the whole vertical space of the window.
Example
main.py
from kivy.lang import Builder
from kivy.base import runTouchApp
runTouchApp(Builder.load_string('''
#:import Factory kivy.factory.Factory
<MySpinnerOptions#SpinnerOption>:
background_color: .4, .4, .4, 1
<MyDropdown#DropDown>:
max_height: 100
<MySpinner#Spinner>:
size_hint: (None, None)
width: 300
font_size: 30
text: "Static"
values: "Static", "Breathing", "Spectrum Cycle", "Rainbow", "Wipe", "Bullet", "Strobe", "Starlight", "Nightlight"
dropdown_cls: Factory.MyDropdown
option_cls: Factory.MySpinnerOptions
GridLayout:
cols: 1
MySpinner:
'''))
Output

Why am I not getting any output from the pong.kv file?

I'm learning kivy and doing the pong tutorial. All I get is a black window. I added a print('test') in the pong.kv file and received an error, so the main.py file is finding pong.kv. Both files are in the same directory. I'm using python v3.6 and kivy v1.10.0
Question - Why am I not getting any output from the pong.kv file?
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.graphics import Rectangle
class PongGame(Widget):
pass
class pongApp(App):
def build(self):
return PongGame()
if __name__ == '__main__':
pongApp().run()
This is the pong.kv file,
#:kivy 1.10.0
<PongGame >:
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: "0"
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: "0"

Resources