I'm writing a photobooth program in Python on my Raspberry Pi 2 and it worked if I just want to take one picture or use auto focus once. I'm trying to write a function "scheduler" that waits 4 seconds then runs auto-focus, then waits 4 seconds to take picture 1, waits another 4 seconds for picture 2 and waits 4 seconds for picture 3 while updating a status bar in PyGTK. But when the "scheduler" function is called it goes through and starts all the while loops at the same time.
I tried putting the while loops in separate functions and running them as threads and using threadx.start(), threadx.join() commands under each thread call. I suspect the way gobject.timeout_add() function is preventing blocking, which I need to keep the GUI updated and the live preview running, but I also can't figure out how to get blocking when I need it. I also started scheduler as a thread and used time.sleep(1) in each of the while loops and it acts behaves how I want it to except the GUI disappears after the first image gets processed and I can't find any error message to explain it.
I've been working on this for weeks and would happily pay someone to help me get this finished via email. I live in a rural area and have had a very hard time finding someone locally to help. I can paypal... please contact me at derosia at the popular google email service. If you have a camera supported by gphoto2 and a usb tether that would be a bonus.
class Photobooth(gtk.Window):
def set_statusBar(self, status):
self.statusBar.label.set_text(str(status))
def focus_counter2(self, counter):
if counter > 3:
self.set_statusBar("Get into position!")
elif counter > 0:
self.set_statusBar("Focusing: " + str(counter-1))
else:
self.set_statusBar("Focusing: " + str(counter-1))
self.focus_function() #runs the function for auto focus
def countdown_function(self, counter, image_name):
if counter > 1:
self.set_statusBar(str(counter-1))
elif counter > 0:
self.set_statusBar("Keep Smiling!")
else:
self.photo_cb(image_name) #runs the function to take an image
def scheduler(self, widget, data = None):
global ftp_path
global image_names
global countdown_timer
image_names = []
counter = countdown_timer
while counter > 0:
gobject.timeout_add(counter * 1000, self.focus_counter2, countdown_timer-counter)
counter -= 1
counter = countdown_timer
while counter >= 0:
image_name = datetime.datetime.now().strftime("%Y_%m-%d_%H-%M-%S")
gobject.timeout_add(counter * 1000, self.countdown_function, countdown_timer-counter, image_name)
counter -= 1
image_names.append(image_name + ".jpg")
counter = countdown_timer
while counter >= 0:
image_name = datetime.datetime.now().strftime("%Y_%m-%d_%H-%M-%S")
gobject.timeout_add(counter * 1000, self.countdown_function, countdown_timer-counter, image_name)
counter -= 1
image_names.append(image_name + ".jpg")
counter = countdown_timer
while counter >= 0:
image_name = datetime.datetime.now().strftime("%Y_%m-%d_%H-%M-%S")
gobject.timeout_add(counter * 1000, self.countdown_function, countdown_timer-counter, image_name)
counter -= 1
image_names.append(image_name + ".jpg")
def __init__(self):
super(PictureBox, self).__init__()
startB_image = gtk.image_new_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_SMALL_TOOLBAR)
startB = gtk.Button()
startB.set_image(startB_image)
startB.set_tooltip_text("Start countdown")
startB.connect('clicked', self.scheduler, None)
Thanks to andlabs I solved this the way he suggested:
Call a function and have each function call the next one when they finished what they were doing. Then to make sure it looped through the proper number of times I just passed along a counter to each function that got iterated at the end of each loop, image_number. It seems to work.
class Photobooth(gtk.Window):
def set_statusBar(self, status):
self.statusBar.label.set_text(str(status))
def focus_counter1(self, widget, data=None):
global countdown_timer
counter = countdown_timer
while counter > 0:
gobject.timeout_add(counter * 1000, self.focus_counter2,
countdown_timer-counter)
counter -= 1
def focus_counter2(self, counter):
if counter > 3:
self.set_statusBar("Get into position!")
elif counter > 0:
self.set_statusBar("Focusing: " + str(counter))
else:
self.set_statusBar("Focusing: " + str(counter))
self.focus_function()
gobject.timeout_add(1000, self.countdown_clicked, 1)
def countdown_clicked(self, image_number):
global countdown_timer
counter = countdown_timer
while counter >= 0:
gobject.timeout_add(counter * 1000, self.countdown_function,
countdown_timer-counter, image_number)
counter -= 1
def countdown_function(self, counter, image_number):
global images_per_sequence
if counter > 1:
self.set_statusBar(str(counter-1))
elif counter > 0:
self.set_statusBar("Keep Smiling!")
else:
image_name = datetime.datetime.now()
self.photo_cb(image_name.strftime("%Y_%m-%d_%H-%M-%S"))
if image_number < images_per_sequence:
gobject.timeout_add(1000, self.countdown_clicked,
image_number + 1)
def __init__(self):
super(PictureBox, self).__init__()
startB_image = gtk.image_new_from_stock(gtk.STOCK_YES,
gtk.ICON_SIZE_SMALL_TOOLBAR)
startB = gtk.Button()
startB.set_image(startB_image)
startB.set_tooltip_text("Start countdown")
startB.connect('clicked', self.focus_counter1, None)
Related
I am trying to make a timer that counts down to 0, then starts counting up. I am using the time and keyboard modules.
The keyboard module from PyPi.
Everything works as expected, and I am able to press a button to close the program, but it only works at the beginning of each iteration. Is there a way for it to check for a key press at any point while the loop is running? Do I need to be using a different module?
This is my code:
import time
import keyboard
m = 2
s = 0
count_down = True
while True:
if keyboard.is_pressed('q'):
break
print(f"{m} minutes, {s} seconds")
if count_down:
if s == 0:
m -= 1
s = 60
s -= 1
elif not count_down:
s += 1
if s == 60:
m += 1
s = 0
if m == 0 and s == 0:
count_down = False
time.sleep(1)
Using callback is common approach in such case, here is solution:
import time
import keyboard
m = 2
s = 0
count_down = True
break_loop_flag = False
def handle_q_button():
print('q pressed')
global break_loop_flag
break_loop_flag = True
keyboard.add_hotkey('q', handle_q_button)
while True:
if break_loop_flag:
break
print(f"{m} minutes, {s} seconds")
if count_down:
if s == 0:
m -= 1q
s = 60
s -= 1
elif not count_down:
s += 1
if s == 60:
m += 1
s = 0
if m == 0 and s == 0:
count_down = False
time.sleep(1)
If you want to do any two things in parallel, independently of another, you need to consider using multiprocessing. However, even if you do, your loop will either still need to check if a key has been registered in the other process, or you need to terminate the process running the loop forcefully, which may result in unexpected outcomes.
However, in your case, since there are no side effects like files being written, this would work:
import time
import keyboard
from multiprocessing import Process
def print_loop():
m = 2
s = 0
count_down = True
while True:
print(f"{m} minutes, {s} seconds")
if count_down:
if s == 0:
m -= 1
s = 60
s -= 1
elif not count_down:
s += 1
if s == 60:
m += 1
s = 0
if m == 0 and s == 0:
count_down = False
time.sleep(1)
def main():
p = Process(target=print_loop)
p.start()
# this loop runs truly in parallel with the print loop, constantly checking
while True:
if keyboard.is_pressed('q'):
break
# force the print loop to stop immediately, without finishing the current iteration
p.kill()
if __name__ == '__main__':
main()
I am trying to run multiProcessing in python for the first time but when I debug in PyCharm I see that Thread 8 is waiting for a lock and I believe this is where my code is slowing down. I thought about mapping process pool in the while loop but this seems redundant to do this. Am i on the right track with this?
import random
import time
import math
import multiprocessing as mp # alias
# from multiprocessing.dummy import Pool as ThreadPool
# initial sequence:
# HHTHTHHHTHHHTHTH
coinChoices = ["H", "T"] # choices
idealFlip = "HHTHTHHH" # string to match
flip = "" # resets flip
margin_error = 0.1 # accuracy
num_matches = 0.0 # matched strings
probability = 0 # calc. probability
counter = 0 # iterations
flag = 1 # exit token
secondFlag = 1
check = math.ceil(pow(2, len(idealFlip))/2) # used for printing prob
# flips a coin *NOT USED*
def flip_coin(coins):
return str(random.choice(coins))
# requests (num) tasks to be completed
def flip_coin_num(num):
return str(random.choice(coinChoices))
# theoretical probability
def compute_probability():
size = len(idealFlip)
return math.pow(0.5, size)
# actual probability
def empirical_probability(count, num_flips):
return count / num_flips
# TODO: implement multiprocessing
if __name__ == "__main__":
# print("# cores: %d" % mp.cpu_count())
probability = compute_probability()
print("\nInitial probability of landing on the sequence: " + str(probability) + "\n")
actualProb = 0
empiricalProb = 0
tasks = range(len(idealFlip))
pool = mp.Pool(processes=4)
while flag != 0 or counter == 1000:
temp = pool.map(flip_coin_num, tasks)
# add other processes?
# handles close / join
flip = "".join(temp)
# print(temp)
# print(flip)
if counter != 0:
empiricalProb = empirical_probability(num_matches, counter)
if flip == idealFlip:
num_matches += 1
counter += 1
flip = ""
if counter % check is 0:
print("Probability" + str(empricalProb))
given the following code (in python):
import random
import sys
import collections
commandStr = ["addClan", "addPlayer",
"clanFight", "getMinClan"]
SUCCESS = "SUCCESS"
FAILURE = "FAILURE"
INVALID_INPUT = "INVALID_INPUT"
ClanID = dict()
playersID = dict()
ClanScore = collections.defaultdict(list)
playersClan = dict()
LosingClans = set()
Clans_count = 0;
players_count = 0;
def initline():
global ClanID, Clans_count
inLine = "init 2 0 1"
outLine = "init done."
ClanID[0] = {}
ClanID[1] = {}
Clans_count += 2
return inLine, outLine
# addClan clanID
def addClan():
global ClanID, Clans_count
clanID = random.randint(-2, 8)
inLine = "addClan %d" % (clanID)
outLine = "addClan: "
if clanID < 0:
outLine += INVALID_INPUT
elif clanID in ClanID.keys():
outLine += FAILURE
else:
ClanID[clanID] = {}
Clans_count += 1
outLine += SUCCESS
return inLine, outLine
# addPlayer playerID score clanID
def addPlayer():
global playersID, ClanID, ClanScore, playersClan, players_count
playerID = random.randint(-10, 1000)
score = random.randint(-10, 1000)
clanID = random.randint(-2, 8)
inLine = "addPlayer %d %d %d" % (playerID, score, clanID)
outLine = "addPlayer: "
if playerID < 0 or clanID < 0 or score <0:
outLine += INVALID_INPUT
elif playerID in playersID.keys() or clanID not in ClanID.keys():
outLine += FAILURE
else:
playersID[playerID] = playerID
if clanID in ClanScore.keys():
ClanScore[clanID].append(score)
else:
ClanScore[clanID] = [score]
playersClan[playerID] = clanID
ClanID[clanID][playerID] = (playersID[playerID], clanID)
players_count += 1
outLine += SUCCESS
return inLine, outLine
# getMinClan
def getMinClan():
global ClanID, LosingClans
inLine = "getMinClan"
outLine = "getMinClan: "
for Clan_id in sorted(ClanID):
if Clan_id not in LosingClans:
outLine += SUCCESS + " %d" % Clan_id
break
return inLine, outLine
def sum_n_strongest(Clan, num):
sortedClan = sorted(Clan, reverse=True)
topNum = sortedClan[:num]
Sum = 0
for element in topNum:
Sum += element
return Sum
# clanFight clan1 clan2 k1 k2
def clanFight():
global ClanID, ClanScore, LosingClans
clan1 = random.randint(-1, 8)
clan2 = random.randint(-1, 8)
k1 = random.randint(-1, 10)
k2 = random.randint(-1, 10)
inLine = "clanFight %d %d %d %d" % (clan1, clan2, k1, k2)
outLine = "clanFight: "
if k1 <= 0 or k2 <= 0 or clan1 < 0 or clan2 < 0:
outLine += INVALID_INPUT
elif clan1 not in ClanID.keys() or clan2 not in ClanID.keys() or clan1 == clan2 or len(ClanID[clan1]) < k1 or len(ClanID[clan2]) < k2:
outLine += FAILURE
elif clan1 in LosingClans or clan2 in LosingClans:
outLine += FAILURE
else:
sum1 = sum_n_strongest(ClanScore[clan1], k1)
sum2 = sum_n_strongest(ClanScore[clan2], k2)
if sum1 == sum2:
if clan1 < clan2:
LosingClans.add(clan2)
else:
LosingClans.add(clan1)
elif sum1 < sum2:
LosingClans.add(clan1)
else:
LosingClans.add(clan2)
outLine += SUCCESS
return inLine, outLine
def main():
if len(sys.argv) < 3:
print("Usage %s <lines>" % sys.argv[0])
exit(0)
lines = int(sys.argv[1])
infname = "%s.in" % sys.argv[2]
outfname = "%s.out" % sys.argv[2]
functions = [addClan, addPlayer,
getMinClan, clanFight]
with open(infname, 'wb') as infhandler:
with open(outfname, 'wb') as outfhandler:
inLine, outLine = initline()
infhandler.write(inLine + "\n")
outfhandler.write(outLine + "\n")
while lines > 0:
f = random.randint(0, len(functions)-1)
func = functions[f]
inLine, outLine = func()
if inLine is not "":
infhandler.write(inLine + "\n")
outfhandler.write(outLine + "\n")
lines -= 1
infhandler.write("quit\n")
outfhandler.write("quit done.\n")
main()
This code should create a new file while I need to give as parameters filename and numberOfLines.
But, while I trying to run this script, opened to me a black window (like a cmd) and it's closed immediately.
How can I fix it? someone know what is the problem?
Note: this code written me in a file which his name is "Test.py" , and I trying to run it with this line command: python ./Test.py <Number of Lines> <Test Name> (at least, I trying to do it while I double-click about "Test.py" but like that I said, the window closed immediately..
Follow-up question: Are you using an IDE, or are you trying to run this from the command line/command prompt?
First, check if the files exist after you run the program. It might just be that the program is running quickly enough that it only takes a couple milliseconds, and the command prompt goes away once the program terminates. If it does create a file, the program wouldn't outwardly notify you; look in your file directory and see if it did.
Second, instead of just calling main() at the end of your file, maybe try:
if __name__ == "__main__":
main()
and see if anything different happens. This bit of code is executed whenever the file is run as specifically as a script (as you're doing with the command % python Test.py <num> <fname>).
import time
def get_time():
global time_elapsed
time_elapsed = 0
time_elapsed = time.time() - last_time
return time_elapsed
def gear_encoder():
global gear_counter, run_counter,encoder_running, sensor_state,last_time
gear_counter = 0
run_counter = 0
encoder_running = False
sensor_state = False
encoder_running = True
while encoder_running:
last_time = time.time()
if run_counter >= 320:
print("END")
break
if sensor_state == True and gear_counter <= 5:
sensor_state = not sensor_state
gear_counter += 1
run_counter += 1
#print("State is " + str(sensor_state))
time.sleep((1.0/24) - get_time())
elif sensor_state == False and gear_counter <= 5:
sensor_state = not sensor_state
gear_counter += 1
run_counter += 1
#print("State is " + str(sensor_state))
time.sleep((1.0/24) - get_time())
elif sensor_state == True and gear_counter <= 8:
sensor_state = not sensor_state
gear_counter += 1
run_counter +=1
#print("State is " + str(sensor_state))
time.sleep((1.0/72) - get_time())
elif sensor_state == False and gear_counter <= 8:
sensor_state = not sensor_state
gear_counter += 1
run_counter +=1
#print("State is " + str(sensor_state))
time.sleep((1.0/72) - get_time())
else:
gear_counter = 0
start_time = time.time()
gear_encoder()
end_time = time.time() - start_time
print("The sensor changed states " + str(run_counter) + " times")
print(str(end_time))
So this is the code that I've got so far. If it runs this is it's output.
END
The sensor changed states 320 times
10.504526853561401
So what I attempted to set up was function called get_time() that will find the difference in time from when the variable last_time is called to when the actual function is called. The goal of this was to account for the time it takes to change the sensor_state and add to the counter variables in the sleep function.
You could do the math yourself but when this loops through 320 times all the time.sleeps that the program runs through add up to 10 seconds. I want the program to end in as close to 10 seconds as possible which is why I tried to account for the extra time with the get_time() function by subtracting the time it took to change sensor state and add to the counters.
My question to you all is, do you know of any methods that could help me lower this time drift? Was my technique to account for lost time even a valid approach? I would like to at some point add variables as arguments into time.sleep and probably lower the sleep to even smaller values than (1.0/72) seconds. Might multi threading be an option here? Any input would be appreciated.
The answer I'd give in code is pretty extensive. Let's see if I can get you on the path. You probably don't want to use sleep, because it blocks and everything becomes serial. That's important because you stated you want to exit in as close to 10 seconds as possible. That's fairly straightforward, if you don't have blocking calls going on. The other timers can be set up similarly. I show some examples below. Set up a timer to exit in 10 seconds. Note that the timer that is set for 11 seconds will not occur because the exit occurs before it triggers. This must be run as a script -- not interactively.
import sched, time, sys
def myExit():
# Do stuff here
sys.exit()
s = sched.scheduler(time.time, time.sleep)
# This causes the program to exit...
s.enter(10, 1, myExit, ())
s.enter(2.0/5, 2, print_answer, (1,))
s.enter(3.0/5, 2, print_answer, (2,))
s.enter(11, 2, print_answer, (3,))
s.run()
I have the following code which turns an outlet on/off every 3 seconds.
start_time = time.time()
counter = 0
agent = snmpy4.Agent("192.168.9.50")
while True:
if (counter % 2 == 0):
agent.set("1.3.6.1.4.1.13742.6.4.1.2.1.2.1.1",1)
else:
agent.set("1.3.6.1.4.1.13742.6.4.1.2.1.2.1.1", 0)
time.sleep(3- ((time.time()-start_time) % 3))
counter = counter + 1
Is there a way I can have the loop terminate at any given point if something is entered, (space) for example... while letting the code above run in the mean time
You can put the loop in a thread and use the main thread to wait on the keyboard. If its okay for "something to be entered" can be a line with line feed (e.g., type a command and enter), then this will do
import time
import threading
import sys
def agent_setter(event):
start_time = time.time()
counter = 0
#agent = snmpy4.Agent("192.168.9.50")
while True:
if (counter % 2 == 0):
print('agent.set("1.3.6.1.4.1.13742.6.4.1.2.1.2.1.1",1)')
else:
print('agent.set("1.3.6.1.4.1.13742.6.4.1.2.1.2.1.1", 0)')
if event.wait(3- ((time.time()-start_time) % 3)):
print('got keyboard')
event.clear()
counter = counter + 1
agent_event = threading.Event()
agent_thread = threading.Thread(target=agent_setter, args=(agent_event,))
agent_thread.start()
for line in sys.stdin:
agent_event.set()