Timer for python functions - python

I'm trying to do an automatic mouse clicker. My main issue now is how to do a timer for each function, for example, function 1 works for about 15 minutes, then function 2 after the 15 minutes starts to work just one time, then comeback to function 1. And i want to function4 Works independente from the others, i want it to click everytime even if the function 1 is running ( im not sure if this is possible) here is my code:
import pyautogui, sys
pyautogui.size()
(1280, 800)
def function1():
pyautogui.click(button='left', x=619, y=266)
pyautogui.PAUSE = 3.9
pyautogui.click(button='left', x=617, y=475)
def function2():
pyautogui.click(button='left', x=624, y=347)
pyautogui.PAUSE = 5.0
pyautogui.click(button='left', x=615, y=431)
pyautogui.PAUSE = 5.0
pyautogui.click(button='left', x=315, y=483)
pyautogui.PAUSE = 5.0
pyautogui.click(button='left', x=616, y=390)
def function3 ():
pyautogui.click(button='left', x=617, y=522)
pyautogui.PAUSE = 5.0
def function4():
pyautogui.click(button='left', x=1257, y=432)
Thank you all :)

Because it is not easy to manage multiple functions with wait independently without introducing a signal and multi-thread, here is another way to manage multiple click() functions with the PyAutoGUI library.
The solution is a multi-sequencer class (called class TimerExec).
Step 1 - Use a class TimerSeq to store sequencer parameters
Only the constructor is needed
class TimerSeq:
def __init__(self, iseq, tseq, bexe, bloop):
self.iseq = iseq
self.tseq = tseq
self.bexe = bexe
self.bloop = bloop
Step 2 - Create a class TimerExec to manage a list of sequencers
The constructor create an empty list
class TimerExec:
def __init__(self):
self.list = [ ]
self.stop = True
self.chrono = -1
self.delay = -1
A simple floating-point seconds value to int milliseconds
#
# convert float seconds to milliseconds
def tomilli(self, fsec):
return int(round(fsec * 1000))
Add a new sequencer in the list
#
# append new sequences to the list
def append(self, func, loop=False):
self.list.append([func, TimerSeq(-1, -1, False, loop)])
print('list:',self.list)
Verify if the sequencer is complete or restart when loop
#
# check end of sequence or restart
def nextcheck(self, seq):
if seq[1].iseq >= len(seq[0]):
if seq[1].bloop:
seq[1].iseq = 0 # restart
return True
return False
Compute parameters for the next sequence in the current sequencer
#
# switch to the next sequence
def nextstep(self, seq):
if seq[1].iseq >= len(seq[0]):
return True
seq[1].iseq = seq[1].iseq+1
seq[1].tseq = self.tomilli(time.time())
seq[1].bexe = False
return False
Manage the current sequencer and execute the current function then
delay the next sequence
#
# explore sequence and execute when
def exestep(self, seq):
bseq = False
if seq[1].tseq < 0:
bseq = self.nextstep(seq)
else:
bseq = self.nextcheck(seq)
if bseq:
return True
pseq = seq[0][seq[1].iseq]
tnow = self.tomilli(time.time())
tdel = self.tomilli(pseq[0])
if seq[1].bexe == False:
print('execute(%d):'% (tnow-self.chrono),pseq)
# execute the selected function
pseq[1](pseq[2],pseq[3],pseq[4])
seq[1].bexe = True
tseq = seq[1].tseq
if tnow > (tseq+tdel):
bseq = self.nextstep(seq)
return bseq
Main loop function to explore all sequencers until complete or
max_delay
#
# loop to execute all sequences with max_delay (s)
def execute(self, max_delay):
print('start:',time.strftime("%H:%M:%S", time.localtime()))
self.stop = False
self.delay = self.tomilli(max_delay)
self.chrono = self.tomilli(time.time())
while self.stop == False:
tnow = self.tomilli(time.time())
#if tnow > (self.chrono + self.delay):
# break
bstop = True
for seq in self.list:
bseq = self.exestep(seq)
bstop = bstop & bseq
if bstop == True:
self.stop = True
print('stop:',time.strftime("%H:%M:%S", time.localtime()),
((tnow-self.chrono)/1000.0))
Step 3 - declare your sequencer based to your declared function
For function1(), use a 2 steps sequencer:
def function1():
pyautogui.click(button='left', x=619, y=266)
pyautogui.PAUSE = 3.9
pyautogui.click(button='left', x=617, y=475)
The sequencer is:
fct1 = [
# pyautogui.click(button='left', x=619, y=266)
[ 3.9, pyautogui.click, 'left', 619, 266 ],
# pyautogui.click(button='left', x=617, y=475)
[ 0.0, pyautogui.click, 'left', 617, 475 ]
]
Step 4 - Create TimerExec object, add sequencer then execute.
The duration is limited to 13.6 seconds maximum
tSeq = TimerExec()
tSeq.append(fct1)
tSeq.execute(13.6)

