Called function play out ahead of my main code - python

I have two files. One with a game with an evil gambler and the other with a loading function to play out between the lines of text. My goal is to replace the time.sleep() functions with my loading function. The first file looks like this:
import random
import time
import test
def game():
string_o = "Opponent "
string_u = "User "
input_n = ""
input_n = input('Care to try your luck?\n')
while input_n == 'yes' or input_n == 'y':
cpu = random.randint(1,6)
user = random.randint(1,6)
time.sleep(0.5)
print('\nGreat!')
time.sleep(0.2)
input_n=input("\nAre you ready?\n")
time.sleep(0.4)
print(string_o , cpu)
#If the gambler's die roll is above three he gets very happy
if cpu > 3:
print('Heh, this looks good')
time.sleep(0.2)
#...but if it's lower he gets very anxious
else:
('Oh, no!')
test.animate()
print(string_u , user)
if cpu < user:
print('Teach me, master')
else:
print('Heh, better luck next time, kid')
time.sleep()
input_n = input('\nDo you want to try again?\n')
print("Heh, didn't think so.\nPlease leave some room for thr big boys")
game()
The other file looks like this:
import itertools
import threading
import time
import sys
done = False
#here is the animation
def animate():
for c in itertools.cycle(['|', '/', '-', '\\']):
if done:
break
sys.stdout.write('\rloading ' + c)
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\rDone! ')
t = threading.Thread(target=animate)
t.start()
#would like an x here instead that is defined in the other file
time.sleep(1)
done = True
The problem is that the function animate() plays out before the game has even started.
I would also like to set the time for the loading function in my main game file. Is that possible?

By putting t.start() outside of any function in your test.py, you are running animate as soon as you import test.py. You should put t.start() inside a function instead. Also, your done flag is also set to True when test.py is imported and will always immediately break your for loop inside animate. I don't think you really need this flag at all. Change your test.py to:
import itertools
import threading
import time
import sys
#here is the animation
def animate():
for c in itertools.cycle(['|', '/', '-', '\\']):
sys.stdout.write('\rloading ' + c)
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\rDone! ')
def start():
t = threading.Thread(target=animate)
t.start()
And then in your first file, instead of calling test.animate() directly, call test.start() instead.

Related

Python - creating a simple killswitch for a function that runs with multiprocessing

So, i would claim that i understand how Asyncio, Multiprocessing, Threading etc. works, basically. I know how to listen for keystrokes too - there are many good examples on this site.
However i was unable to combine both into one. I have a programm that runs continously in a loop, until it runs into certain cases where it stops. In these cases, it uses a Multiprocessing.Queue() to prompt for user input on wether it should continue or not.
All of this works, so far so good. Now i want to add a second catch case here: The programm should, once it starts running, immediatly cease working as soon as i press a certain button (lets say Escape).
This is the very dumbed down version of my programm:
test.py:
from test3 import Counter
from multiprocessing import Process, Queue
import sys
def main(q, passed_variable):
foo = Counter()
p1 = Process(target=foo.counting, args=(q,passed_variable))
p1.start()
p1.join()
if q.get() == False:
x = input("Keep going?")
print(x)
if x == "y":
main(q, user_Input)
else:
sys.exit()
if __name__ == "__main__":
q = Queue()
user_Input = ("What you want from me, man?")
print("Starting")
main(q, passed_variable=user_Input)
test3.py:
import time
class Counter:
def counting(self, q, user_input):
x = 0
while True:
print(str(x) + " " + user_input)
if x == 4:
q.put(False)
break
time.sleep(1)
x += 1
I tried everything i could think of, in no case did i get the desired result, and no question i found here was able to help me in this specific case.
You can solve this using keyboard and then creating a second Queue():
from test3 import Counter
from multiprocessing import Process, Queue
import sys
import keyboard
def main(q, queue2, passed_variable):
foo = Counter()
p1 = Process(target=foo.counting, args=(q,passed_variable))
p1.start()
p2 = Process(target=keyCatcher, args=(queue2,))
p2.start()
if queue2.get() == False:
p1.terminate()
print("Terminating Programm")
sys.exit()
if q.get() == False:
x = input("Keep going?")
print(x)
if x == "y":
main(q, queue2, user_Input)
else:
sys.exit()
def keyCatcher(queue2):
while True:
if keyboard.is_pressed('q'): # if key 'q' is pressed
queue2.put(False)
if __name__ == "__main__":
q = Queue()
queue2 = Queue()
user_Input = ("What you want from me, man?")
print("Starting")
main(q, queue2, passed_variable=user_Input)
The crux is:
p1.start()
p1.join()
Which means after main() starts p1, it waits for it to finish. So there's no chance to interrupt it while processing.
You need to:
wait for p1 to finish
while waiting, see if the main process gets a 'q'
if the main process gets a 'q', stop it.
Something like:
p1.start()
while p1.is_alive():
k = keyboard.read_key()
if k == 'q':
p1.terminate()

