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"
Related
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.
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()
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.
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
I want to use Kivy to rotate an image of a 45RPM record. I discovered that this can be done using an image widget on a scatter layout. However I can't seem to find the code to make the image of the 45RPM record be resized to match the size of the window. I've tried for hours different iterations of code and I've become quite frustrated. Full code below with link to image.
Any Suggestions?
Appreciated In Advance.
....brad....
Image for code at: https://drive.google.com/open?id=0B-T2cvsAoZ2vQ2hmaHM0SnlQVlU
# Modified from https://gist.github.com/tshirtman/6222891
from kivy.app import App
from kivy.properties import NumericProperty
from kivy.lang import Builder
from kivy.clock import Clock
kv = """
BoxLayout:
Widget:
Scatter:
center: self.parent.center
do_rotation: False
do_translation: False
do_scale: False
rotation: app.angle
Image:
source: '45rpm.png'
"""
class RotateRecordApp(App):
angle = NumericProperty(0)
def build(self):
Clock.schedule_interval(self.update_angle, 0)
return Builder.load_string(kv)
def update_angle(self, dt, *args):
self.angle += dt * 100
if __name__ == '__main__':
RotateRecordApp().run()
Use scale property:
# Modified from https://gist.github.com/tshirtman/6222891
from kivy.app import App
from kivy.properties import NumericProperty
from kivy.lang import Builder
from kivy.clock import Clock
kv = """
BoxLayout:
Widget:
# Gray background
canvas.before:
Color:
rgba: 0.1, 0.1, 0.1, 1
Rectangle:
pos: self.pos
size: self.size
Scatter:
center: self.parent.center
do_rotation: False
do_translation: False
do_scale: False
rotation: app.angle
scale: min(self.parent.width/self.width,\
self.parent.height/self.height)
Image:
source: '45rpm.png'
"""
class RotateRecordApp(App):
angle = NumericProperty(0)
def build(self):
Clock.schedule_interval(self.update_angle, 0)
return Builder.load_string(kv)
def update_angle(self, dt, *args):
self.angle += dt * 100
if __name__ == '__main__':
RotateRecordApp().run()
Output:
I added a gray background just to improve image visibility.