Related

Python class scope lost in nested recursion function

When I attempted to write a recursive nested function within a python class, every time the recursive function completed and returned back to the first function my class property reverted back to the original state.
def main():
input = [
[1,3,1],
[1,5,1],
[4,2,1]
]
sln = Solution()
sln.findPath(input)
print("Output: " + str(sln.minPathSum))
print(*sln.minPath, sep = "->")
class Solution():
minPath = []
minPathSum = None
grid = []
def findPath(self, grid):
self.minPath = []
self.minPathSum = None
self.grid = grid
self.recurse(0,0,[])
def recurse(self, xCoord, yCoord, currentPath):
if(len(self.grid) <= yCoord):
return
if(len(self.grid[yCoord]) <= xCoord):
return
currentValue = self.grid[yCoord][xCoord]
currentPath.append(currentValue)
if(len(self.grid) - 1 == yCoord and len(self.grid[yCoord]) - 1 == xCoord):
currentPathSum = sum(currentPath)
if(self.minPathSum == None or currentPathSum < self.minPathSum):
self.minPathSum = currentPathSum
self.minPath = currentPath
else:
#right
self.recurse(xCoord + 1, yCoord, currentPath)
#down
self.recurse(xCoord, yCoord + 1, currentPath)
currentPath.pop()
return
if __name__ == "__main__":
main()
Running this results in:
Output: 7
Debugging the code within VSCode does indicate that self.minPath is getting set within the recurse function; however, it appears to be losing scope to the original class instance.
In addition I attempted to recreate the same nested situation with separate code and ended up with the following.
def main():
tst = ScopeTest()
tst.run()
print(*tst.tstArray)
print(tst.tstNumber)
class ScopeTest():
tstArray = []
tstNumber = 0
def run(self):
self.otherFunc()
def otherFunc(self):
self.tstArray = [1,2,3,4,5]
self.tstNumber = 7
if __name__ == "__main__":
main()
The above did return the expected outcome which leads me to think this has something to do with the recursion.
Just a heads up, I'm fairly new to python so I may be making a rookie mistake, but I can't seem to figure out why this is happening.
Your recurse() method is generating a currentPath parameter and when this is deemed to be correct you execute: self.minPath = currentPath. Unfortunately, you just reference the same object as currentPath which is later mutated.
Your line should be: self.minPath = currentPath[:]
You then see some contents in self.minPath
Mandatory link to Ned Batchelder
Also you can remove these lines:
minPath = []
minPathSum = None
grid = []
from just below class Solution():

Kivy clocks issue(something doubles every time)

So my problem is that whenever i create a new game by create_game() method something strange happens and everything starts to move 2 times faster i think it is due to a Clock issue but i am not sure. My question is what can i do to make it run in normal pace. Here is code:
def create_game(self):
for i in range(1,11):
for j in range(15):
brick = Brick(pos = [self.width/15*j,self.height - self.height/30*i - self.height/6], size_hint = [1/15,1/30] )
a = random.randint(1,3)
if(a!=3):
self.brick_container.add_widget(brick)
ball = Ball(pos = (self.paddle.center_x, self.paddle.height + 1), size_hint = [None,None], size = [self.width/50,self.width/50])
self.ball_container.add_widget(ball)
self.game_on_pause = True
self.update()
def serve_ball(self):
self.ball_container.children[0].velocity = Vector(3,3)
if(len(self.ball_container.children) == 0):
self.level += 1
self.brick_container.clear_widgets()
self.ball_container.clear_widgets()
self.bonus_container.clear_widgets()
Clock.unschedule(self.update)
self.create_game()
Clock.schedule_once(self.update, 1.0 / 60.0)
Save the Clock.schedule_once to a variable, event and replace Clock.unschedule(self.update) with self.event.cancel()
if(len(self.ball_container.children) == 0):
...
self.bonus_container.clear_widgets()
self.event.cancel()
self.create_game()
self.event = Clock.schedule_once(self.update, 1.0 / 60.0)
Note
An alternative is to use Trigger events.
Each time you call trigger(), it will schedule a single call of your
callback. If it was already scheduled, it will not be rescheduled.
self.trigger = Clock.create_trigger(my_callback, 1.0 / 60.0)
self.trigger() # schedule it
self.trigger.cancel() # cancel / unschedule it

Python - returning from a While Loop after its end

