I tried to make Galaxy project. I am having A Mistake in between!
This I am try through freecodecamp.org kivy full course.
I am beginner to kivy.
See if there is problem in canvas.
I wasn't able to see my drawn lines on kivy window.
Here are My files:
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import *
from kivy.lang import Builder
from kivy.properties import NumericProperty
Builder.load_file('galaxy.kv')
class MainWidget(Widget):
perspective_point_x = NumericProperty(0)
perspective_point_y = NumericProperty(0)
V_NB_LINES = 7
V_NB_SPACING = .1
vertical_lines = []
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
self.init_vertical_lines()
def on_parent(self, widget, parent):
pass
def on_size(self, *args):
self.update_vertical_lines()
def on_perspective_point_x(self, widget, value):
pass
def on_perspective_point_y(self, widget, value):
pass
def init_vertical_lines(self):
with self.canvas:
Color(1, 1, 1)
for i in range(0, self.V_NB_LINES):
self.vertical_lines.append(Line())
def update_vertical_lines(self):
central_line_x = int(self.width/2)
spacing = int(self.V_NB_SPACING * self.width)
offset = -int(self.V_NB_LINES/2)
for i in range(0, self.V_NB_LINES):
line_x = central_line_x + offset*spacing
self.vertical_lines[i].points = [line_x, 0, line_x, self.height]
offset += 1
class GalaxyApp(App):
pass
GalaxyApp().run()
galaxy.kv
<MainWidget>
perspective_point_x: self.width / 2
perspective_point_y: self.height * 0.75
You where missing the build function in GalaxyApp
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import *
from kivy.lang import Builder
from kivy.properties import NumericProperty
Builder.load_file('galaxy.kv')
class MainWidget(Widget):
perspective_point_x = NumericProperty(0)
perspective_point_y = NumericProperty(0)
V_NB_LINES = 7
V_NB_SPACING = .1
vertical_lines = []
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
self.init_vertical_lines()
def on_parent(self, widget, parent):
pass
def on_size(self, *args):
self.update_vertical_lines()
def on_perspective_point_x(self, widget, value):
pass
def on_perspective_point_y(self, widget, value):
pass
def init_vertical_lines(self):
with self.canvas:
Color(1, 1, 1)
for i in range(0, self.V_NB_LINES):
self.vertical_lines.append(Line())
def update_vertical_lines(self):
central_line_x = int(self.width/2)
spacing = int(self.V_NB_SPACING * self.width)
offset = -int(self.V_NB_LINES/2)
for i in range(0, self.V_NB_LINES):
line_x = central_line_x + offset*spacing
with self.canvas:
self.vertical_lines[i].points = [line_x, 0, line_x, self.height]
offset += 1
class GalaxyApp(App):
def build(self):
return MainWidget()
GalaxyApp().run()
Related
I have a music player app that reads the cover of songs and displays them on the screen. For this I created my own class ByteFitImage for the image to read bytes and for it to adapt to different sizes.
The problem I am encountering is that if I decide to change the data, I get a key error:
KeyError: 'source'
And obviously this line is causing the problem:
self.kv.ids.img.data = BytesIO(second_img.read())
Why is it asking for source? My Code:
from io import BytesIO
from kivy.clock import Clock
from kivy.core.image import Image
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.lang import Builder
from kivy.properties import BooleanProperty, ObjectProperty, VariableListProperty, Property
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.app import App
class ByteFitImage(BoxLayout):
data = Property(None)
radius = VariableListProperty([0], length=4)
mipmap = BooleanProperty(False)
_container = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_once(self._late_init)
def _late_init(self, *args):
self._container = Container(self.data)
self.bind(data=self._container.setter("source"))
self.add_widget(self._container)
def reload(self):
self._container.image.reload()
class Container(Widget):
# source = ObjectProperty()
image = ObjectProperty()
def __init__(self, source, **kwargs):
super().__init__(**kwargs)
# self.image = AsyncImage(mipmap=mipmap)
self.bind(size=self.adjust_size, pos=self.adjust_size)
self.image = Image(source, ext="jpg")
self.image.bind(on_load=self.adjust_size)
# self.source = source
def on_source(self, instance, value):
if isinstance(value, str):
self.image.source = value
else:
self.image.texture = value
self.adjust_size()
def adjust_size(self, *args):
if not self.parent or not self.image.texture:
return
(par_x, par_y) = self.parent.size
if par_x == 0 or par_y == 0:
with self.canvas:
self.canvas.clear()
return
par_scale = par_x / par_y
(img_x, img_y) = self.image.texture.size
img_scale = img_x / img_y
if par_scale > img_scale:
(img_x_new, img_y_new) = (img_x, img_x / par_scale)
else:
(img_x_new, img_y_new) = (img_y * par_scale, img_y)
crop_pos_x = (img_x - img_x_new) / 2
crop_pos_y = (img_y - img_y_new) / 2
subtexture = self.image.texture.get_region(
crop_pos_x, crop_pos_y, img_x_new, img_y_new
)
with self.canvas:
self.canvas.clear()
Color(1, 1, 1)
Rectangle(texture=subtexture, pos=self.pos, size=(par_x, par_y))
class Example(App):
def __init__(self, **kwargs):
with open("images/song_img.jpg", "rb") as song_img:
self.song_img_bytes = BytesIO(song_img.read())
super(Example, self).__init__(**kwargs)
self.kv = Builder.load_string('''
#:kivy 2.0.0
BoxLayout:
orientation: "vertical"
padding: 10
ByteFitImage:
id: img
data: app.song_img_bytes
Button:
text: "Change cover!"
font_size: 32
on_release: app.change_cover()''')
def build(self):
return self.kv
def change_cover(self):
with open("images/second_img.jpg", "rb") as second_img:
self.kv.ids.img.data = BytesIO(second_img.read())
if __name__ == '__main__':
Example().run()
How to resolve widget overlapping in kivy framework, I am adding widgets in for cycle so I have more than one widget but all of them comes to be in one place, how can I prevent this?
My python code:
from kivy.app import App
from kivymd.app import MDApp
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.graphics import Color
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivymd.uix.card import MDCard
from kivy.properties import StringProperty
from kivy.lang import Builder
import requests, time, collections
class request_sender():
return 'DATA'
class CustomLabel(Label):
pass
class CustomBox(BoxLayout):
pass
class AuctionCard(Widget):
auc_timer = ''
auc_img = ''
auc_name = ''
def __init__(self, **kwargs):
super(AuctionCard, self).__init__(**kwargs)
with self.canvas.before: Color(1, 0, .4, mode='rgb')
Clock.schedule_once(self.load_info)
def load_info(self, dt):
print(self.auc_name)
self.size_hint = None, None
box = BoxLayout(orientation='vertical', size = (800, 600))
box.size_hint_x = 50;
box.height = 100
AuctionName = CustomLabel(text=self.auc_name, pos_hint={'top': 300.9})
AuctionImage = CustomLabel(text=self.auc_img)
AuctionTimer = CustomLabel(text=self.auc_name)
box.add_widget(AuctionName)
box.add_widget(AuctionTimer)
box.add_widget(AuctionImage)
self.add_widget(box)
class MyWidget(Widget):
prop = StringProperty('')
array_of_labels = []
prop1 = StringProperty('Second card')
n = 0
all_cards = collections.defaultdict()
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.screen_load()
def timer_le(self, dt):
returned_data = request_sender().sender_update('ajax.php')
for key in self.all_cards:
for data in returned_data:
if data['pid'] == key:
self.all_cards[key][0].text = str(data['remaining_time'])
def screen_load(self):
returned_data = request_sender().sender_update('ajax.php')
box = GridLayout(cols=2)
self.size_hint = None, None
for data in returned_data:
AucCard = AuctionCard()
AucCard.auc_name = str(data['auc_name'])+'\n\n'
AucCard.auc_timer = str(data['remaining_time'])+'\n\n'
AucCard.auc_img = str(data['auc_img'])+'\n\n'
box.add_widget(AucCard)
print('Widget added')
self.add_widget(box)
#self.all_cards[str(data['pid'])] = [AucCard]
#Clock.schedule_interval(self.timer_le, 1/30)
class TestApp(App):
def build(self):
box = GridLayout(cols=2)
return MyWidget()
TestApp().run()
My kv code:
<CustomLabel>:
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
Result:
Kind of result that I want to create:
Actually I found easy way to solve this issue. The problem was in parent class of my AuctionCard and MyWidget classes, I set parent class to Widget but for the AuctionCard it should be BoxLayout and for MyWidget GridLayout. So from there I managed to set cols = 2 and size to window.size. From here it works exactly how it should work.
from kivy.lang import Builder
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.gridlayout import GridLayout
class AuctionCard(BoxLayout):
auc_timer = ''
auc_img = ''
auc_name = ''
def __init__(self, **kwargs):
super(AuctionCard, self).__init__(**kwargs)
Clock.schedule_once(self.load_info)
def load_info(self, dt):
self.orientation = 'vertical'
AuctionName = Label(text=self.auc_name)
AuctionImage = Label(text=self.auc_img)
AuctionTimer = Label(text=self.auc_timer)
self.add_widget(AuctionName)
self.add_widget(AuctionTimer)
self.add_widget(AuctionImage)
class MyWidget(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.size = (Window.size[0], self.size[1])
self.load_app()
print('MyWidget size: '+str(self.size))
def load_app(self):
self.size_hint = None, None
returned_data = [{'auc_name':'name 1', 'remaining_time':'100', 'auc_img':'https://img.src'},
{'auc_name':'name 2', 'remaining_time':'200', 'auc_img':'https://img.src'},
{'auc_name':'name 3', 'remaining_time':'300', 'auc_img':'https://img.src'}]
for data in returned_data:
AucCard = AuctionCard()
AucCard.auc_name = str(data['auc_name'])+'\n\n'
AucCard.auc_timer = str(data['remaining_time'])+'\n\n'
AucCard.auc_img = str(data['auc_img'])+'\n\n'
self.add_widget(AucCard)
print('Widget added')
class MyTestApp(App):
def __init__(self, **kwargs):
self.title = "My Material Application"
super().__init__(**kwargs)
def build(self):
return MyWidget()
if __name__ == "__main__":
MyTestApp().run()
Result:
Result
In the code below I have a second screen, and I tried to add two buttons to this screen, but one button is on top of the other, I can see it when I use label instead.
I need to add each Label on a row or column because at the moment I cant even read the Label text.
This is how I add the label to new screen:
self.new_screen.add_widget(Label(text='LABEL NUMER 1'))
self.new_screen.add_widget(Label(text='THIS IS THE LABEL NUMBER 2'))
full code:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
class Grid(GridLayout):
def __init__(self, **kwargs):
super(Grid, self).__init__(**kwargs)
self.rows= 3
self.titlet = Label(text='MAIN SCREEN')
self.add_widget(self.titlet)
self.MainGrid = GridLayout()
self.MainGrid.cols = 2
self.b4 = Button(text="Botao")
self.MainGrid.add_widget(self.b4)
self.add_widget(self.MainGrid)
class MyScreenManager(ScreenManager):
def __init__(self, **kwargs):
super(MyScreenManager, self).__init__(**kwargs)
self.main_screen = Screen(name="main_screen")
self.new_screen = Screen(name="new_screen")
self.add_widget(self.main_screen)
self.add_widget(self.new_screen)
self.grid = Grid()
self.grid.b4.bind(on_press=self.change_screen)
self.new_screen.cols = 2
self.new_screen.add_widget(Label(text='LABEL NUMER 1'))
self.new_screen.add_widget(Label(text='THIS IS THE LABEL NUMBER 2'))
self.main_screen
self.main_screen.add_widget(self.grid)
def change_screen(self, *args):
self.current = "new_screen"
class MyApp(App):
def build(self):
return MyScreenManager()
if __name__ == "__main__":
MyApp().run()
In the same way that you have established a layout on the first Screen you must do it with the second Screen, in this case you must use BoxLayout:
from kivy.uix.boxlayout import BoxLayout
# ...
class MyScreenManager(ScreenManager):
def __init__(self, **kwargs):
super(MyScreenManager, self).__init__(**kwargs)
self.main_screen = Screen(name="main_screen")
self.new_screen = Screen(name="new_screen")
self.add_widget(self.main_screen)
self.add_widget(self.new_screen)
self.grid = Grid()
self.grid.b4.bind(on_press=self.change_screen)
box_layout = BoxLayout(orientation="vertical")
self.new_screen.add_widget(box_layout)
box_layout.add_widget(Label(text='LABEL NUMER 1'))
box_layout.add_widget(Label(text='THIS IS THE LABEL NUMBER 2'))
self.main_screen.add_widget(self.grid)
# ...
I'm trying to translate the beginnings of a simple canvas app I wrote in JavaScript to the Kivy framework. I have been able to distribute vertices along the perimeter of a circle, but I have been unsuccessful in registering click events on each vertex whether attempted in Python or Kv language. A nice start might be changing the size of any vertex clicked. Any tips, feedback, solutions welcome.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.graphics import *
from kivy.properties import NumericProperty
from random import randint
import math
class Vertex(Widget):
def __init__(self, position=(50,50), **kwargs):
super(Vertex, self).__init__(**kwargs)
self.position = position
self.size = (10,10)
def draw(self):
with self.canvas:
Color(1., 0, 0)
Ellipse(pos=self.position, size=self.size)
class ChromaticCircle(Widget):
vertices = []
def __init__(self, radius=100, **kwargs):
super(ChromaticCircle, self).__init__(**kwargs)
self.radius = radius
self.draw()
def draw(self):
interval = (math.pi * 2) / 12
with self.canvas:
Color(1., 0, 0)
for i in range(1, 13):
angle = (math.radians(360) / 12) * (i + 9)
position = ((self.center_x + 200) + (self.radius*math.cos(angle)), (self.center_y + 200)+(self.radius)*math.sin(angle))
self.vertices.append(Vertex(position))
for j in range(len(self.vertices)):
self.vertices[j].draw()
class MyApp(App):
def build(self):
return ChromaticCircle()
if __name__ == '__main__':
MyApp().run()
You can capture clicks on your Vertex widget by using the collide_point method in the on_touch_down method to check if a Touch event occurs within a Vertex:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.graphics import *
from kivy.properties import NumericProperty
from random import randint
import math
class Vertex(Widget):
def __init__(self, position=(50,50), **kwargs):
super(Vertex, self).__init__(**kwargs)
self.position = position
self.size = (10,10)
self.pos = position # need to set the `pos` as `collide_point` uses it
def draw(self):
with self.canvas:
Color(1., 0, 0)
Ellipse(pos=self.position, size=self.size)
class ChromaticCircle(Widget):
vertices = []
def __init__(self, radius=100, **kwargs):
super(ChromaticCircle, self).__init__(**kwargs)
self.radius = radius
self.draw()
def on_touch_down(self, touch):
# check if the touch is on a Vertex
for vert in self.vertices:
if vert.collide_point(touch.x, touch.y):
print('click on vertex: ' + str(vert.pos))
return True
return super(ChromaticCircle, self).on_touch_down(touch)
def draw(self):
interval = (math.pi * 2) / 12
with self.canvas:
Color(1., 0, 0)
for i in range(1, 13):
angle = (math.radians(360) / 12) * (i + 9)
position = ((self.center_x + 200) + (self.radius*math.cos(angle)), (self.center_y + 200)+(self.radius)*math.sin(angle))
print('adding vertex at ' + str(position))
self.vertices.append(Vertex(position))
for j in range(len(self.vertices)):
self.vertices[j].draw()
class MyApp(App):
def build(self):
return ChromaticCircle()
if __name__ == '__main__':
MyApp().run()
There are some additional details that you may want to consider. The pos of the Ellipse is the lower left corner, so your vertex position is not at the center of the drawn Ellipse.
I need to create an app that works on a VR glasses, as in this example below:
But I have two problems:
I can not make two simultaneous accesses to the camera and create two widgets.
I can not make the widgets created in kv file (in this case a label) stay on the camera, to write information about the view.
Any way to create this dual camera widget and write it over?
my code:
main:
__version__ = '1.0'
from kivy.lang import Builder
from kivy.app import App
from kivy.properties import ObjectProperty, ListProperty, BooleanProperty, \
NumericProperty
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from jnius import autoclass, PythonJavaClass, java_method, cast
from android.runnable import run_on_ui_thread
PythonActivity = autoclass('org.renpy.android.PythonActivity')
Camera = autoclass('android.hardware.Camera')
SurfaceView = autoclass('android.view.SurfaceView')
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
class PreviewCallback(PythonJavaClass):
__javainterfaces__ = ('android.hardware.Camera$PreviewCallback', )
def __init__(self, callback):
super(PreviewCallback, self).__init__()
self.callback = callback
class SurfaceHolderCallback(PythonJavaClass):
__javainterfaces__ = ('android.view.SurfaceHolder$Callback', )
def __init__(self, callback):
super(SurfaceHolderCallback, self).__init__()
self.callback = callback
#java_method('(Landroid/view/SurfaceHolder;III)V')
def surfaceChanged(self, surface, fmt, width, height):
self.callback(fmt, width, height)
class AndroidWidgetHolder(Widget):
view = ObjectProperty(allownone=True)
def __init__(self, **kwargs):
self._old_view = None
from kivy.core.window import Window
self._window = Window
kwargs['size_hint'] = (None, None)
super(AndroidWidgetHolder, self).__init__(**kwargs)
def on_view(self, instance, view):
activity = PythonActivity.mActivity
activity.addContentView(view, LayoutParams(*self.size))
view.setZOrderOnTop(True)
view.setX(self.x)
view.setY(self._window.height - self.y - self.height)
class AndroidCamera(Widget):
index = NumericProperty(0)
__events__ = ('on_preview_frame', )
def __init__(self, **kwargs):
self._holder = None
self._android_camera = None
super(AndroidCamera, self).__init__(**kwargs)
self._holder = AndroidWidgetHolder(size=self.size, pos=self.pos)
self.add_widget(self._holder)
self.start()
#run_on_ui_thread
def stop(self):
if self._android_camera is None:
return
self._android_camera.setPreviewCallback(None)
self._android_camera.release()
self._android_camera = None
self._holder.view = None
#run_on_ui_thread
def start(self):
if self._android_camera is not None:
return
self._android_camera = Camera.open(self.index)
self._android_surface = SurfaceView(PythonActivity.mActivity)
surface_holder = self._android_surface.getHolder()
self._android_surface_cb = SurfaceHolderCallback(self._on_surface_changed)
surface_holder.addCallback(self._android_surface_cb)
self._holder.view = self._android_surface
self._holder2.view = self._android_surface
def _on_surface_changed(self, fmt, width, height):
params = self._android_camera.getParameters()
params.setPreviewSize(width, height)
self._android_camera.setParameters(params)
self._previewCallback = PreviewCallback(self._on_preview_frame)
self._android_camera.setPreviewCallbackWithBuffer(self._previewCallback);
self._android_camera.setPreviewDisplay(self._android_surface.getHolder())
self._android_camera.startPreview();
def _on_preview_frame(self, camera, data):
self.dispatch('on_preview_frame', camera, data)
self._android_camera.addCallbackBuffer(data)
def on_preview_frame(self, camera, data):
pass
def on_size(self, instance, size):
if self._holder:
self._holder.size = size
def on_pos(self, instance, pos):
if self._holder:
self._holder.pos = pos
class vrtest(App, BoxLayout):
def build(self):
return Builder.load_file('qrcode.kv')
camera_size = ListProperty([640, 480])
xpos = NumericProperty(-190)
ypos = 25
def __init__(self, **kwargs):
super(vrtest, self).__init__(**kwargs)
self._camera = AndroidCamera(
size=self.camera_size,
size_hint=(None, None),
pos=(self.xpos, self.ypos))
self.add_widget(self._camera)
def start(self):
self._camera.start()
if __name__ == '__main__':
vrtest().run()
kv:
FloatLayout:
Label:
text: 'Label Test12345678987654321'
pos_hint:{'x':0, 'y':0}