generating random numbers on a kivy label - python

I want to show randomly generated numbers on a kivy label which is similar to a countdown animation.
prototype.py (python file):
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
import random
import time
class RootWidget(BoxLayout):
def do(self):
a = 0
total = 5
user = self.ids.name
while a <= 10:
randnum = random.randrange(total)
user.text = str(randnum)
a+=1
class prototypeApp(App):
def build(self):
return RootWidget()
if __name__ == "__main__":
no=prototypeApp()
no.run()
prototype.kv (kv file):
<RootWidget>:
orientation:'vertical'
Label:
id: name
text:''
font_size:70
height:70
Button:
text:'click me'
on_release:app.root.do()
size_hint_y:.1
There was no error but on the label, it displays only the last number
of the randomly generated numbers.
But I expected the numbers in the loop to flow smoothly on the label not show me only the last number in the loop.

Your loop in the do() method is run on the main thread, which is the same thread that updates the GUI. Since it is a single thread, the GUI cannot be updated until your do() method completes, which means that only the last random number gets displayed in the GUI.
To fix that, run that loop in a separate thread (see threading). And you will need to execute the self.ids.name.text = str(randnum) back on the main thread (consider using Clock to accomplish that).
You will probably want to add a sleep in the loop, otherwise it will probably happen too fast to see the intermediate numbers.

Related

Why doesn't my Kivy Text input work in returning the value to root when I use kivy.clock

import kivy
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.widget import Widget
from kivy.clock import Clock
Builder.load_string('''
<apper>
input_val : input_t
BoxLayout:
size: root.size
Label:
id: label_t
text: root.texter
TextInput:
id: input_t
Button:
on_press : root.doer()
''')
class apper(Widget):
texter = StringProperty()
input_val = ObjectProperty(None)
run = 0
def doer(self, dt):
self.run+=1 # keep track of number of times this function ran
#self.texter = self.ids.input_t.text
print(self.ids.input_t.text, self.run) #prints an empty string for self.ids.input_t.text
self.texter = self.input_val.text
class runs(App):
def build(self):
Clock.schedule_interval(apper().doer, 1.0/2.0)
return apper()
runs().run()
#the code works when you remove the Clock statement and the second argument or apper.doer() and you have to press the button
#to rename the label
#how comes it doesnt work when you introduce the clock statement
#Note that I am new to kivy but not new to programming
When I use a button to run the function on press the correct values from text input are returned but using a clock it doesn't work, can anyone help.
Also when the program should be running properly with the Clock then the button is unnecessary since it updates the label with the text of the TextInput every 0.5 seconds.
Is there any way the code can be rewritten or mended such that the TextInput returns a value and not an empty string.
move the clock to ur apper class: and put it like this
Clock.schedule_interval(self.doer, 1/2)
and declare doer like this:
def doer(self, *args):
and feel free to check this tuto i uploaded recently
https://www.youtube.com/watch?v=DbX-CdVob6E&t=57s

When running Python + Kivy program including multiprocessing, white blank screen appears

I want to use Kivy program including multiprocessing process.
Sample code worked as i thought;however, a blank white screen which does not accept the operation appeared. It appeared just when i started the multiprocessing process.
I already know that using threading.Thread, the blank screen won't appear;however, an actual program that i want to implement needs multiprocessing process.
How can i remove this blank screen?
This is what i saw.
P.S.
Considering comments, maybe it's a peculiar problem on windows.
I'm using windows 10, Python 3.6.4.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
import time
from datetime import datetime
import multiprocessing as mp
from multiprocessing import Process, Queue, freeze_support
sm = ScreenManager(transition=NoTransition())
Builder.load_string('''
<TestScreen>:
BoxLayout:
orientation: "vertical" # place object vertically
Label:
size_hint: 1, 1
text: 'TITLE'
font_size: 150
color: 1, 1, 1, 1
Button:
text: 'start'
on_press: root.start_mp()
Button:
text: 'stop'
on_press: root.stop_time()
''')
def count_time(x):
for i in range(x):
print('count={}'.format(i))
time.sleep(1)
class Test(App):
def build(self):
sm.add_widget(TestScreen(name='test'))
return sm
class TestScreen(Screen):
p1 = mp.Process(target=count_time, args=(10, ))
def start_mp(self):
self.p1 = mp.Process(target=count_time, args=(10, ))
self.p1.start()
def stop_time(self):
self.p1.terminate()
print(datetime.now())
if __name__ == '__main__':
Test().run()
Turns out that the problem is the ScreenManager that you create at the start of your file. Move the sm = ScreenManager(transition=NoTransition()) into the build method of your app and it should work without creating a second screen.
After some research, it appears that the problem results from the new Process importing the original python script. Since the sm = ScreenManager(transition=NoTransition()) is unprotected in the original file, it gets executed upon that import. So the solution is simply to put that command anywhere that is protected. So another possibility that will work is to move that command into the if __name__ == '__main__': block just before the Test().run() command.