I am trying to code a menu routine for an alarm-clock in python, which displays the relevant information on a 7-segment display according to some inputs via pushbuttons.
I managed to create a loop which displays the current time, and whe the button "debMenu" is clicked 3 menu options are displayed. This works well only for the first time untill the 3rd. menu option is reached. When I push the button again the routine does not work - so the function "main_menu" is not called again.
What I am doing wrong...? Thanks !!
stop_loop = [False]
def main_menu(stop_loop, n=[0]):
stop_loop[0] = True
debSelect = DebouncedSwitch(butSelect, cbButSelect, "butSelect")
menuList = ['HO ', 'AL ', 'UP ']
if n[0] < 3:
display.scroll(menuList[n[0]], 200) #display menu on 7-seg display
display.show(menuList[n[0]])
n[0] += 1
elif n[0] == 3:
n=[0]
stop_loop[0] = False
main()
def main():
stop_loop[0] = False
debMenu = DebouncedSwitch(butMenu, main_menu, stop_loop)
while not stop_loop[0]: # display current time on 7-seg display
curTime = rtc.datetime()
display.numbers(curTime.hour, curTime.minute, False)
time.sleep_ms(500)
display.numbers(curTime.hour, curTime.minute)
time.sleep_ms(500)
main()
I guess the default value mutable variable is the one killing you. Check here: http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments.
I don't even understand why you need a list variable in this case, why not just n=0 without having to use a list?.
maybe make a new global variable or class to encapsulate both state-related variables (stop_loop and n). Like:
loop = dict(stop=False, n=0)
now you pass this instead of stop_loop to main_menu.
Or use a class that encapsulates a circular array for menu like:
class LoopMenu:
""" circular array menu list with pause/continue/next"""
def __init__(self, menu):
self.menu = menu
self.stop = False
self.n = 0
def next(self):
menu = self.menu[self.n]
self.n += 1
if self.n > len(self.menu):
self.n = 0
return menu
def pause(self):
self.stop = True
def play(self):
self.stop = False
then you main_menu method could be:
my_menu_loop = LoopMenu(['HO ', 'AL ', 'UP '])
def main_menu(menu_loop):
menu_loop.pause()
debSelect = DebouncedSwitch(butSelect, cbButSelect, "butSelect")
menu = menu_loop.next()
display.scroll(menu, 200) #display menu on 7-seg display
display.show(menu)
main()
def main():
my_menu_loop.play()
debMenu = DebouncedSwitch(butMenu, main_menu, my_menu_loop)
while not loop_menu.stop: # display current time on 7-seg display
curTime = rtc.datetime()
display.numbers(curTime.hour, curTime.minute, False)
time.sleep_ms(500)
display.numbers(curTime.hour, curTime.minute)
time.sleep_ms(500)
main()

Python Tkinter preventing button command from executing

I'm trying to get the last few lines of the following code to have when one of the original 6 buttons is pressed to call the appropriate function to rename the buttons. I've tried changing the command line to buttons[0].command = Pistols(). I've also tried using a if loop with a variable such as x == 1 to determine that if the button is pressed x with then be 1 and the for loop will call the function Pistols, but with no success. However the button automatically calls the function and renames the first button to ".44 Pistol" rather than what it should be "Pistols". I wan't the command to only be executed and call the function when pressed. I know that tkinter will automatically look to the function being called and run it's code. How can I either delay this or go about this in another way to have the functions code only execute when pressed. Thanks in advance!
from tkinter import *
buttons = []
clm = [1,2,1,2,1,2]
rw = [1,1,2,2,3,3]
btnmain_list = ['Pistol','Rifle','Assult Rifle','Submachine Gun','Heavy Weapon','Plasma Weapons']
btnpistol_list = ['.44 Pistol', '10mm Pistol', 'Pipe Bolt-Action Pistol','Flare Gun', 'Pipe Pistol', 'Pipe Revolver']
btnrifle_list = []
btnasrifle_list = []
btnsubgun_list = []
btnheavy_list = []
btnplasma_list = []
ms = Tk()
ms.title('Fallout 4 weapon mods and needed materials')
ms.geometry('450x400')
placement = Frame(ms)
placement.grid()
class Guns:
def Pistols ():
buttons[0] = Button(placement,height = '5',width = '20', text = btnpistol_list[0])
buttons[0].grid(column = clm[0], row = rw[0])
def Rifles ():
x = 0
def AssultRifles ():
x = 0
def SubmachineGuns ():
x = 0
def HeavyWeapons ():
x = 0
def PlasmaWeapons ():
x = 0
for i in range (6):
b = Button(placement,height = '5',width = '20', text = btnmain_list[i])
b.grid(column = clm[i], row = rw[i])
buttons.append(b)
buttons[0].command = Pistols()
I've found a solution by changing the class to this:
class Guns:
global counter
counter = 0
def pistolCycle():
global counter
buttons[0].config(text=btnpistol_list[counter])
if counter == len(btnpistol_list)-1:
counter=0
counter = counter+1
def Pistols ():
buttons[0] = Button(placement, height = '5',width = '20', text="Pistols", command = lambda: Guns.pistolCycle() )
buttons[0].grid(column = clm[0], row = rw[0])
def Rifles ():
x = 0
def AssultRifles ():
x = 0
def SubmachineGuns ():
x = 0
def HeavyWeapons ():
x = 0
def PlasmaWeapons ():
x = 0
for i in range (6):
b = Button(placement,height = '5',width = '20', text = btnmain_list[i])
b.grid(column = clm[i], row = rw[i])
buttons.append(b)
Pistols()
So, here's a breakdown of what happens:
Once your buttons are defined, the Pistol function is called, which adds all the features to your Pistol button, including changing the text, and adding the function it will call when pressed.
When the button is pressed, it calls to pistolCycle. What pistol cycle does, is takes the "counter" value, and changes the text of the button to the item in the list which is associated to it. EG, when the counter is 0, .44 Pistol is displayed.
The counter increases by one, each time pistolCycle is called, meaning the next time it's called, it will display the next item in the list.
Now, using global variables can get messy. I've given you the basic framework, so you may be able to use your own logic to get the variable "counter" to pass into pistolCycle each time (EG, pistolCycle(counter))
You will need to make a separate counter and cycle function in order for all the buttons to work.
I hope this helped!!
PS: The if statement in the pistolCycle function means that it wont try and get an item when it doesn't exist in the list.

