Black lines in Kivy.garden.progressspinner - python

source
It is ok when I just use color like green. But when I use this with custom image for gradation effect, it seems it always shows black lines inside no matter what kind of image I put in.
I have not touched any code. Only thing I've done was changing the source of the image. Is there anyway to get rid of that blurry black lines? Why do they appear?
'''
ProgressSpinner
===============
Android Lollipop style progress spinner.
'''
from kivy.lang import Builder
from kivy.core.image import Image as CoreImage
from kivy.properties import NumericProperty, ListProperty, BoundedNumericProperty, StringProperty, ObjectProperty
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import BooleanProperty
from kivy.uix.widget import Widget
Builder.load_string('''
<ProgressSpinnerBase>:
_size: min(self.height, self.width)
_rsize: self._size / 2.
_stroke: max(0.1, self._rsize / 20. if self.stroke_width is None else self.stroke_width)
_radius: self._rsize - self._stroke * 2.
<ProgressSpinner>:
canvas:
Color:
rgba: self.color
Line:
circle:
(self.center_x, self.center_y, self._radius,
self._angle_center + self._angle_start,
self._angle_center + self._angle_end)
width: self._stroke
cap: 'none'
<TextureProgressSpinner>:
canvas:
StencilPush
Color:
rgba: 1, 1, 1, 1
Line:
circle:
(self.center_x, self.center_y, self._radius,
self._angle_center + self._angle_start,
self._angle_center + self._angle_end)
width: self._stroke
cap: 'none'
StencilUse
Color:
rgba: self.color
Rectangle:
pos: self.center_x - self._rsize, self.center_y - self._rsize
size: self._size, self._size
texture: self.texture
StencilUnUse
Color:
rgba: 1, 1, 1, 1
Line:
circle:
(self.center_x, self.center_y, self._radius,
self._angle_center + self._angle_start,
self._angle_center + self._angle_end)
width: self._stroke
cap: 'none'
StencilPop
<RotatingTextureProgressSpinner>:
canvas:
PushMatrix
Rotate:
angle: -self._angle_center
origin: self.center
StencilPush
Color:
rgba: 1, 1, 1, 1
Line:
circle:
(self.center_x, self.center_y, self._radius,
self._angle_start, self._angle_end)
width: self._stroke
cap: 'none'
StencilUse
Color:
rgba: self.color
Rectangle:
pos: self.center_x - self._rsize, self.center_y - self._rsize
size: self._size, self._size
texture: self.texture
StencilUnUse
Color:
rgba: 1, 1, 1, 1
Line:
circle:
(self.center_x, self.center_y, self._radius,
self._angle_start, self._angle_end)
width: self._stroke
cap: 'none'
StencilPop
PopMatrix
''')
class ProgressSpinnerBase(Widget):
'''ProgressSpinnerBase - base class for progress spinner widgets
'''
color = ListProperty([1, 1, 1, 1])
'''Color to render the spinner.
:attr:`color` is a :class:`~kivy.properties.ListProperty` and defaults
to [1, 1, 1, 1] (white, full opacity).
'''
speed = BoundedNumericProperty(1, min=0.1)
'''Speed coefficient of the spinner. This value is multiplied by the
base speed of 90 degrees per second.
:attr:`speed` is a :class:`~kivy.properties.BoundedNumericProperty` and
defaults to 1.
'''
stroke_length = BoundedNumericProperty(25., min=1, max=180)
'''Base length of the stroke in degrees.
:attr:`stroke_length` is a :class:`~kivy.properties.BoundedNumericProperty`
and defaults to 25.
'''
stroke_width = NumericProperty(None, allownone=True)
'''Width of the stroke in pixels. If set to None, the width will be
calculated automatically as 1/20th of the radius.
:attr:`stroke_width` is a :class:`~kivy.properties.NumericProperty` and
defaults to None.
'''
auto_start = BooleanProperty(True)
'''Whether to automatically start spinning.
:attr:`auto_start` is a :class:`~kivy.properties.BooleanProperty` and
defaults to True.
'''
# internal properties
_angle_center = NumericProperty(0)
_angle_start = NumericProperty()
_angle_end = NumericProperty()
_size = NumericProperty()
_rsize = NumericProperty()
_stroke = NumericProperty(1)
_radius = NumericProperty(50)
def __init__(self, **kwargs):
super(ProgressSpinnerBase, self).__init__(**kwargs)
self._state = 'wait1'
self._next = None
self._spinning = False
if self.auto_start:
self.start_spinning()
def start_spinning(self, *args):
'''Start spinning the progress spinner. Ignores all positional args
for easy binding.
'''
if not self._spinning:
self._state = 'wait1'
self._next = None
self._angle_center = 0.
self._angle_start = 360.
self._angle_end = 360. + self.stroke_length
Clock.schedule_interval(self._update, 0)
Clock.schedule_once(self._rotate, 0.3)
self._spinning = True
def stop_spinning(self, *args):
'''Stop spinning the progress spinner. Ignores all positional args
for easy binding.
If you intend to keep the spinner around, you should stop it when
not using it and restart it when needed again.
'''
if self._spinning:
if self._next:
if isinstance(self._next, Animation):
self._next.cancel(self)
else:
self._next.cancel()
Clock.unschedule(self._update)
Clock.unschedule(self._rotate)
self._angle_start = self._angle_end = 0
self._spinning = False
def _update(self, dt):
angle_speed = 90. * self.speed
self._angle_center += dt * angle_speed
if self._angle_center > 360:
self._angle_center -= 360.
def _rotate(self, *args):
if not self._spinning:
return
rotate_speed = 0.6 / self.speed
wait_speed = 0.3 / self.speed
if self._state == 'wait1':
self._state = 'rotate1'
self._next = Animation(_angle_end=self._angle_start + 360. - self.stroke_length, d=rotate_speed,
t='in_quad')
self._next.bind(on_complete=self._rotate)
self._next.start(self)
elif self._state == 'rotate1':
self._state = 'wait2'
self._next = Clock.schedule_once(self._rotate, wait_speed)
elif self._state == 'wait2':
self._state = 'rotate2'
self._next = Animation(_angle_start=self._angle_end - self.stroke_length, d=rotate_speed, t='in_quad')
self._next.bind(on_complete=self._rotate)
self._next.start(self)
elif self._state == 'rotate2':
self._state = 'wait1'
self._next = Clock.schedule_once(self._rotate, wait_speed)
while self._angle_end > 720.:
self._angle_start -= 360.
self._angle_end -= 360.
class ProgressSpinner(ProgressSpinnerBase):
'''ProgressSpinner class. Android Lollipop style progress spinner.
'''
pass
class TextureProgressSpinnerBase(ProgressSpinnerBase):
texture = ObjectProperty()
'''Texture to render for the spinner.
:attr:`texture` is a :class:`~kivy.properties.ObjectProperty` and
defaults to None.
'''
source = StringProperty('')
'''Source image to render for the spinner.
:attr:`source` is a :class:`~kivy.properties.StringProperty` and
defaults to an empty string.
'''
def on_source(self, inst, value):
if value:
self.texture = CoreImage(value).texture
class TextureProgressSpinner(TextureProgressSpinnerBase):
'''TextureProgressSpinner class.
Same as ProgressSpinner, but with a texture/image instead of a solid color.
'''
pass
class RotatingTextureProgressSpinner(TextureProgressSpinnerBase):
'''RotatingTextureProgressSpinner class.
Same as TextureProgressSpinner, but the texture/image rotates along with
the spinner.
'''
pass
if __name__ == '__main__':
from kivy.app import App
from kivy.graphics.texture import Texture
from textwrap import dedent
class TestApp(App):
texture = ObjectProperty()
def blittex(self, *args):
rgbpixels = [(x, 0, y, 255) for x in range(256) for y in range(256)]
pixels = b''.join((b''.join(map(bytearray, pix)) for pix in rgbpixels))
self.texture = Texture.create(size=(256, 256))
self.texture.blit_buffer(pixels, colorfmt='rgba', bufferfmt='ubyte')
def build(self):
Clock.schedule_once(self.blittex, -1)
return Builder.load_string(dedent('''\
<ProgressSpinnerBase>:
on_touch_down: self.stop_spinning() if self._spinning else self.start_spinning()
<TTextureProgressSpinner#TextureProgressSpinner>:
texture: app.texture
<TRotatingTextureProgressSpinner#RotatingTextureProgressSpinner>:
texture: app.texture
<ITextureProgressSpinner#TextureProgressSpinner>:
source: 'demoimage.jpg'
<IRotatingTextureProgressSpinner#RotatingTextureProgressSpinner>:
source: 'demoimage.jpg'
BoxLayout:
BoxLayout:
orientation: 'vertical'
ProgressSpinner
TTextureProgressSpinner
TRotatingTextureProgressSpinner
BoxLayout:
orientation: 'vertical'
BoxLayout:
ProgressSpinner:
color: 0.3, 0.3, 1, 1
stroke_width: 1
ProgressSpinner:
speed: 0.5
color: 1, 0, 0, 1
ProgressSpinner:
speed: 2
color: 0, 1, 0, 1
BoxLayout:
TTextureProgressSpinner:
color: 1, 0, 0, 1
ITextureProgressSpinner:
stroke_width: 10
ITextureProgressSpinner:
stroke_length: 20
BoxLayout:
TRotatingTextureProgressSpinner:
color: 1, 0, 0, 1
IRotatingTextureProgressSpinner:
stroke_width: 10
IRotatingTextureProgressSpinner:
stroke_length: 20
'''))
TestApp().run()
The code above is the code from progressspinner.py

