Kivy: Set label text then change it after a second - python

I have made a coin flip button that shows the result in Label, every time the button is pressed, the result change according to the function.
def coin_flip(self):
sound_1 = SoundLoader.load('coin.wav')
res = random.randint(1,2)
if sound_1:
sound_1.play()
if res == 1:
self.coin_f_result.text = "HEAD"
else:
self.coin_f_result.text = "TAIL"
What I want to do is, showing the result in the Label, and then, after a second set the Label text as " ". Here's what I tried, but i only get the function calling delayed, and the label text is set directly to " ".
def coin_flip(self):
sound_1 = SoundLoader.load('dice.wav')
res = random.randint(1,2)
if sound_1:
sound_1.play()
if res == 1:
self.coin_f_result.text = "HEAD"
time.sleep(1)
self.coin_f_result.text = " "
else:
self.coin_f_result.text = "TAIL"
time.sleep(1)
self.coin_f_result.text = " "

Never use time.sleep() in an event driven framework such as kivy. It just blocks execution and as you saw, events are not handled. Use Clock.schedule_once() instead. For example, in the same class that has the coin_flip method, define
def reset_label(self, *args):
self.coin_f_result.text = ' '
And at the end of coin_flip() write
Clock.schedule_once(self.reset_label, 1)
For smooth transitions you can pair that with Animation, too.

Related

Why are these lines of code causing keyboard lag and is there an alternative?

So I am learning python and I am trying to create a code that detects the time between pressing "space" and "a" for something in Minecraft. The problem is that this program lags my keyboard/causes delay in keyboard presses.
I've narrowed the issue down to this:
while True:
if keyboard.is_pressed ('p'):
strafe45()
If I replace this with something like this: It does not cause keyboard lag.
run = 1
while run == 1:
strafe45()
I think it is because the first is constantly checking if I am typing 'p' or not, which is the cause of the lag, but how else can I write something like that? I cannot use while run == 1: because eventually it gives me an error since I am holding down 'a' and the variable 'start' does not have an assigned value any more.
Here is the full code if needed:
import keyboard
import time
import math
def strafe45():
while True:
if keyboard.is_pressed ('space'):
print ("starting timer")
start = time.perf_counter()
time.sleep(0.05)
if keyboard.is_pressed ('a'):
end = time.perf_counter()
print ("ending timer")
tickTime = end - start
tick = 0.05
if tickTime > tick:
print ("Did not make strafe. Too slow by " + str(tickTime - tick) + "\n" +
"Time passed (r): " + str(round(tickTime/tick, 2)) + "\n" +
"Time passed (a): " + str(tickTime/tick))
break
if tickTime < tick:
print ("Did make strafe by " + str(tick - tickTime) + "\n" +
"Time passed (r): " + str(round(tickTime/tick, 2)) + "\n" +
"Time passed (a): " + str(tickTime/tick))
break
run = 1
while run == 1:
strafe45()
"""while True:
if keyboard.is_pressed ('p'):
strafe45()"""
while True:
if keyboard.is_pressed ('p'):
strafe45()
When the p key is pressed, strafe45 gets called, and some sleep calls happen as a result.
As long as the p key is not being pressed, there is a tight while loop that keeps checking for when the key gets pressed.
You should have a single while loop, outside the key-handling function, and ensure that a time.sleep call occurs every time through this loop - by putting it in the loop explicitly. If you call out to functions to handle the keys (a good idea as the code becomes more complicated), they should not have their own loop - they should just make the appropriate changes to the program state according to what was pressed.
For example:
begin = None
def begin_timing():
global begin
begin = time.perf_counter()
def end_timing():
global begin
if begin is not None: # otherwise, we weren't timing yet.
end = time.perf_counter()
print('elapsed time:', end - begin)
begin = None # so that we can begin timing again.
while True:
# There needs to be a delay each time through the loop,
# but it needs to be considerably shorter than the time interval you're
# trying to measure.
time.sleep(0.01)
if keyboard.is_pressed('b'): # begin
begin_timing()
elif keyboard.is_pressed('e'): # end
end_timing()
Rather than constantly checking each loop, add a hook and check only when a key is pressed. keyboard.on_press(callback) adds a listener to every keyboard and key that envokes the given callback. This should alleviate your latency issues. Check out the Keyboard API Page for full documentation
def check_key(x): #x should be an keyboard.KeyboardEvent
print x.name, x.scan_code, x.time
if x.name == "":
something
elif x.name == "":
something else
keyboard.on_press(check_key)

Python: ' ' is not defined