Keeping state in a constantly running python script

I have a python 2.7 script which is getting pretty unweildly.
The script runs constantly, in each loop it checks a few things, compares them to the previous run and makes some decisions.
The biggest problem I have is with the variables, I have half a dozen and I am limited in how I use them due to scope.
eg;
import time
import os
LOG_FILE = "/var/log/sc.log"
CHECK_FILE_TMP = "/tmp/myfile"
CHECK_FILE_USR = "/home/snoppy/testfile"
CHECK_FILE_TMP_TIME_INTERVAL = 20
CHECK_FILE_USR_TIME_INTERVAL = 30
def main():
try:
last_file_tmp_size = 0
last_file_usr_size = 0
last_file_tmp_mtime = 0
last_file_usr_mtime = 0
last_file_tmp_check_time = 0
last_file_usr_check_time = 0
tmp_file_changed = False
usr_file_changed = False
loop_start_time = 0
print "Starting loop"
while True:
loop_start_time = time.time()
if (time.time() - last_file_tmp_check_time > CHECK_FILE_TMP_TIME_INTERVAL):
tmp_file_changed = checkFileChanged(CHECK_FILE_TMP, last_file_tmp_size, last_file_tmp_mtime)
last_file_tmp_size = getFileSize(CHECK_FILE_TMP)
last_file_tmp_mtime = getFileMTime(CHECK_FILE_TMP)
if(tmp_file_changed):
logChange(CHECK_FILE_TMP, last_file_tmp_size, last_file_tmp_mtime)
last_file_tmp_check_time = time.time()
....
....
sleep(1)
...
So thats sort of what I am dealing with.
I have local variables, which I seem to be stuck with, I have to pass them around into functions - I don't want to call them global.
Ideally.... if I could get a main() function like
try:
checkFile(CHECK_FILE_TMP)
checkFile(CHECK_FILE_USR)
sleep(0.1)
except:
...
as the main is so big! and I have to pass around the variables everywhere... it feels like the main function is so bloated!
Perhaps.... I might have to go for a class?
You need to identify the parts of your program that can be abstracted from their specific details and reused multiple times.
This is an art, don't expect it to be easy. You can find many books on Object oriented programming that explain the basics and give you some direction.
Here's just a quick example:
class FileChecker(object):
def __init__(self, path, interval):
self.path = path
self.interval = interval
self.last_size = None
self.last_mtime = None
self.last_check_time = None
def hasChanged(self):
...
def logChange(self):
...
class MultiFileChecker(object):
DELAY = 1
def __init__(self):
self.files = []
self.loop_start_time = 0
def addFile(self, f):
self.files.append(f)
def loop(self):
try:
print "Starting loop"
while True:
now = time.time()
self.loop_start_time = now
for f in self.files:
if now - f.last_check_time > f.interval:
if f.hasChanged():
f.logChange()
...
sleep(self.DELAY)
...
if __name__ == '__main__':
checker = MultiFileChecker()
checker.add(FileChecker("/var/log/sc.log", 10))
checker.add(FileChecker("/tmp/myfile", 20))
checker.add(FileChecker("/home/snoppy/testfile", 30))
checker.loop()

Categories