Related

Any module that can be used to connect Pandas with kivy?

I want to display the data that is contained inside my 'data.cls' file into kivy. I have used this code
class Show(Screen):
def __init__(self,**kwargs):
super(Show,self).__init__(**kwargs)
with self.canvas:
Color(.62,.91,.968,1, mode='rgba')
Rectangle(pos=(0, 0), size=(2000, 2000))
Color(.83,1,1,1,mode='rgba')
Rectangle(pos=(0,140),size=(2000,1000))
df = str((pd.read_csv('data.csv')))
self.add_widget(Label(text=df, color=(0, 0, 0, 1)))
However, the result is kinda messy:
Is there any way to 'tidy up' the result? Should I use any module to get better result?
Thankss
I am not aware of any such module, but you can build your own display using GridLayout. Here is a sample code that I have used (with some small modifications for your situation):
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen
import pandas as pd
class CellLabel(Label):
pass
class Show(Screen):
def __init__(self,**kwargs):
super(Show,self).__init__(**kwargs)
Clock.schedule_once(self.fill)
def fill(self, dt):
# fill the GridLayout with CellLabels
df = pd.read_csv('data.csv')
grid = self.ids.grid
grid.cols = df.shape[1]
for row in df.values:
for col in row:
txt = str(col).strip()
if txt == 'nan':
txt = ''
grid.add_widget(CellLabel(text=txt))
Clock.schedule_once(self.do_centering)
def do_centering(self, dt):
# center the text in each CellLabel
# start by getting the max width of each column
grid = self.ids.grid
reversed = grid.children[:]
reversed.reverse()
max_col_widths = [0] * grid.cols
col = 0
for cell in reversed:
if cell.width > max_col_widths[col]:
max_col_widths[col] = cell.width
col += 1
col = col % grid.cols
# use those max widths to center the text in each CellLabel
col = 0
for cell in reversed:
cell.width = max_col_widths[col]
cell.halign = 'center'
cell.text_size = cell.size
col += 1
col = col % grid.cols
kv = '''
<CellLabel>:
size_hint: None, None
size: self.texture_size
color: 0,0,0,1
<Show>:
canvas:
Color:
rgba: 0.62, 0.91, 0.968, 1
Rectangle:
pos: self.pos
size: self.size
ScrollView:
GridLayout:
id: grid
padding: 5
spacing: 5
size_hint: None, None
size: self.minimum_size
'''
class TestApp(App):
def build(self):
Builder.load_string(kv)
return Show()
TestApp().run()

