Stop running function once mouse key is released - python

I have a function that runs when the mouse key is pressed down. I would like to cancel it once it's released. How would I do that using pynput? For some assistance, here is the code I used to do this but it does not work as it waits for the on_click function to finish or that is my guess.
running = False
i = 0
f = 4
delta_x = [1,2,3]
delta_y = [3,2,1]
def wasd(l):
global f
f = f + l
print(f)
if (f == 5):
return True
else:
return False
def logging_mouse(running, i):
while (running and i < len(delta_x)):
print(delta_x[i],delta_y[i])
if wasd(0) == True: break
i = i+1
running = False
def on_click(*args):
global running
global i
print(running)
i = args[3]
if args[-1]:
if not running:
running = True
threading.Thread(target=logging_mouse(running,i)).start()
else:
running = False
wasd(1)
f = 4
i = 0
with Listener(on_release=wasd(1),on_click=lambda event1,event2,event3,event4: on_click(event1,event2,event3,i,event4)) as listener:
listener.join()

while True:
def on_press_start(*args):
if args[-1]:
return False
def on_press_loop(*args):
if not args[-1]:
return False
i = 0
with Listener(on_click=on_press_start) as listener:
listener.join()
with Listener(on_click=on_press_loop) as listener:
for i in range(len(delta_x)):
print(delta_x[i],delta_y[i])
if not listener.running:
break
print(i)

Related

how to record between key input delay?

When I run the code below, I have noticed that what key is entered is recorded.
But there is one more thing I want.
That's for example, assuming I typed a and b, I want the time between a and b to be recorded between the keys as well.
import pynput, time
from pynput.keyboard import Key, Listener
count = 0
keys = []
def on_press(key):
global keys, count
keys.append(key)
count += 1
print("{0} pressed".format(key))
if count >= 10:
count = 0
write_file(keys)
keys = []
def write_file(keys):
with open("log.txt", "w") as f:
for key in keys:
k = str(key).replace("'", "")
if k.find("space") > 0:
f.write('\n')
elif k.find("Key") == -1:
f.write(k)
def on_release(key):
if key == Key.esc:
return False
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
I sloved question.
Thanks for everyone.
# From: https://github.com/moses-palmer/pynput
from pynput.keyboard import Key, Listener
import logging
import keyboard, sys, os
import time
on_press_previous = 0
log_dir = ""
os.remove(log_dir + 'key_log.txt')
logging.basicConfig(filename=(log_dir + "key_log.txt"), level=logging.DEBUG, format='%(message)s')
def on_press(key):
global on_press_previous
on_press_current = time.time()
delta = round(on_press_current - on_press_previous, 3)
on_press_previous = on_press_current
logging.info('{0}'.format(key))
logging.info('{0}'.format(delta))
if keyboard.is_pressed("F7"):
sys.exit()
with Listener(on_press=on_press) as listener:
listener.join()
Here is a Timer class I use to count / record time when writing programs:
class Timer:
def __init__(self):
self.start_time = None
self.on = False
def start(self):
# start counting
self.start_time = time.time()
self.on = True
def value(self):
# --- return current value ---
if self.on:
return time.time() - self.start_time
return 0
def tostr(self):
# --- return value as {min:sec} format ---
val = math.floor(self.value())
mins = '0' + str(int(val / 60)) if len(str(int(val / 60))) == 1 else str(int(val / 60))
secs = '0' + str(val % 60) if len(str(val % 60)) == 1 else str(val % 60)
return "{}:{}".format(mins, secs)
def stop(self):
# --- stop counting ---
self.__init__()
Use timeit module (import timeit):
Put this two lines in on_press:
print("The time difference is :", timeit.default_timer() - start)
start = timeit.default_timer()
Now it will print out time difference between two button clicks:
count = 0
keys = []
start = timeit.default_timer()
def on_press(key):
global keys, count, start
...
...
Also you will have to declare new global variable start.
You can track the time for the on_press event, store it, and then calculated the difference. Here I am using a global variable just for simplicity and consistency with your existing code. Library function time.time_ns() should give plenty of resolution.
import pynput, time
from pynput.keyboard import Key, Listener
count = 0
keys = []
on_press_previous = 0
def on_press(key):
global keys, count, on_press_previous
# current timestamp in nanoseconds
on_press_current = time.time_ns()
# calculate delta from previous on_press event
delta = on_press_current - on_press_previous
# store the current timestamp for the next round
on_press_previous = on_press_current
keys.append((key,delta))
count += 1
print("{} pressed {} nanoseconds from previous".format(key, delta))
if count >= 10:
count = 0
write_file(keys)
keys = []
def write_file(keys):
with open("log.txt", "w") as f:
for key,delta in keys:
k = str(key).replace("'", "")
if k.find("space") > 0:
f.write('\n')
elif k.find("Key") == -1:
f.write(k)
f.write(str(delta))
def on_release(key):
if key == Key.esc:
return False
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()