Is there anyway to wait for a couple of minutes and if no input is provided then take the value "n" as input for first variable?

while 1:
wat=water()
if wat==10:
print("water condition")
mixer.music.load("water.mp3")
mixer.music.play()
first=input("Drank?Y/N")
if first.lower()=="y":
with open("HealthLog.txt","a") as water1:
Content=f"Drank water at [{getdate()}] \n"
water1.write(Content)
else:
pass
Is there any way to wait for a couple of minutes and if no input is provided, then take the value "n" as input for the first variable?
Guess by default it will wait indefinitely. I tried using a timer function, but it cannot record any input.
What I am trying to do is to track my activities, so if I drink water I say y--> this records my activity and writes it to a file.
All help will be greatly appreciated
Here is how you can use a combination of pyautogui.typewrite, threading.Thread and time.sleep:
from pyautogui import typewrite
from threading import Thread
from time import sleep
a = ''
def t():
sleep(5)
if not a: # If by 5 seconds a still equals to '', as in, the user haven't overwritten the original yet
typewrite('n')
typewrite(['enter'])
T = Thread(target=t)
T.start()
a = input()
b = input() # Test it on b, nothing will happen
Here is the code implemented into your code:
from pyautogui import typewrite
from threading import Thread
from time import sleep
while 1:
wat = water()
if wat == 10:
print("water condition")
mixer.music.load("water.mp3")
mixer.music.play()
first = 'waiting...'
def t():
sleep(5)
if first == 'waiting...':
typewrite('n')
typewrite(['enter'])
T = Thread(target=t)
T.start()
first = input("Drank?Y/N")
if first.lower() == "y":
with open("HealthLog.txt","a") as water1:
Content=f"Drank water at [{getdate()}] \n"
water1.write(Content)
else:
pass

I am testing out threading.Time in this little game I made, but it keeps overlapping

import time
from threading import Timer
from random import randint
print("Every wrong answer is a 3s delay; you have 30s")
end = False
def lose():
print(end)
print("Time up!")
time.sleep(1)
print("Score is",pts,", with",wrong,"wrong answers.")
time.sleep(1)
input("enter to quit")
quit()
timer = Timer(10,lose)
timer.start()
pts = 0
wrong = 0
while end == False:
a = randint(5,50)
b = randint(5,50)
print(a,"+",b)
ans = input()
if ans.isnumeric():
ans = int(ans)
if ans == a+b:
print("correct")
pts = pts+1
else:
print("wrong,",a+b)
wrong = wrong+1
print("delay")
time.sleep(3)
print("delay end")
print("")
When the timer finishes, the loop overlaps the 'lose' function, and it messes up on the line like this:
Time up!
45 + 10
55
Score iscorrect
3
, with29 0+ wrong answers.37
enter to quitwrong,p
66
delay
How do I fix this issue?
Sorry if this question has already been answered, but I want to know.
Ideally, you should probably avoid using threads altogether, as mentioned in the comments.
However, if you are going to use threads, consider using a mutex to ensure that multiple threads are not trying to write to stdout at the same time.
For example:
# setup at the beginning:
from threading import Thread, Lock
mutex = Lock()
# surrounding each print statement:
mutex.acquire()
try:
print('text')
finally:
mutex.release()

RuntimeError: threads can only be started once for a beginner