Text based game Kivy- text not updating

I am currently making a text based game using Python, and I have been wanting to use Kivy to make a Graphical Interface for it. I have been unable to get it to work so far though.
The reasoning is that I have changed from what was Print and Input to self.label1 = Label(text='Hello world') etc (There is multiple variables- label2, 3 and 4.) and then for the input, a text input box, which the input is used by a function when the button is pressed (Currently the textbox isn't involved, since I am first just testing whether the button works.). The problem is, I need a way to update the text displayed with the new value. For example I would like label1 to change to "These are the controls". But when a button is clicked, changes don't happen- I would like the GUI to be updated with the new text, through the changing of the values of the label variables. I believe that since those are being returned, the code above no longer loops through. The ideas I've been given, is to put the different sections into functions, or to use threading. Does anyone have any tips to push me in the right direction. I understand it may be too much be to ask, if so I'll continue to look for a solution myself. I can show some of the code if needed.
import kivy.uix.boxlayout
import kivy.uix.textinput
import kivy.uix.label
import kivy.uix.button
from kivy.app import App
from random import shuffle
import time
from kivy.uix.button import Button
from kivy.clock import Clock
alive = 1
buttonPressed = 0
class SimpleApp(App):
def build(self):
global alive
global buttonPressed
donext = 0
alive = 1
def callback(self):
global buttonPressed
buttonPressed = 1
self.label1 = kivy.uix.label.Label(text="")
self.label2 = kivy.uix.label.Label(text="")
self.label3 = kivy.uix.label.Label(text="You have found yourself in a dungeon, somewhere is your escape path, will you make it out, and if so, what with?")
self.label4 = kivy.uix.label.Label(text="")
print(buttonPressed)
if buttonPressed == 1:
print("Has been pressed should work theoretically")
self.label1 = kivy.uix.label.Label(text="These are the basic controls-")
self.label2 = kivy.uix.label.Label(text="The controls-")
self.label3 = kivy.uix.label.Label(text="A- approach enemy/ attack enemy")
self.label4 = kivy.uix.label.Label(text="C- Go to chest")
print("Press enter to continue.")
self.boxLayout = kivy.uix.boxlayout.BoxLayout(orientation="vertical")
self.boxLayout.add_widget(self.label1)
self.boxLayout.add_widget(self.label2)
self.boxLayout.add_widget(self.label3)
self.boxLayout.add_widget(self.label4)
self.btn1 = Button(text='Hello world 1', on_press=callback)
self.boxLayout.add_widget(self.btn1)
return self.boxLayout # Causes script not to continue
if __name__ == "__main__":
simple = SimpleApp()
simple.run()
If you have been advised to use threads it seems that your advisor does not know about GUI, in the GUIs the tasks are done asynchronously through events, that is, the GUI will provide you with methods to indicate when something has happened in the GUI, for example the event on_press notifies you when the button is pressed, so they connect a signal to that event. On the other hand the GUIs have a high component of object-oriented programming, event-oriented programming, and in the .kv is a declarative language, so I recommend you read about those concepts and for it kivy offers a great documentation and examples, review them. If you want to update a Label at least it must be accessible in the whole class, so it must be an attribute of the class and use the text property, on the other hand if you want to show a text of several lines use \n to indicate that there is a jump of line.
Considering the above, the solution is as follows:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
class SimpleApp(App):
def build(self):
self.label = Label(text="You have found yourself in a dungeon,\nsomewhere is your escape path,\nwill you make it out, and if so, what with?")
self.button = Button(text="Press Me", on_press=self.on_clicked, size_hint=(1.0, None))
layout = BoxLayout(orientation="vertical")
layout.add_widget(self.label)
layout.add_widget(self.button)
return layout
def on_clicked(self, instance):
self.label.text = "These are the basic controls-\nThe controls-\nA- approach enemy/ attack enemy\nC- Go to chest"
if __name__ == "__main__":
SimpleApp().run()

kivy - change things twice with one button click

wonder if anyone can help me out.
I am stuck with a problem. I want to change a buttons background_normal TWICE by clicking another button.
So for example first change to yellow than wait 2 seconds (time.sleep(2)) and then change to red.
I tried to make a simple functions that does this. But the problem is that it does not update the first change of the background_normal. It only displays the latest change.
Tried to split it into two functions, one changes it to the first color, the other one changes it to the second color. But still it does not display anything but the latest change.
Is there any possibility to solve this?
Update:
My code is ~2k lines so i will post a short form of my problem
class Game(FloatLayout):
def firstaction(self):
#change button1 to yellow and wait 2 seconds
def secondaction(self):
#change button1 to red
class GameApp(App):
def build(self):
return Game
kv file looks something like
button2:
on_press: Game.firstaction
on_press: Game.secondaction
You talk about colors so you should use background_color instead of background_normal. Going to the problem, the cause is time.sleep() because it blocks the event loop, the event loop helps the GUI to update due to OS or user events. The solution is to use Clock.
from kivy.app import App
from kivy.uix.button import Button
from kivy.clock import Clock
class MyButton(Button):
def on_press(self):
self.background_color = (1,1,0,1)
fn = lambda dt: setattr(self, "background_color", (1,0,0,1))
Clock.schedule_once(fn, 2) # <--- 2 seconds
class MyApp(App):
def build(self):
button = MyButton(text='Hello World')
return button
if __name__ == '__main__':
MyApp().run()

Kivy: Label text does not update during for-loop

I have an issue when I try to update a label text during a for-loop. There are similar entries (e.g.: Update properties of a kivy widget while running code) but they do not seem to fit my issue exactly (or I missed the point…).
I run following code:
*.py:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
#from time import sleep
class MyBox(BoxLayout):
tobeupd = StringProperty()
def __init__(self,*args,**kwargs):
super(MyBox,self).__init__(*args,**kwargs)
self.tobeupd = '#'
def upd_ltxt(self):
for i in range(1,10):
self.tobeupd = str(i)
print(self.tobeupd)
input('Write something: ') # new line, see edit below
#sleep(0.5)
class updApp(App):
def build(self):
return MyBox()
if __name__ == '__main__':
updApp().run()
*.kv
<MyBox>:
orientation: 'horizontal'
cols: 2
Label:
text: root.tobeupd
Button:
text: 'Start Update'
on_release: root.upd_ltxt()
Whereas the ‘print’ statement updates the shell regularly, the label text updates at the end of the for-loop only.
Can anyone explain to me why Kivy works this way and how I can overcome this problem?
EDIT: According to PM2Ring and Gugas, I changed the code in order to avoid the sleep-function. The problem remains if I ask the user to enter something before the loop can be continued. The values are updated in the shell but not on the label.
You can use threading for this.
When you do a loop or wait for an input in kivy, the main thread is waiting, and nothing will update on the app. threading will prevent that.
Use threading to make another thread besides the main thread.
Example:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.lang import Builder
import threading
Builder.load_string('''
<MyBox>:
orientation: 'horizontal'
cols: 2
Label:
text: root.tobeupd
Button:
text: 'Start Update'
on_release: root.upd_ltxt()
''')
class MyBox(BoxLayout):
tobeupd = StringProperty()
def __init__(self,*args,**kwargs):
super(MyBox,self).__init__(*args,**kwargs)
self.tobeupd = '#'
def upd_ltxt(self):
threading.Thread(target=self.update_label).start()
def update_label(self):
for i in range(1,10):
print(self.tobeupd)
self.tobeupd = str(i)
input('Write something: ') # new line, see edit below
class updApp(App):
def build(self):
return MyBox()
if __name__ == '__main__':
updApp().run()
Now its worth mentioning that you can keep pushing the button and start threads, even if the first did not finish yet. This might be an unwanted behavior.
This can be prevented by disabling the button in the beginning of the thread, and enabling it again at the end.
Give the button an id in kv:
Button:
id: updatebutton
text: 'Start Update'
on_release: root.upd_ltxt()
And in the thread do like this:
def update_label(self):
self.ids.updatebutton.disabled = True
for i in range(1,10):
self.tobeupd = str(i)
input('Write something: ')
self.ids.updatebutton.disabled = False
You can also use Kivys clock Class, which is an Event dispatcher. It will schedule an event, which is a function. For example, updating your labels text.
from kivy.clock import Clock
def to_be_called_back(self,dt):
print("This function should be periodically executed")
def do_the_loop(self):
Clock.schedule_interval(self.to_be_called(),0.5)
Here the function to_be_called() will be called every 0.5 seconds. The dt variable stands for deltatime and is just apparently needed by the Clock class (without it, it made problems with my code)
I'd still put the do_the_loop() function into a separate thread. But thats what kivy provides for it. If you want to know more about the clock Class head over here.
I believe all you need to do is put
root.update()
whenever you want the GUI to update, so therefore in the loop, after the text change has been made. Works in tkinter like a charm. You can also restrict the update to a specific text area by changing root (or main) to the name of the text area.

Categories

Resources