Display an image partially using stencilview

I have asked a question about making a progress bar using stencilview here
And I could make one successfully.
Now I am trying to make a partially displaying image display as below
First showing background image.
Progressively display certain part of filled image.
For example, fill up the arm then fill up the leg.
In this case, I am trying to fill up depending on which arm and leg parameter is inserted.
I am thinking maybe I should device the images as 5 parts and have stencilview for each of them, but I am asking if there is a way of doing it without putting 5 different images and stencilviews.
Here is what I've currently done for a single progress image.
from kivy.app import App
from kivy.properties import StringProperty, NumericProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.lang.builder import Builder
from kivy.clock import Clock
class CLS_PROGRESS_IMAGE(FloatLayout):
background = StringProperty(None)
progress_image = StringProperty(None)
max = NumericProperty(0.0)
def __init__(self, font_size=20, **kwargs):
super(CLS_PROGRESS_IMAGE, self).__init__(**kwargs)
self.progress_event = None
self.font_size = font_size
self.firstDraw = True
self.pbi = None
self.rect = None
self.bgi = None
self.value = 0.0
self.value_normalized = 0.0
self.horizontal = False
self.looping = True
self.progress_event = Clock.schedule_interval(self._progress, 0.1)
def draw(self):
if self.firstDraw:
self.ids.bgi.source=self.background
self.ids.pbi.source = self.progress_image
self.ids.lab.font_size = self.font_size
self.ids.lab.text = str(int(self.value_normalized*100)) + "%"
self.ids.sten.pos = self.pos
if self.horizontal is True:
self.ids.sten.width = self.size[0]*self.value_normalized
self.ids.sten.height = self.size[1]
else:
self.ids.sten.height= self.size[1]*self.value_normalized
self.ids.sten.width = self.size[0]
self.firstDraw = False
else:
self.ids.sten.pos = self.pos
self.ids.lab.text = str(int(self.value_normalized*100)) + "%"
if self.horizontal is True:
self.ids.sten.width = self.size[0]*self.value_normalized
else:
self.ids.sten.height= self.size[1]*self.value_normalized
def set_value(self, value):
self.value = value
self.value_normalized = self.value / self.max
self.draw()
def _progress(self, dt):
if self.value < self.max - 1:
self.set_value(self.value + 1)
else:
if self.looping:
self.value = 0
self.set_value(self.value)
else:
self.set_value(self.max)
self.progress_event.cancel()
# Demo
class DemoApp(App):
def build(self):
kv = Builder.load_string('''
<CLS_PROGRESS_IMAGE>:
Image:
id: bgi
allow_stretch: True
keep_ratio: False
StencilView:
id: sten
horizontal: False
size_hint: (None, None)
size: (0, root.height) if self.horizontal else (root.width, 0)
Image:
id: pbi
allow_stretch: True
keep_ratio: False
size_hint: (None, None)
size: (root.width, root.height)
pos: (root.pos[0], root.pos[1])
Label:
id: lab
text: '0%'
size_hint: (None, None)
size: self.texture_size
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
CLS_PROGRESS_IMAGE:
size_hint: (None, None)
height: 100
width: 500
max: 100
background: '../images/empty.png'
progress_image: '../images/filled.png'
''')
return kv
if __name__ == '__main__':
DemoApp().run()
Though this is not "partially" progressing an image,
but I could successfully emulate this by just using multiple different images.
Basically, what I've done was progressing images that look like they are partially loaded
and continue the same progressing until I use up all the images I put in a list.
class CLS_PROGRESS_BODY(CLS_PROGRESS_IMAGE):
def __init__(self, **kwargs):
super(CLS_PROGRESS_BODY, self).__init__(**kwargs)
self.percentage_show = False
self.image_list = None
self.apply_adjustment = True
self.progress_event.cancel()
self.limitation = 0
def start_progress(self):
self.firstDraw = True
self.progress_event.cancel()
self.background= '../images/progress0.png'
self.progress_image= '../images/progress1.png'
self.image_list= ['../images/progress2.png', '../images/progress3.png', '../images/progress4.png',
'../images/progress5.png']
if self.apply_adjustment:
self.adjust_progress_position(self.progress_image)
else:
self.set_value(0)
self.limitation = self.max * 0.8
self.progress_event = Clock.schedule_interval(partial(self._progress, self.limitation), 0.1)
def set_image_list(self, list_of_path):
self.image_list = list_of_path
def _progress(self, limit, dt):
if self.value < self.max - 1 and self.value < limit:
self.set_value(self.value + 1)
else:
self.set_value(self.max)
self.progress_event.cancel()
self.load_next_image()
def load_next_image(self):
if self.image_list:
self.ids.bgi.source = self.ids.pbi.source
self.ids.pbi.source = self.image_list.pop(0)
if self.apply_adjustment:
self.adjust_progress_position(self.ids.pbi.source)
else:
self.set_value(0)
self.progress_event = Clock.schedule_interval(partial(self._progress, self.limitation), 0.1)
else:
if self.looping:
self.start_progress()