Python keyboard module not working properly inside loop

I'm trying to create a simple game that creates math problems and the users task is to decide if they are true or false. (eg. 2 + 2 = 6, True or False?)
I am using the keyboard module and I want to have the user press the left arrow key if he thinks that the problem is true, and the right one if he thinks that it's false.
import random
import keyboard
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
while True:
if keyboard.is_pressed('left'):
user_answer = True
break
elif keyboard.is_pressed('right'):
user_answer = False
break
if user_answer == answer_correct:
return True
else:
return False
The thing is, after I paste this function into a loop, I can only press left or right once. After that the rest of the code is executed without waiting for my keypress.
from problems import addition_easy
exercise_amount = int(input("How many exercises would you like to solve?"))
for exercise in range(1, exercise_amount + 1):
addition_easy()
This returns (for input of 5):
How many exercises would you like to solve? 5
6 + 1 = 9
True or False? //(Here it waits for me to press "left" or "right")
3 + 3 = 8
True or False? //(From here it doesn't stop to wait for a keypress)
4 + 3 = 7
True or False? //(Same here and so on...)
2 + 3 = 3
True or False?
1 + 2 = 3
True or False?
How can I make it wait for a keypress every time it prints out a math problem?
If the user holds down "left" for half a second, and addition_easy executes a hundred times in that half second, then keyboard.is_pressed('left') will evaluate to True for every one of them, even though the user only pressed "left" once.
You can verify that is_pressed doesn't permanently consider "left" to be pressed by telling your program to do 1000 problems. Pressing left will only answer about 20 of them.
One possible solution is to alter your loop so it waits until the key is subsequently released before continuing.
while True:
if keyboard.is_pressed('left'):
user_answer = True
while keyboard.is_pressed("left"):
pass
break
elif keyboard.is_pressed('right'):
user_answer = False
while keyboard.is_pressed("right"):
pass
break
Another possible design is to use keyboard.on_press_key, which should only fire when the key changes state from "not pressed" to "pressed" (or when the auto repeat time elapses, which probably won't happen unless the user is doing it intentionally). You can abstract this out to a function to keep your addition_easy function clean:
import random
import keyboard
import time
def wait_for_keys(keys):
key_pressed = None
def key_press_event(key):
nonlocal key_pressed
key_pressed = key.name
for key in keys:
keyboard.on_press_key(key, key_press_event)
while key_pressed is None:
time.sleep(0.01)
return key_pressed
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
key = wait_for_keys(["left", "right"])
user_answer = (key == "left")
if user_answer == answer_correct:
return True
else:
return False
exercise_amount = 1000
for exercise in range(1, exercise_amount + 1):
addition_easy()
Not sure if you indented your function correctly. Try:
import random
import keyboard
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
while True:
if keyboard.is_pressed('left'):
user_answer = True
break
elif keyboard.is_pressed('right'):
user_answer = False
break
if user_answer == answer_correct:
return True
else:
return False
exercise_amount = int(input("How many exercises would you like to solve?"))
for exercise in range(1, exercise_amount + 1):
addition_easy()

Micro:bit classes instead of global variables - memory allocation error in micropython

I've just created my first little truth or dare/spin the bottle game for a senior/high school coding club on the Micro:bit. I would like to introduce using oop/classes/objects instead of (dreaded) global vars. The game works great on emulators such as https://create.withcode.uk/, but on the Micro:bit itself I encounter memory allocation errors as soon as I try to put pretty much anything into classes. Is the microbit's 16KB of RAM not enough? Or am I declaring classes incorrectly?
Coming from front-end and a bit of PHP/SQL, I'm a Python/memory knowledge noob so any help is appreciated.
If I use global vars in each of the functions it works without issue.
Here is the code:
from microbit import *
import random
#timer current function
#Global variables
class game:
gsTime = 3000
timerPrev = 0
numOfPlayers = 0
maxPlayers = 8
stage = 'start'
minSpinTime = 3000
class player:
#Which player is currently selected
selected = 0
#Player position display
def pos1(): display.set_pixel(2, 4, 9)
def pos2(): display.set_pixel(2, 0, 9)
def pos3(): display.set_pixel(0, 2, 9)
def pos4(): display.set_pixel(4, 2, 9)
def pos5(): display.set_pixel(0, 4, 9)
def pos6(): display.set_pixel(4, 0, 9)
def pos7(): display.set_pixel(0, 0, 9)
def pos8(): display.set_pixel(4, 4, 9)
#Array of all player positions
positions = [
[pos1, 1, Image.ARROW_S],
[pos2, 5, Image.ARROW_N],
[pos3, 3, Image.ARROW_W],
[pos4, 7, Image.ARROW_E],
[pos5, 2, Image.ARROW_SW],
[pos6, 6, Image.ARROW_NE],
[pos7, 4, Image.ARROW_NW],
[pos8, 8, Image.ARROW_SE]
]
positionsOrdered = []
class buttons:
pressed = False
class spinner:
completeSpins = 0
isCompleteSpin = False
rdTime = 0
stage = 'start'
stageStarted = False
gameCall = game()
playerCall = player()
buttonsCall = buttons()
spinnerCall = spinner()
#Return a random range of numbers
def rdRange(minMult,maxMult,base):
return random.randint(base*minMult, base*maxMult)
#return sort key of list
def getKey(item):
return item[1]
#Timer function
def timer(timeElapsed, onCompleteFunc):
if running_time() - gameCall.timerPrev >= timeElapsed:
onCompleteFunc()
#Position Players Start is true
def positionPlayersStartTrue():
game.stage = 'positionPlayers'
def selectNumOfPlayers(gteOrLte):
game.timerPrev = running_time()
if gteOrLte == 'gte':
if gameCall.numOfPlayers >= gameCall.maxPlayers:
game.numOfPlayers = 1
else:
game.numOfPlayers += 1
else:
if gameCall.numOfPlayers <= 1:
game.numOfPlayers = maxPlayers
else:
game.numOfPlayers -= 1
display.show(str(gameCall.numOfPlayers)) #Have to convert int to string before passing to display.show func
buttons.pressed = True
#Ask for number of players up to maxPlayers.
def setPlayerNum():
#If B is pressed increment by 1 up the max players and cycle back to 1
if button_b.was_pressed():
selectNumOfPlayers('gte')
#If A is pressed decrement by 1 down to 1 and then cycle back to maxPlayers var
elif button_a.was_pressed():
selectNumOfPlayers('lte')
elif buttonsCall.pressed == False:
#Ask how many players
display.show('#?')
else:
timer(gameCall.gsTime, positionPlayersStartTrue)
#display the position of players
def positionPlayers():
buttons.pressed = False
display.clear()
for i in range(gameCall.numOfPlayers):
el = player.positions[i]
player.positionsOrdered.append(el)
el[0]()
player.positionsOrdered.sort(key=getKey)
while buttonsCall.pressed == False:
startSpin()
#start the spin - useful for starting the spin after one spin was complete too
def startSpin():
if button_a.was_pressed() or button_b.was_pressed():
buttons.pressed = True
game.stage = 'spin'
#Spin start
def spin():
if spinnerCall.stage == 'start' and spinnerCall.stageStarted == False:
game.timerPrev = running_time()
spinner.rdTime = rdRange(200, 700, gameCall.numOfPlayers)
spinner.stageStarted = True
print(spinner.rdTime)
for i in range(gameCall.numOfPlayers):
display.clear()
el = player.positionsOrdered[i]
el[0]()
if i + 1 == gameCall.numOfPlayers:
spinner.completeSpins += 1
spinner.isCompleteSpin = True
else:
spinner.isCompleteSpin = False
if spinnerCall.stage == 'start':
if (running_time() - gameCall.timerPrev >= (gameCall.minSpinTime + spinnerCall.rdTime)) and (spinnerCall.isCompleteSpin == True):
spinner.stage = 'slow'
spinner.stageStarted = False
sleep(200)
#Slower spin to emulate spinner slowing down as it comes near to stopping. Should probably use some clever-er maths here instead.
elif spinner.stage == 'slow':
if spinnerCall.stageStarted == False:
game.timerPrev = running_time()
spinner.rdTime = rdRange(500, 900, gameCall.numOfPlayers)
spinner.stageStarted = True
print(spinnerCall.rdTime)
if running_time() - gameCall.timerPrev >= spinnerCall.rdTime:
spinner.stage = 'stop'
spinner.stageStarted = False
sleep(400)
elif spinner.stage == 'stop':
player.selected = i
game.stage = 'selectedPlayer'
# reset spinner stage for next spin
spinner.stage = 'start'
break
#Player has been selected
def selectedPlayer():
el = playerCall.positionsOrdered[playerCall.selected]
sleep(200)
display.show(el[2])
sleep(200)
display.clear()
while True:
#CALL FUNCTIONS
if gameCall.stage == 'start':
setPlayerNum()
elif gameCall.stage == 'positionPlayers' and buttonsCall.pressed == True:
positionPlayers()
elif gameCall.stage == 'spin':
spin()
elif gameCall.stage == 'selectedPlayer':
#print('this one is selected ', playerCall.selected)
selectedPlayer()
#start spin again if button is pressed
startSpin()
Your code is too big for microbit. Microbit is limited by 16KB of RAM. To decrease size of your code you can:
minify it directly from Mu editor or use any other minifier lib
Shrink variable names
Delete comments

Program keeps running - infinite loop

I've been creating a combination calculator, something I'm having a hard time creating. A problem I'm constantly trying to fix is dealing with any infinite loops in my code.
oglist = ["a","b","c","d"]
combocounter = 3
lists = {}
comboloop = True
combolist = ["lol"]
pendinglist = ["lol"]
for x in range(0, combocounter):
lists["list" + str(x)] = ["lol"]
def loop(counter1):
global recursion1
global recursion2
if len(lists["list" + str(counter1)]) == 0:
lists["list" + str(counter1 - 1)] = lists["list" + str(counter1 - 1)][1:]
print(lists)
recursion1 = True
recursion2 = True
else:
lists["list" + str(counter1 + 1)] = lists["list" + str(counter1)][1:]
print(lists)
recursion2 = False
return
def startingloop():
global recursion1
if len(lists["list0"]) == 0:
comboloop = False
else:
lists["list1"] = lists["list0"][1:]
print(lists)
recursion1 = False
return
def endingloop():
global counter2
global recursion2
if len(lists["list2"]) == 0:
lists["list1"] = lists["list1"][1:]
print(lists)
recursion2 = True
else:
combolist[counter2] = lists["list0"][0]
for y in range(1, combocounter):
combolist[counter2] = combolist[counter2] + lists["list" + str(y)][0]
combolist.append("lol")
lists["list2"] = lists["list2"][1:]
counter2 += 1
print(lists)
print(combolist)
return
lists["list0"] = oglist
counter2 = 0
while comboloop == True:
startingloop()
while recursion1 == False:
loop(1)
while recursion2 == False:
endingloop()
combolist.remove("lol")
print(combolist)
I've placed a bunch of print functions:
print(lists) and print(combolist).
When I run it, lists and combolist are constantly updated and printed. It then stops printing which is expected, but my program keeps running something. It also never reaches
combolist.remove("lol")
print(combolist)
I went through the effort of following the logic of my code to find any issues, but I didn't. What's constantly looping in my code?
comboloop = False is creating a local variable that shadows your global called comboloop. If you add
global comboloop
in your startingloop() function, the program exits

Python - Files wont download

Here's My code, all the urls are in a Config Parser format file. When the button is pressed files will not download. What did go wrong? I used urllib should I have used urllib2? Some of the functions may be there but not used just ignore that.
import wx
import ConfigParser
import urllib
def Download(url):
response = urllib.urlopen(url).read()
doned = wx.MessageDialog("Download Done")
doned.ShowModal()
doned.Destroy()
#First thing i gona do is Parse 98 box data
BoxParser = ConfigParser.RawConfigParser() #Set Raw
BoxParser.read("98.box") #Mount into 98.box
#Get Vars, and print them
WxTitle = BoxParser.get("meta_data","Window_title") #get the window title
Application = BoxParser.get("meta_data","Application") #Get app name
Desc = BoxParser.get("meta_data","Description") #Get the description of the app
Author = BoxParser.get("meta_data","Author") #Of course! I dont wanna be plagurized
Contact = BoxParser.get("meta_data","Contact_Info") #My Contact Info
Date = BoxParser.get("meta_data","Date") #Date when the current update was made
#UpdateUrl = BoxParser.get("meta_data","Update_url") #Url to update
#BoxUp = BoxParser.get("meta_data","Update_box") #Url to update 98.box
# Meta Data loaded
#time to load the firmwares
e660 = BoxParser.get("Firmware_links","660") #6.60
e6602 = False
e660g = BoxParser.get("Firmware_links","660go") #6.60 Go Eboot
e6602g = False
e639 = BoxParser.get("Firmware_links","639") #6.39
e6392 = False
e639g = BoxParser.get("Firmware_links","639go") #6.39 Go Eboot
e6392g = False
e635 = BoxParser.get("Firmware_links","635") #6.35
e6352 = False
e635g = BoxParser.get("Firmware_links","635go") #6.35 Go Eboot
e6352g = False
e620 = BoxParser.get("Firmware_links","620") #6.20
e550 = BoxParser.get("Firmware_links","550") #5.50
e5502 = False
e500 = BoxParser.get("Firmware_links","500") #5.00
e5002 = False
e401 = BoxParser.get("Firmware_links","401") #4.01
e4012 = False
#Firmwares Loaded
def BoxUpdate():
Download(Update_box)
#Check if DD equ true so we can post the MSG
if downloaddone == True:
Done2 = wx.MessageDialog(self,"Download Done, 98.Box Updated!")
Done2.ShowModal()
Done.Destroy()
#Time to get out Gui
class FrameClass(wx.Frame): #Finally making the gui!
def __init__(self,parent,title): #making init!
app = wx.Frame
app.__init__(self,parent,title=WxTitle,size = (340,280)) #set window size
Menu = wx.Menu() #Lets make a menu!
panel = wx.Panel(self) #set the panel var
contact = Menu.Append(wx.ID_NONE,"&Contact Info") #add update thing
self.Bind(wx.EVT_MENU,self.contact1,contact) #Add event for Update
fwMsg = wx.StaticText(panel, label='Firmware', pos=(59,25))
fwlist = wx.ComboBox(panel,pos=(118,22), choices=["6.60","6.60 Go/N1000","6.39","6.39 Go/N1000","6.35 Go/N1000","5.50","5.00","4.01"])
self.Bind(wx.EVT_COMBOBOX, self.getsel, fwlist)
downloadbutton = wx.Button(panel, label="Download FW", pos=(100,76))
self.Bind(wx.EVT_BUTTON, self.DLB, downloadbutton)
#now for the member!
TopM = wx.MenuBar()
TopM.Append(Menu, "Tool Opt")
self.SetMenuBar(TopM)
self.Show(True)
def DLUpdate(self,e):
#Check if DD equ true so we can post the MSG
Download(Update_url)
print "downloading"
Done = wx.MessageDialog(self,"Download Done, download stored in \"DLBOXV$.zip\" file")
Done.ShowModal()
Done.Destroy()
def contact1(self,e):
con = wx.MessageDialog(self,Contact)
con.ShowModal()
con.Destroy()
def getsel(self,e):
i = e.GetString()
if i == "6.60":
e6602 = True
print e6602,"660"
else:
e6602 = False
print e6602,"660"
if i == "6.60 Go/N1000":
e6602g = True
print e6602g,"660 go"
else:
e6602g = False
print e6602g,"660 go"
if i == "6.39":
e6392 = True
print e6392,"639"
else:
e6392 = False
print e6392,"639"
if i == "6.39 Go/N1000":
e6392g = True
print e6392g,"639 go"
else:
e6392g = False
print e6392g,"639 go"
if i == "6.35 Go/N1000":
e6352g = False
print e6352g,"635 go"
else:
e6352g = False
print e6352g,"635 go"
if i == "5.50":
e5502 = True
print e5502,"550"
else:
e5502 = False
print e5502,"550"
if i == "500":
e5002 = True
print e5002,"500"
else:
e5002 = False
print e5002,"500"
if i == "401":
e4012 = True
print e4012,"401"
else:
e4012 = False
print e4012,"401"
def DLB(self,e):
if e6602 == True:
Download(e660)
elif e6602g == True:
Download(e660g)
elif e6392 == True:
Download(e639)
elif e639g == True:
Download(e639g)
elif e6352g == True:
Download(e635g)
elif e5502 == True:
Download(e550)
elif e5002 == True:
Download(e500)
elif e4012 == True:
Download(e401)
G = wx.App(False)
Win = FrameClass(None,WxTitle)
G.MainLoop()
But at the function Download(url) will not function, it will not download
def Download(url):
response = urllib.urlopen(url).read()
doned = wx.MessageDialog("Download Done")
doned.ShowModal()
doned.Destroy()
what triggers Download(url) is a few if and elsif statements
def getsel(self,e):
i = e.GetString()
if i == "6.60":
e6602 = True
print e6602,"660"
else:
e6602 = False
print e6602,"660"
if i == "6.60 Go/N1000":
e6602g = True
print e6602g,"660 go"
else:
e6602g = False
print e6602g,"660 go"
if i == "6.39":
e6392 = True
print e6392,"639"
else:
e6392 = False
print e6392,"639"
if i == "6.39 Go/N1000":
e6392g = True
print e6392g,"639 go"
else:
e6392g = False
print e6392g,"639 go"
if i == "6.35 Go/N1000":
e6352g = False
print e6352g,"635 go"
else:
e6352g = False
print e6352g,"635 go"
if i == "5.50":
e5502 = True
print e5502,"550"
else:
e5502 = False
print e5502,"550"
if i == "500":
e5002 = True
print e5002,"500"
else:
e5002 = False
print e5002,"500"
if i == "401":
e4012 = True
print e4012,"401"
else:
e4012 = False
print e4012,"401"
def DLB(self,e):
if e6602 == True:
Download(e660)
elif e6602g == True:
Download(e660g)
elif e6392 == True:
Download(e639)
elif e639g == True:
Download(e639g)
elif e6352g == True:
Download(e635g)
elif e5502 == True:
Download(e550)
elif e5002 == True:
Download(e500)
elif e4012 == True:
Download(e401)
What did go wrong? I used urllib should I have used urllib2?
Yes. The fact that just adding the 2 to your code fixes the problem is obviously proof of that, but it doesn't explain much.
As the docs for urllib.urlopen say:
Deprecated since version 2.6: The urlopen() function has been removed in Python 3 in favor of urllib2.urlopen().
This doesn't mean that they stopped fixing bugs in urllib as of 2.6 (there was a bug fix as late as 2.7.9), but it does mean that missing functionality will never be added. That includes, among other things, some kinds of HTTP authentication, redirections, HTTPS with non-standard CAs, proxy settings, and proxy authentication.
Without knowing anything at all about the problem besides "the download doesn't happen", or what URL you're trying to download (you seem to be passing in a variable named Update_box that isn't assigned anywhere), or the setup you're running on, it's impossible to know exactly which one of these problems (or any others) is the key.

Categories