This is for my first program. I am trying to put this loading animation in a while loop, but it gives this error after the second "f.start()". As I don't understand much about threads, the "help" I could find on Google was not helpful at all, which involved long codes with class creation and everything. Can somebody help me understand what I could do here?
I copied the animation code from here: Python how to make simple animated loading while process is running
import itertools
import threading
import time
import sys
#here is the animation
def animate():
for c in itertools.cycle(['|', '/', '-', '\\']):
if done:
break
sys.stdout.write('\rloading ' + c)
sys.stdout.flush()
time.sleep(0.25)
sys.stdout.write('\rDone! ')
t = threading.Thread(target=animate)
while True:
done = False
user_input = input('Press "E" to exit.\n Press"S" to stay.')
if user_input is "E":
break
elif user_input is "S":
# Long process here
t.start()
time.sleep(5)
done = True
time.sleep(1)
print("\nThis will crash in 3 seconds!")
time.sleep(3)
break
# Another long process here
t.start()
time.sleep(5)
done = True
As the error says, a thread can only be started once. So, create a new thread instead. Notice also that I use join to wait for the old thread to stop.
import itertools
import threading
import time
import sys
#here is the animation
def animate():
for c in itertools.cycle(['|', '/', '-', '\\']):
if done:
break
sys.stdout.write('\rloading ' + c)
sys.stdout.flush()
time.sleep(0.25)
sys.stdout.write('\rDone! ')
t = threading.Thread(target=animate)
while True:
done = False
user_input = input('Press "E" to exit.\n Press"S" to stay.')
if user_input is "E":
break
elif user_input is "S":
# Long process here
t.start()
time.sleep(5)
done = True
t.join()
print("\nThis will crash in 3 seconds!")
time.sleep(3)
break
# Another long process here
done = False
t = threading.Thread(target=animate)
t.start()
time.sleep(5)
done = True

Pause and resume a running script in Python 3.42 in Windows

I'm new to Python and have been googling for a couple of days and read all I can find on this forum. Might be that I don't understand it all but I haven't found a solution to my problem yet. Ask for forgiveness already if there's an answer already to my problem, then I haven't understood it.
I want to make a Pause function for my program Tennismatch. The program will when it's being run print the score of a tennis match like this: "15-0, 15-15 etc ongoing till the match ends. It will print the score line by line.
I want the user to be able to pause after x number of balls, games, etc. So I don't know when the user wants to pause and after the user has paused I want the user to be able to resume the tennismatch where it was.
Have seen the time.sleep() but as I have understood it you must know when you want to pause to use this and it also ain't an indefinetie pause like I want. With input() it's the same.
Am going to make a GUI later on when the code is finished. Happy for anything that leads me to solving my problem.
I use Windows and Python 3.42 and run the program in Shell.
A piece of the code (haven't written it all yet, it's more of a general situation when something is being printed line after line for some time and want to be able do pause in the CIL:
#self.__points = [0,0]
def playGame(self):
if self.server == True: #self.server is either True or False when someone calls playGame()
server = self.player_1.get_win_serve() #self.player_1 = an object of a class Player():
else:
server = self.player_2.get_win_serve() #get_win_serve() method returns the probability to win his serv (1-0)
while (0 < self.__points[0] - self.__points[1] >= 2 or 0 < self.__points[1] - self.__points[0] >= 2) and (self.__points[1] >= 4 or self.__points[0] >= 4):
x = random.uniform(0,1)
if x > 0 and x < server:
self.__points[0] += 1
else:
self.__points[1] += 1
# print('The score, by calling a score() function that I haven't written yet')
For dealing with events in main loop you need to make a separated thread which capture input or any other event.
import sys
from sys import stdin
from time import sleep
from threading import Thread
from Queue import Queue, Empty
def do_something():
sleep(1)
print 42
def enqueue_output(queue):
while True:
# reading line from stdin and pushing to shared queue
input = stdin.readline()
print "got input ", input
queue.put(input)
queue = Queue()
t = Thread(target=enqueue_output, args=(queue,))
t.daemon = True
t.start()
pause = False
try:
while True:
try:
command = queue.get_nowait().strip()
print 'got from queue ', command
except Empty:
print "queue is empty"
command = None
if command:
if command == 'p':
pause = True
if command == 'u':
pause = False
if not pause:
print pause
do_something()
except KeyboardInterrupt:
sys.exit(0)
I came up with the following.
while True:
try:
## Keep doing something here
## your regular code
print '.',
except KeyboardInterrupt:
## write or call pause function which could be time.sleep()
print '\nPausing... (Hit ENTER to continue, type quit to exit.)'
try:
response = raw_input()
if response.lower() == 'quit':
break
print 'Quitting...'
except KeyboardInterrupt:
print 'Resuming...'
continue
The Event loop might as well be the code I wrote with.
I don't see any user input so I assume that x emulates it. To pause the game if x < 0.1 and to unpause(/resume) it if x > 0.9, you could:
while your_condition(self.__points):
x = random.random()
if x < 0.1: # pause
self.pause()
elif x > 0.9: # resume
self.resume()
if self.is_paused:
continue # do nothing else only wait for input (`x`)
# assume your_condition() has no side-effects
# here's what the resumed version does:
print("...")
# change self.__points, etc
where pause(), resume(), is_paused() methods could be implemented as:
def __init__(self):
self.is_paused = False
def pause(self):
self.is_paused = True
def resume(self):
self.is_paused = False
as you can see the implementation is very simple.

Categories