Kivy - duplicate class instance on super()

Immediately after the super() function is called, it creates a duplicate WidgetClass instance.
My understanding of the super() I've used is that it refers to the EditImageLayout class to inherit from. To this end I've tried to implement different variations of the super() but admittedly I'm only guessing at this stage.
Updated to full working, I've cut out a few hundred lines
Run as > OK > Select an Image > Open > Crop (dual instances start running here)
App_R3App.py
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.graphics import Line, Color
from kivy.properties import NumericProperty, ObjectProperty, StringProperty, ListProperty
from kivy.uix.image import Image
import io
from kivy.core.image import Image as CoreImageKivy
from kivy.uix.bubble import Bubble
from kivy.core.window import Window
__version__ = '0.1'
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
def hl(self, image_address):
self.new_image_address = image_address # for the sake of this
self.callback_image(self.new_image_address, image_address, "Auto-cropped image")
def callback_image(self, new_image_address_tmp, image_address_tmp, title):
if new_image_address_tmp:
third_screen = self.manager.get_screen("_third_screen_")
new_image_address_tmp = [k.replace("\\", "/") for k in new_image_address_tmp]
third_screen.callback_image(new_image_address_tmp[0], image_address_tmp[0], title)
class ThirdScreen(Screen, BoxLayout):
# class attribute references
image_size = (0, 0)
image_pos = (0, 0)
image_address = ""
new_image_address = ""
title = "Upload"
rect_box = ObjectProperty(None)
t_x = NumericProperty(0.0)
t_y = NumericProperty(0.0)
x1 = y1 = x2 = y2 = NumericProperty(0.0)
def __init__(self, **kwargs):
super(ThirdScreen, self).__init__(**kwargs)
pass
def callback_image(self, new_image_address, image_address, title):
sm.current = "_third_screen_"
self.new_image_address = new_image_address
self.image_address = image_address
self.title = title
self.ids.main_image.source = self.new_image_address
self.ids.main_title.text = self.title
def enable_cropping(self):
# overwrite class attributes
ThirdScreen.image_address = self.image_address
ThirdScreen.new_image_address = self.new_image_address
print("enable_cropping")
sm.current = "_edit_image_screen_"
return True
class EditImageScreen(Screen):
def __init__(self, **kwargs):
print("EditImageScreen")
super(EditImageScreen, self).__init__(**kwargs)
self.layout = None
def on_pre_enter(self):
print("on_pre_enter")
self.layout = EditImageLayout()
self.add_widget(self.layout)
class EditImageLayout(FloatLayout):
color_button = ListProperty([1, .3, .4, 1])
button_color = ListProperty([0, 0, 0, 1])
rectangle_selector = ObjectProperty()
text_size_rectangle = ObjectProperty()
image_layout = ObjectProperty()
bubble_buttons = ObjectProperty()
bubble_buttons_undo_confirm = ObjectProperty()
def __init__(self, **kwargs):
print("EditImageLayout")
self.sm = kwargs.pop('sm', None)
self.crop_image_screen = kwargs.pop('crop_image_screen', None)
# This is where the problem occurs
super(EditImageLayout, self).__init__(**kwargs)
self.rectangle_selector.bind(size_selected=self.on_change_size_rectangle_selector)
self.rectangle_selector.bind(size_selected_temp=self.update_text_size_rectangle)
self.bind(on_touch_down=self.bubble_buttons.hide)
self.bubble_buttons.resize_button.bind(on_press=self.on_press_resize_button)
self.bubble_buttons_undo_confirm.undo_button.bind(on_press=self.on_press_undo_button)
self.bubble_buttons_undo_confirm.confirm_button.bind(on_press=self.on_press_confirm_button)
def on_change_size_rectangle_selector(self, instance, size_selected):
print("on_change_size_rectangle_selector")
if not self.rectangle_selector.tap_not_draw_a_line():
self.bubble_buttons.show()
else:
self.text_size_rectangle.text = ''
def on_press_resize_button(self, instance):
print("on_press_resize_button")
self.image_layout.resize_image(width=self.rectangle_selector.size_selected[0],
height=self.rectangle_selector.size_selected[1])
self.rectangle_selector.delete_line()
self.text_size_rectangle.text = ''
self.bubble_buttons_undo_confirm.show()
def on_press_undo_button(self, instance):
print("on_press_undo_button")
size = self.image_layout.old_size
self.image_layout.resize_image(width=size[0], height=size[1])
self.bubble_buttons_undo_confirm.hide()
def on_press_confirm_button(self, instance):
print("on_press_confirm_button")
self.bubble_buttons_undo_confirm.hide()
def update_text_size_rectangle(self, instance, size):
print("update_text_size_rectangle")
self.text_size_rectangle.text = str('({0}, {1})'.format(int(size[0]), int(size[1])))
class ImageLayout(Image):
image = ObjectProperty()
path_image = StringProperty('image_tmp.jpg')
path_image_tmp = StringProperty('image_tmp.jpg')
old_size = ListProperty([0, 0])
def __init__(self, **kwargs):
print("ImageLayout")
super(ImageLayout, self).__init__(**kwargs)
self.path_image = ThirdScreen.image_address
self.image = CoreImage(self.path_image,
data=io.BytesIO(open(self.path_image, "rb").read()),
ext=self.path_image[self.path_image.rfind('.') + 1::])
self.source = self.path_image
def resize_image(self, width, height, pos_x=None, pos_y=None):
pos_x, pos_y = abs(Window.width - width)/2 , abs(Window.height - height)/2
self.image.resize(self.path_image,
self.path_image_tmp,
int(width),
int(height))
self.source = self.path_image_tmp
self.pos = pos_x, pos_y
self.old_size = self.size
self.size = width, height
self.reload()
class CoreImage(CoreImageKivy):
def __init__(self, arg, **kwargs):
print("CoreImage")
super(CoreImage, self).__init__(arg, **kwargs)
def resize(self, fname, fname_scaled, width, height):
try:
img = Image.open(fname)
except Exception as e:
print('Exception: ', e)
return
img = img.resize((width, height), Image.ANTIALIAS)
try:
img.save(fname_scaled)
except Exception as e:
print('Exception: ', e)
return
class TouchSelector(Widget):
# Points of Line object
Ax = NumericProperty(0)
Ay = NumericProperty(0)
Bx = NumericProperty(0)
By = NumericProperty(0)
Cx = NumericProperty(0)
Cy = NumericProperty(0)
Dx = NumericProperty(0)
Dy = NumericProperty(0)
# Object line
line = ObjectProperty()
# List of line objects drawn
list_lines_in_image = ListProperty([])
# Size of the selected rectangle
size_selected = ListProperty([0, 0])
# Size previous of the selected rectangle
size_selected_previous = ListProperty([0, 0])
# Size temporary of the selected rectangle
size_selected_temp = ListProperty([0, 0])
# Line Color and width
line_color = ListProperty([0.2, 1, 1, 1])
line_width = NumericProperty(1)
# First tap in TouchSelector
first_tap = True
def __init__(self, *args, **kwargs):
super(TouchSelector, self).__init__(*args, **kwargs)
self.bind(list_lines_in_image=self.remove_old_line)
def on_touch_up(self, touch): # on button up
self.size_selected = abs(self.Cx - self.Dx), abs(self.Cy - self.By)
self.size_selected_previous = self.size_selected
print(self.Dx, self.Dy, self.Cx, self.Cy)
def on_touch_down(self, touch):
with self.canvas:
Color(self.line_color)
# Save initial tap position
self.Ax, self.Ay = self.first_touch_x, self.first_touch_y = touch.x, touch.y
# Initilize positions to save
self.Bx, self.By = 0, 0
self.Cx, self.Cy = 0, 0
self.Dx, self.Dy = 0, 0
# Create initial point with touch x and y postions.
self.line = Line(points=([self.Ax, self.Ay]), width=self.line_width, joint='miter', joint_precision=30)
# Save the created line
self.list_lines_in_image.append(self.line)
print("on_touch_down")
def remove_old_line(self, instance=None, list_lines=None):
if len(self.list_lines_in_image) > 1:
self.delete_line()
def delete_line(self, pos=0):
try:
self.list_lines_in_image.pop(pos).points = []
except:
pass
def on_touch_move(self, touch):
# Assign the position of the touch at the point C
self.Cx, self.Cy = touch.x, touch.y
# There are two known points A (starting point) and C (endpoint)
# Assign the positions x and y known of the points
self.Bx, self.By = self.Cx, self.Ay
self.Dx, self.Dy = self.Ax, self.Cy
# Assign points positions to the last line created
self.line.points = [self.Ax, self.Ay,
self.Bx, self.By,
self.Cx, self.Cy,
self.Dx, self.Dy,
self.Ax, self.Ay]
self.size_selected_temp = abs(self.Cx - self.Dx), abs(self.Cy - self.By)
def tap_not_draw_a_line(self):
return (self.size_selected[0] == 0 and self.size_selected[1] == 0)
class BaseBubbleButtons(Bubble):
def __init__(self, **kwargs):
super(BaseBubbleButtons, self).__init__(**kwargs)
def hide(self, instance=None, value=None):
self.opacity = 0
def show(self, instance=None, value=None):
self.opacity = 1
class BubbleButtons(BaseBubbleButtons):
resize_button = ObjectProperty()
cut_button = ObjectProperty()
rotate_button = ObjectProperty()
class BubbleButtonsUndoConfirm(BaseBubbleButtons):
undo_button = ObjectProperty()
confirm_button = ObjectProperty()
class App_R3App(App):
Builder.load_file('App_R3.kv')
def build(self):
return sm
def on_start(self):
return True
def on_pause(self):
return True
def on_resume(self):
return True
def on_stop(self):
return True
if __name__ == '__main__':
# Create the screen manager
sm = ScreenManager()
sm.add_widget(FirstScreen(name='_first_screen_'))
sm.add_widget(SecondScreen(name='_second_screen_'))
sm.add_widget(ThirdScreen(name='_third_screen_'))
sm.add_widget(EditImageScreen(name='_edit_image_screen_'))
App_R3App().run()
App_R3.kv
#:import Window kivy.core.window.Window
<MyScreenManager>:
FirstScreen:
id: first_screen
SecondScreen:
id: second_screen
ThirdScreen:
id: third_screen
EditImageScreen:
id: edit_image_screen
<FirstScreen>:
name: '_first_screen_'
BoxLayout:
orientation: "horizontal"
Label:
id: first_screen_label
text: "Hi, I'm the home page"
BoxLayout:
orientation: "vertical"
Button:
text: "Okay!"
on_press: root.manager.current = '_second_screen_'
Button:
text: "Cancel!"
on_press: app.stop()
<SecondScreen>:
name: '_second_screen_'
id: file_chooser
BoxLayout:
id: file_chooser_box_layout
orientation: "horizontal"
Button
text: "Open"
on_press: root.hl(file_chooser_list_view.selection)
FileChooserListView:
id: file_chooser_list_view
<ThirdScreen>:
name: '_third_screen_'
id: third_screen
xx1: root.x1
yy1: root.y1
tt_x: root.t_x
tt_y: root.t_y
BoxLayout:
orientation: "vertical"
id: third_screen_boxlayout
Label:
id: main_title
text: root.title
size_hint: (1, 0.1)
BoxLayout:
id: image_box_layout
# limits the box layout to the position of the image
Image:
id: main_image
source: root.image_address
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
BoxLayout:
id: button_boxlayout
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
id: accept_button
text: "Okay"
size_hint: (0.33, 1)
on_press: root.image_accepted_by_user(root.image_address)
Button:
id: crop_button
text: "Crop"
size_hint: (0.33, 1)
on_press: root.enable_cropping()
Button:
id: cancel_button
text: "Cancel"
size_hint: (0.33, 1)
on_press: root.manager.current = '_first_screen_'
<EditImageLayout>:
rectangle_selector: rectangle_selector
text_size_rectangle: text_size_rectangle
image_layout: image_layout
bubble_buttons: bubble_buttons
bubble_buttons_undo_confirm: bubble_buttons_undo_confirm
canvas.before:
Color:
rgba: (1, 1, 1, 0.2)
Rectangle:
pos: self.pos
size: Window.width, Window.height
Label:
id: text_size_rectangle
pos_hint_x: None
pos_hint_y: None
pos: Window.width*.45, Window.height*.45
color: (1, 1, 1, 1)
ImageLayout:
id: image_layout
size: Window.width*.8, Window.height*.8
pos: Window.width*.1, Window.height*.1
size_hint: None, None
pos_hint_x: None
pos_hint_y: None
TouchSelector:
id: rectangle_selector
BubbleButtons:
id: bubble_buttons
size_hint: (None, None)
size: (200, 40)
pos_hint_x: None
pos_hint_y: None
pos: Window.width*.4, Window.height*.1
opacity: 0
arrow_pos: 'top_mid'
BubbleButtonsUndoConfirm:
id: bubble_buttons_undo_confirm
size_hint: (None, None)
size: (200, 40)
pos_hint_x: None
pos_hint_y: None
pos: Window.width*.4, Window.height*.9
opacity: 0
arrow_pos: 'top_mid'
<BubbleButtons>:
resize_button: resize_button
cut_button: cut_button
rotate_button: rotate_button
BubbleButton:
id: resize_button
text: 'Resize'
BubbleButton:
id: cut_button
text: 'Cut'
BubbleButton:
id: rotate_button
text: 'Rotate'
<BubbleButtonsUndoConfirm>:
undo_button: undo_button
confirm_button: confirm_button
BubbleButton:
id: undo_button
text: 'Undo'
BubbleButton:
id: confirm_button
text: 'Confirm'
Console output, aka what the code prints (you can see that ImageLayout and CoreImage run twice)
EditImageScreen
enable_cropping
on_pre_enter
EditImageLayout
ImageLayout
CoreImage
ImageLayout
CoreImage
What I suspect is happening is that the super() is calling the base class EditImageLayout, the static elements of that base class are calling the .kv file and initating the ImageLayout and CoreImage classes from there. At the same time, "self" goes into action and does the exact same thing. This causes trouble later on when I implement on_touch_down over it (on_touch_down then appears twice, etc. )
Problem
ImageLayout and CoreImage were called twice.
[INFO ] [GL ] Unpack subimage support is available
enable_cropping
on_pre_enter
EditImageLayout
ImageLayout
CoreImage
ImageLayout
CoreImage
[INFO ] [Base ] Leaving application in progress...
Process finished with exit code 0
Root Cause
The double calls to ImageLayout and CoreImage were due to two kv files, App_R3.kv and app_r3.kv present as shown in the screen shots below. In the application, the app class is App_R3App(App): and it is using Builder.load_file('App_R3.kv') to load kv code into application.
Solution
Remove kv file, app_r3.kv. In the example, we renamed it to app_r3-off.kv
Kv language » How to load KV
There are two ways to load Kv code into your application:
By name convention:
Kivy looks for a Kv file with the same name as your App class in
lowercase, minus “App” if it ends with ‘App’ e.g:
MyApp -> my.kv
If this file defines a Root Widget it will be attached to the App’s
root attribute and used as the base of the application widget tree.
By Builder:
You can tell Kivy to directly load a string or a file. If this string
or file defines a root widget, it will be returned by the method:
Builder.load_file('path/to/file.kv')
or:
Builder.load_string(kv_string)
Recommendations
Since class rule, <MyScreenManager>: is defined in your kv file, you should use it rather than define sm = ScreenManager() in your Python code. On top of that, keep the view/design separate.
kv file
Add missing class rule for EditImageScreen.
<EditImageScreen>:
name: '_edit_image_screen_'
<EditImageLayout>:
Python code
Add class definition for MyScreenManager
In build() method, replace return sm with return MyScreenManager()
Before App_R3().run(), remove all references to sm
Since each screen has by default a property manager that gives you the instance of the ScreenManager used. In callback_image() and enable_cropping() methods, replace sm.current with self.manager.current
In __init__() method of EditImageLayout() class, remove self.sm = kwargs.pop('sm', None). If you need to access the root/ScreenManager, use sm = App.get_running_app().root
Snippet
class MyScreenManager(ScreenManager):
pass
...
def callback_image(self, new_image_address, image_address, title):
self.manager.current = "_third_screen_"
self.new_image_address = new_image_address
...
def enable_cropping(self):
...
print("enable_cropping")
self.manager.current = "_edit_image_screen_"
...
def __init__(self, **kwargs):
print("EditImageLayout")
self.crop_image_screen = kwargs.pop('crop_image_screen', None)
...
class App_R3App(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
App_R3App().run()
ScreenManager » Basic Usage
Each screen has by default a property manager that gives you the
instance of the ScreenManager used.

Cannot get kivy to display the relevant canvas and items for a breakout game

I am trying to create a breakout game using Python and Kivy. I have tried displaying other kivy features like labels and buttons and they all work perfectly. However, when I try to run the below code I keep getting a blank screen. Any help would be appreciated.
BreakoutApp.py
from kivy.app import App # App is base for any kivy app
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.modalview import ModalView
from kivy.properties import (ListProperty, NumericProperty, ObjectProperty, StringProperty)
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.uix.label import Label
from kivy.uix.scatter import Scatter
import random
__version__ = '0.1' # used in Android compilation
#Game is a big widget that will contain the entire game
# A subclass of Float layout as it will proportion its children in proportion to its own pos and size
class Game(FloatLayout): # Will contain everything
blocks = ListProperty([])
player = ObjectProperty() # The game's player instance
ball = ObjectProperty() # The game's ball instance
def setup_blocks(self):
for y_jump in range(5):
for x_jump in range(10):
print "in setup blocks"
block = Block(pos_hint={
'x': 0.05 + 0.09*x_jump,
'y': 0.05 + 0.09*y_jump})
self.blocks.append(block)
self.add_widget(block)
# App will initialise everything that kivy needs
class BreakoutApp(App):
def build(self):
print "in build self blocks"
g = Game()
g.setup_blocks()
return g
#f = FloatLayout()
#s = Scatter()
#l = Label(text="Hello!",
# font_size=150)
#f.add_widget(s)
#s.add_widget(l)
#return f
#Require a class for each game object
#kivy properties are special attributes declared at class level
class Player(Widget): # A moving paddle
position = NumericProperty(0.5)
direction = StringProperty("none")
def __init__(self, **kwargs):
super(Player, self).__init__(**kwargs)
with self.canvas:
Color(1, 0, 0, 1)
Rectangle(pos=self.pos, size=self.size)
class Ball(Widget): # A bouncing ball
# pos_hints are for proportional positioning, see below
pos_hint_x = NumericProperty(0.5)
pos_hint_y = NumericProperty(0.3)
proper_size = NumericProperty(0.)
velocity = ListProperty([0.1,0.5])
class Block(Widget): # Each coloured block to destroy
colour = ListProperty([1, 0, 0])
def __init__(self, **kwargs):
super(Block, self).__init__(**kwargs)
self.colour = random.choice([
(0.78,0.28,0), (0.28,0.63,0.28), (0.25,0.28,0.78)])
if __name__ == "__main__":
print "main method called"
BreakoutApp().run()
Breakout.kv
#:kivy 1.9.1
<Player>:
canvas:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
<Ball>:
canvas:
Color:
rgb: 1, 0.55, 0
Rectangle:
pos: self.pos
size: self.size
<Block>:
canvas:
Color:
rgb: self.color #property defined above
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: 0.1, 0.1, 0.1
Line:
rectangle:
[self.x, self.y, self.width, self.height]
Also, do I need to explicitly refer to the layout .kv file in the python file or are there any specific naming restrictions. I found some documentation online to name the two as found below.
You have in kv file color
<Block>:
canvas:
Color:
rgb: self.color #property defined above
yet in py file there is colour (with U):
class Block(Widget): # Each coloured block to destroy
colour = ListProperty([1, 0, 0])
If you change it, you'll get the desired output I believe:
<Block>:
canvas:
Color:
rgb: self.colour #property defined above
Proof:

Kivy widgets behaving erratically

I have been playing around with the Kivy Pong tutorial, getting up to speed with the framework, seeing if I could implement a few ideas. I have removed most of the Pong functionality, so I could have only bouncing ball on the screen and added some code to generate multiple bouncing balls on the screen, generated on touch. That worked fine. I then added some extra canvas instructions, so I would have a line drawn indicating the direction the ball is moving. This is where things got weird. The first ball acts just as it should, bouncing around the screen. But any following clicks generate balls that go off screen, randomly change direction and speed and in general behave chaotically. I have been looking at my code and I cannot seem to find any indication of what might be going wrong. I keep all the references to the widgets, I add them to the root widget, I don't seem to be sharing any information between them... Anyway, here is the code, maybe someone can enlighten me. Using latest kivy and python 3.6.
from random import randint
from kivy.app import App
from kivy.clock import Clock
from kivy.config import Config
from kivy.vector import Vector
from kivy.uix.widget import Widget
from kivy.properties import AliasProperty, ListProperty, NumericProperty, ReferenceListProperty
class Playground(Widget):
critters = ListProperty([])
def update(self, dt):
for critter in self.critters:
critter.move()
if (critter.y self.height):
critter.v_y *= -1
if (critter.x self.width):
critter.v_x *= -1
self.score.text = "{}".format(len(self.critters))
def on_touch_down(self, touch):
critter = Critter()
critter.pos = touch.x, touch.y
self.critters.append(critter)
self.add_widget(critter)
class Critter(Widget):
angle = NumericProperty(0)
v_x = NumericProperty(0)
v_y = NumericProperty(0)
velocity = ReferenceListProperty(v_x, v_y)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.velocity = Vector(5, 0).rotate(randint(0, 360))
self.angle = Vector(*self.velocity).angle(Vector(1, 0))
def move(self):
self.pos = Vector(*self.velocity) + self.pos
self.angle = Vector(*self.velocity).angle(Vector(1, 0))
class WorldApp(App):
def build(self):
game = Playground()
Clock.schedule_interval(game.update, 1.0/60.0)
return game
if __name__ == '__main__':
Config.set('kivy', 'desktop', 1)
Config.set('kivy', 'exit_on_escape', 1)
Config.set('graphics', 'resizable', 0)
WorldApp().run()
and the KV file
<Playground>
score: score
canvas:
Color:
rgb: 0.0, 0.1, 0.0
Rectangle
pos: self.pos
size: self.size
Label:
id: score
pos: self.parent.width - self.size[0], self.parent.height - self.size[1]
font_size: 16
size: self.texture_size
<Critter>
size: 30, 30
canvas:
Rotate:
angle: self.angle
origin: self.center
axis: 0, 0, 1
Color:
rgb: 0.5, 0.0, 0.0
Ellipse:
pos: self.pos
size: self.size
Color:
rgb: 1, 1, 0.0
Line:
width: 2
points: self.center[0], self.center[1], self.center[0] + self.size[0] / 2, self.center[1]
I'm not sure if it's causing your problem, but your Rotate instructions aren't bounded by the widget rule and will affect any later widgets - so the Rotate of each Critter is applied to every later one.
To avoid this, add PushMatrix: at the top of the canvas rule and PopMatrix: at the bottom. These instructions effectively save and later revert to the initial rotation state before your change.

Categories