I have an array with x and y positions. I want to show these points linking a line successively for each point, then, creating an animation. It is just like a path tracking whose trail is the line. I'm using python-kivy to try to display it.
I couldn't find any help on google.
There's a button that triggers this animation.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics.vertex_instructions import Line
from kivy.graphics.context_instructions import Color
from time import sleep
x_moves = [250, 250.4305, 249.8804, 246.0923, 239.7496, 233.8188, 225.7797, 215.8385, 205.8413, 196.6497, 189.7026, 181.2445, 174.9816, 171.9882, 166.1171, 161.6505, 159.9929, 161.1338, 164.853, 168.2874, 170.768, 178.6918, 184.5233, 190.0262, 195.607, 202.0255, 210.5954, 216.1031, 219.6285, 224.9134, 230.2314, 237.7017, 243.7408, 250.5839, 256.2949]
y_moves = [250, 240.0093, 230.0244, 220.7697, 213.0386, 204.9872, 199.0396, 197.9567, 197.7209, 201.6598, 208.8527, 214.1875, 221.9834, 231.5249, 239.62, 248.567, 258.4287, 268.3634, 277.6461, 287.0379, 296.7253, 302.8256, 310.9492, 319.299, 327.5969, 335.2652, 340.4185, 348.7651, 358.1231, 366.6125, 375.0812, 381.7291, 389.6996, 396.9915, 405.2003]
class my_App(App):
def build(self):
self.widget = Widget()
self.widget.on_touch_down = self.touch
with self.widget.canvas:
Color(0, 1, 0, 1) #just initial config
Line(points = [0,0,500,0,500,500,0,500], close = True) #just initial config
return self.widget
def touch(self, touch): #executes the animation
pts = []
for i in range(len(x_moves)):
pts.append(x_moves[i])
pts.append(y_moves[i])
self.widget.canvas.add(Line(points = pts))
sleep(0.1)
if __name__ == '__main__':
obj = my_App()
obj.run()
This is my code that doesn't work. But that's the idea.
You're blocking the UI from updating because your function doesn't return and you use sleep(). For obvious reasons, Kivy can't do anything while your code is running. If you want to wait before running some more code, you can use Kivy's Clock for this. Then Kivy will be able to update the screen.
But you should probably just look at Kivy's Animation, which is built to do this much better.
Related
I'm trying to make an app that allows me to take pictures. I've tried to copy different examples that allow to do this, but I always get the same error:
self._camera = CoreCamera(index=self.index, stopped=True)
TypeError: 'NoneType' object is not callable
If it helps, this is the latest code I've tried:
from kivy.app import App
from kivy.uix.camera import Camera
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class CameraExample(App):
def build(self):
layout = BoxLayout(orientation='vertical')
# Create a camera object
self.cameraObject = Camera(play=False)
self.cameraObject.play = True
self.cameraObject.resolution = (300, 300) # Specify the resolution
# Create a button for taking photograph
self.camaraClick = Button(text="Take Photo")
self.camaraClick.size_hint = (.5, .2)
self.camaraClick.pos_hint = {'x': .25, 'y': .75}
# bind the button's on_press to onCameraClick
self.camaraClick.bind(on_press=self.onCameraClick)
# add camera and button to the layout
layout.add_widget(self.cameraObject)
layout.add_widget(self.camaraClick)
# return the root widget
return layout
# Take the current frame of the video as the photo graph
def onCameraClick(self, *args):
self.cameraObject.export_to_png('/kivyexamples/selfie.png')
# Start the Camera App
if __name__ == '__main__':
CameraExample().run()
I've also tried to install the opencv-python, which is something that many people recommended, however it still returns the same error.
So I am trying to create a program where you click on a button and it gives you a random task from a list of tasks you provide and another button to list all those tasks. So there is no errors in the code, the only problem is that when I run it, I want each button to call the same function but give different parameters depending on the i variable in the loop. Also I took out the button that gets the task since that is not in any way related to the problem.
GUI code:
#imports
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.textinput import TextInput
import Functions
import sys
import time
class Home(FloatLayout):
#function that setups the first page
def setup(self, obj):
self.clear_widgets()
List = Button(
text="Your List",
font_size=20,
size_hint=(1/6, 1/12),
pos_hint={"x":2.5/6, "y":1/6},
on_press = self.LIISST
)
self.add_widget(List)
#function that lists tasks
def LIISST(self, obj):
self.clear_widgets()
FUNC_ = Functions.Choose_("GE", 5)
FUNC_.sort()
LT = GridLayout(
cols=1,
spacing=10,
size_hint_y=None,
)
LT.bind(minimum_height=LT.setter('height'))
SCR = ScrollView(
size_hint=(1/3.5, 2/3),
pos=(self.width*137/384, self.height/3.25)
)
for i in range(len(FUNC_)):
but_ = Button(text=str(FUNC_[i]),
size_hint=(18/20, None),
height=40,
font_size=self.width/75,
on_press=lambda s:Functions.Choose_("DE", but_.text)
)
LT.add_widget(but_)
SCR.add_widget(LT)
ACC_ = Button(
text="Back",
font_size=20,
size_hint=(1/8, 1/14),
pos_hint={"x":3.5/8, "y":1/6},
on_press=self.setup
)
self.add_widget(SCR)
self.add_widget(ACC_)
def __init__(self, **kwargs):
super(Home, self).__init__(**kwargs)
Window.clearcolor = (255/255, 255/255, 255/255, 255/255)
self.setup(self)
class App_(App):
def build(root):
return Home()
if __name__ == '__main__':
App_().run()
Function to get the task: (separate file)
import random
import sys
def Choose_(vall_, VAAL_):
try:
#variables
cfile = open(r"Choice.txt", "r+")
cfile.seek(0)
dfile = open(r"Done.txt", "r+")
dfile.seek(0)
items = []
DON = []
#appenders for items in file to line
[items.append(line) for line in cfile]
[DON.append(line) for line in dfile]
stripp1 = [s.strip() for s in items]
stripp2 = [s.strip() for s in DON]
stripp1.sort()
stripp2.sort()
if vall_ == "DE":
print(VAAL_)
if vall_ == "GE":
return stripp1
sys.exit()
for s in stripp2:
if s in stripp1:
stripp1.remove(s)
if not stripp1:
dfile.seek(0)
dfile.truncate(0)
return False
sys.exit()
luck = random.randint(0, (len(stripp1)-1))
dfile.write(stripp1[luck])
dfile.write("\n")
return(stripp1[luck])
finally:
cfile.close()
dfile.close()
Task file (same directory as above codes):
ClIP STUDIO PAINT
CYBRARY (HACKING)
CYBRARY (LINUX)
VIRTUAL DJ
RASPBERRY PI
PACKET TRACER
VIRTUALBOX
PHOTOSHOP
BLENDER
SOLIDWORKS
KHAN ACADEMY (ANATOMY)
SOLOLEARN
UNITY
KHAN ACADEMY (ELECTRICAL)
PROGRAMMING
KHAN ACADEMY (PHYSICS)
ADOBE PREMIERE
Tasks already done(again, same directory as above files):
ClIP STUDIO PAINT
CYBRARY (HACKING)
CYBRARY (LINUX)
VIRTUAL DJ
RASPBERRY PI
PACKET TRACER
I expected the output to print the button's text for each different button, but it only texts the very last item in the task file for each button, which is virtualbox. Also the code sorts the tasks in abc order, which is why the last item is virtualbox.
Problem
I expected the output to print the button's text for each different
button, but it only texts the very last item in the task file for each
button, which is virtualbox.
I want each button to call the same function but give different
parameters depending on the i variable in the loop.
Root Cause
The text printed is always "VIRTUALBOX" because it is the last button added and it is referenced in the call by but_.text.
Solution
The following enhancements are required to solve the problem.
main.py
Implement a new method, callback()
Bind button's on_press event to the new method, callback()
Snippets - main.py
for i in range(len(FUNC_)):
but_ = Button(text=str(FUNC_[i]),
size_hint=(18 / 20, None),
height=40,
font_size=self.width / 75,
on_press=self.callback
)
LT.add_widget(but_)
SCR.add_widget(LT)
...
def callback(self, instance):
print(f"\ncallback: instance={instance}, text={instance.text}")
Functions.Choose_("DE", instance.text)
Output
Question: How can I get all Unicode characters to render correctly in a TextInput using Kivy?
More details below
I'm generating random Unicode characters in a range between 0x0200 and 0x9990 which is massive the issue is that a large portion of the characters will not render correctly in a TextInput to be more specific less than half will work.
Whatever doesn't render ends up looking like a small rectangle with an x through it, yet when I copy and paste it into another display source it works fine. I've run the code through idle and it displays fine as well the issue seems to be with kivy, any suggestions as to why this is happening?
import random
import kivy
from kivy.uix.textinput import TextInput
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.app import App
kivy.require('1.9.1')
class testclass(object):
def example(self, event):
k_len = list()
complete = ''
for i in range(32):
k_len.append(random.randint(0x0200, 0x9990))
for i in k_len:
if i != 32:
complete += chr(i)
result.text = complete
t = testclass()
Root = Widget(size = (600, 200))
buttonOne = Button(text = 'click me', pos = (1,170), size = (120,30))
buttonOne.bind(on_press = t.example)
result = TextInput(hint_text = 'Output: ', size = (600, 50), pos = (0, 0), multiline = (True))
Root.add_widget(buttonOne)
Root.add_widget(result)
class testappApp(App):
def build(self):
return Root
Window.size = (600, 200)
if __name__ == '__main__':
testappApp().run()
This code will only work if you have kivy setup, you can tweak it to work in idle but as I stated the code works as intended it's just not displaying correctly within kivy :)
Your font does not seem to support these characters - switch to another one with support for that range (see https://en.wikipedia.org/wiki/Unicode_block for more info on what needs to be there)
I am trying to create a line and update is after a fixed interval of time (say 5 seconds). I wrote the code below but it does not update the line. Can anyone help me figure out who to do it?
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.graphics import Color, Ellipse, Line
import time
from kivy.clock import Clock
class MyWidget(Widget):
def my_callback(dt,ds):
Line(points=[100, 100, 200, 100, 100, 200], width=10)
pass
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
with self.canvas:
self.line = Line(points=[100, 100, 200, 100, 100, 200], width=1)
self.line.width = 2
Clock.schedule_once(self.my_callback, 5)
pass
# add your instruction for main canvas here
with self.canvas.before:
pass
# you can use this to add instructions rendered before
with self.canvas.after:
pass
# you can use this to add instructions rendered after
class LineExtendedApp(App):
def build(self):
root = GridLayout(cols=2, padding=50, spacing=50)
root.add_widget(MyWidget())
return root
if __name__ == '__main__':
LineExtendedApp().run()
my_callback is not actually called within the with statement but rather 5s later when that is long gone, and even it if was it wouldn't do what you want - it would draw a new Line, not modify the existing one.
Instead you can change my_callback to do:
self.line.points = [100, 100, 200, 100, 100, 200]
self.line.width = 10
This will modify the existing line as you want. You can also take the clock scheduling out of the with statement but its position doesn't actually matter as long as it stays in the __init__
I have an app that needs to add lots of widgets dynamically. Here's a working app to simulate this:
from threading import Thread
from kivy.app import App
from kivy.uix.stacklayout import StackLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.clock import Clock
class LotsOfWidgets(App):
def build(self):
self.widgets_amt = 5000
root = GridLayout(cols=1)
self.label_container = StackLayout()
generate_button = Button(text='Generate lots of labels',
size_hint_y=None, height=44)
generate_button.bind(on_press=self.generate_lots_of_labels)
hooray_button = Button(text='Print hooray',
size_hint_y=None, height=44)
hooray_button.bind(on_press=self.print_hooray)
for widget in (generate_button, hooray_button,
self.label_container):
root.add_widget(widget)
return root
def generate_lots_of_labels(self, *args):
for _ in xrange(self.widgets_amt):
label = Label(text='a', size_hint=(None, None), size=(10,10))
self.label_container.add_widget(label)
def scheduled_generate_lots_of_labels(self, *args):
Clock.schedule_once(self.generate_lots_of_labels)
def threaded_generate_lots_of_labels(self, *args):
thread = Thread(target=self.generate_lots_of_labels)
thread.start()
def print_hooray(self, *args):
print 'hooray'
LotsOfWidgets().run()
We have a grid layout that has 2 buttons and a stack layout. By clicking the first button, 5000 labels will be generated inside the stack layout. The second button only prints "hooray" to the console.
Adding 5000 widgets to the stack layout and drawing them on the screen takes quite a bit of time which is fine. When you press the button to generate them and immediately press the "print hooray" button, on my computer hooray gets printed about 3 seconds later after the labels appear on the screen. So the problem is, that the UI becomes unresponsive while generating the labels.
I tried to solve this with threading by binding generate_button.on_press to scheduled_generate_lots_of_labels and threaded_generate_lots_of_labels (of course not at the same time) methods instead the one shown in the code, but they don't seem to help.
Is there anything you can do to keep the UI responsive even if it's generating all those widgets?
You could add the labels in batches scheduled by Kivy's Clock module.
I looked into the idea that brousch gave in his answer. What I tried first was to split those 5000 items into smaller chunks and iterating each chunk in its own Clock.schedule_once.
The result turned out to be very similar to just scheduling once the whole shebang of 5000 items. If you schedule it to be executed in 1 second, you have 1 second to click the hooray button. After that UI becomes unresponsive until all the widgets have been generated.
So in most cases, the only option is to use Clock.schedule_interval and here's an experiment of that. The build method remains the same.
def chunk(self, l, n):
for i in xrange(0, len(l), n):
yield l[i:i+n]
def generate_lots_of_labels(self, *args):
n = 500
interval = 0.01
self.chunks = list(self.chunk(range(self.widgets_amt), n))
self.i = 0
Clock.schedule_interval(self.iterate, interval)
def iterate(self, *args):
for _ in self.chunks[self.i]:
label = Label(text='a', size_hint=(None, None), size=(10,10))
self.label_container.add_widget(label)
self.i += 1
if self.i >= len(self.chunks):
Clock.unschedule(self.iterate)
This is a compromise between the widget generation speed and responsiveness of the UI. Depending on the application and executing environment, different values of scheduling interval and n give the best result.