Here is my code:
# This program makes the robot calculate the average amount of light in a simulated room
from myro import *
init("simulator")
from random import*
def pressC():
""" Wait for "c" to be entered from the keyboard in the Python shell """
entry = " "
while(entry != "c"):
entry = raw_input("Press c to continue. ")
print("Thank you. ")
print
def randomPosition():
""" This gets the robot to drive to a random position """
result = randint(1, 2)
if(result == 1):
forward(random(), random())
if(result == 2):
backward(random(), random())
def scan():
""" This allows the robot to rotate and print the numbers that each light sensors obtains """
leftLightSeries = [0,0,0,0,0,0]
centerLightSeries = [0,0,0,0,0,0]
rightLightSeries = [0,0,0,0,0,0]
for index in range(1,6):
leftLight = getLight("left")
leftLightSeries[index] = leftLightSeries[index] + leftLight
centerLight = getLight("center")
centerLightSeries[index] = centerLightSeries[index] + centerLight
rightLight = getLight("right")
rightLightSeries[index] = rightLightSeries[index] + rightLight
turnRight(.5,2.739)
return leftLightSeries
return centerLightSeries
return rightLightSeries
def printResults():
""" This function prints the results of the dice roll simulation."""
print " Average Light Levels "
print " L C R "
print "========================="
for index in range(1, 6):
print str(index) + " " + str(leftLightSeries[index]) + " " + str(centerLightSeries[index]) + " " + str(rightLightSeries[index])
def main():
senses()
pressC()
randomPosition()
scan()
printResults()
main()
So, I am getting this error when I run my program.
NameError: global name 'leftLightSeries' is not defined
I understand that I must be doing something wrong related to the return statement. I'm not sure if I can only return one variable at the end of a user-defined function. If that were to be true, then I should probably separate the scan(): function. Anyways, I would appreciate any help on how to fix this error. Also, this is the result that I am looking for when I successfully complete my program:
Click Here
I am looking to complete the average values like the picture shows, but I am not worried about them at this point, only the list of values from the light sensors. I do not need to reach those exact numbers, the numbers will vary in the simulator.
If you want to return multiple items from scan(), don't use three separate return statements. Instead, do this:
return leftLightSeries, centerLightSeries, rightLightSeries
Also, when you call the function, you have to assign variable(s) to the returned values; it won't automatically create new local variables with the same names. So in main, call scan() like this:
leftLightSeries, centerLightSeries, rightLightSeries = scan()

changing buttons within tkinter

win=Tk()
level1=matrixmaker(4)
def display(x,y):
if z["text"] == " ":
# switch to Goodbye
z["text"] = level1[x][y]
else:
# reset to Hi
z["text"] = " "
for x in range(0,4):
for y in range(0,4):
z=Button(win,text=" ", command=display(x,y))
z.grid(row=x,column=y)
I have this code but I don't know how to get the display function to work. How can I call the button and change the text without it having a hardcoded variable name?
You can't assign the command to the button with the called function (display(x, y)) because this assigned what this function returns (None) to the button's command. You need to assign the callable (display) instead.
To do this and pass arguments you'll need to use a lambda:
z = Button(win, text='')
z['command'] = lambda x=x, y=y, z=z: display(x, y, z)
z.grid(row=x, column=y)
Also, you'll want to change your display() function to accept another argument z so that the correct button gets changed (and correct the indentation):
def display(x,y,z):
if z["text"] == " ":
# switch to Goodbye
z["text"] = level1[x][y]
else:
# reset to Hi
z["text"] = " "

Python not reading string a second time

I'm writing a text adventure (does anyone remember Zork?), and I'm having troubles with this code:
from random import randint
def prompt():
action = input(">>> ").lower()
if action == "exit":
quit()
elif action == "save":
save()
else:
return action
def action_error(custom=False):
if custom != False:
print(custom)
else:
phrases = ["A bunch", "of funny", "error phrases"]
print(phrases[randint(1, len(phrases)-1)])
return prompt()
action = prompt()
while True:
print(action) #Debugging purposes
if action.find("switch") != -1:
if action.find("light") != -1:
second_room() #Story continues
else:
action = action_error("What do you want to switch?")
action = action_error()
The matter is that if I enter a string that contains "switch", the next input is not picked up.
Also, anyone has better ways to parse verb-noun strings like "switch the light", "open the door" or "look around"/"look at OBJECT"?
First of all I noticed that if you enter switch twice the second time it's caught as an error by your program.
I think the problem lies at the end of the action_error function where you assign the return value to prompt(), so the input get consumed too early.
A possible fix would be:
def action_error(custom=False):
if custom != False:
print(custom)
else:
phrases = ["A bunch", "of funny", "error phrases"]
print(phrases[randint(1, len(phrases)-1)])
while True:
action = prompt()
print(action) #Debugging purposes
if action.find("switch") != -1:
if action.find("light") != -1:
second_room() #Story continues
else:
action_error("What do you want to switch?")
else:
action_error()
So no return value for action_error() and direct assignment at the beginning of the while loop.
How about, in the case of a partially entered compound action, you concatenate the new input to the old one? Then "switch" becomes "switch light", and both of your conditionals will pass.
action = prompt()
while True:
print(action) #Debugging purposes
if action.find("switch") != -1:
if action.find("light") != -1:
second_room() #Story continues
else:
action = action + " " + action_error("What do you want to switch?")
continue
action = action_error()
Bonus style suggestions:
replace a.find("b") != -1 with "b" in a
use random.choice(phrases) instead of phrases[randint(1, len(phrases)-1)]

New line after x pixels when adding to a label (tkinter)

Currently when I write to a label, it will write only one line, and this sometimes disappears off the edge of the label, like so:
Here is an example of the code I am using to write to the label:
def updateLabel(self, event):
global string
global labelContents
global windowCommand
global currentEnvironment
if event != "no input":
windowCommand = self.getEntry(event)
labelDisplay = "> " + windowCommand
labelContents += labelDisplay
labelContents += "\n"
self.checkLabel()
string.set(labelContents)
self.textEntry.delete(0, END)
self.master.after(0, play)
else:
self.checkLabel()
string.set(labelContents)
labelContents += "You have died. Game over." + "\n"
labelContents += "You scored {0}.".format(score) + "\n"
app.updateLabel("no input")
I would like to know if there was any way to force it to a new line after a certain amount of pixels (the label width) without having to go through and add "\n" everywhere (as that last line is 1 of ~150 possibilities).
Label widget has a perfect option for that: wraplengt.
label = Label(parent, text="This is a really long text; " * 5, wraplengt=200)
From the Label's documentation on effbot.org:
Determines when a label’s text should be wrapped into multiple lines. This is given in screen units. Default is 0 (no wrapping).

Categories

Resources