I don't know much of Python yet, but I'm trying to create an app that controls multiple streams of sound simultaneously (It has to do with binaural beats, noise and the brain). Given that I would like to control the volume and state of each of the tracks separately, I think that I need to use multiprocessing module. The first of those streams is a simple background music. I would like the user to pause it whenever he wants and I'm using pygame module.
import pygame
import time
import os
import multiprocessing
class play_music:
def __init__(self, path="", name=""):
self.state="" #pause, play, stop
self.name=name #name of the song
self.path=path #path of the song
def play_music(self):
path=self.path
pygame.init()
pygame.mixer.music.load(path)
pygame.mixer.music.play()
print (f"Playing {path}...")
while True:
if self.state=="pause":
pygame.mixer.music.pause()
self.state=""
while self.state=="":
if self.state == "continue":
pygame.mixer.music.unpause()
elif self.state=="stop"():
pygame.mixer.music.stop()
break
elif self.state=="stop":
pygame.mixer.music.stop()
break
def main():
task = ""
while not ( task == "meditacion" or task == "estudio"):
task = input ("Introduce que vas a hacer (meditacion, estudio): ").rstrip()
name ="non-existing track"
while not (os.path.exists(f"musica/{task}/{name}")):
name = input ("Introduce pista musical: ").rstrip()
path = f"musica/{task}/{name}"
print (f"Correct track. Path: {path}")
music = play_music()
music.path=path
music.name=name
p1 = multiprocessing.Process(target=music.play_music)
p1.start()
time.sleep(3) #for letting the process start correctly
while True:
music.state=input("pause, stop?: ")
if __name__=="__main__":
main()
This doesn't work. The state doesn't get modified whenever I input pause or stop. Any help is welcomed. Thank you so much in advance.
Since multiprocessing module creates separate Python processes, it is not possible to share variables in an easy way.
In case you would like to share variables yet still parallelize some of your computing, you should look into the built-in threading module.
Related
I'm trying to share the class instance/object variables with other processes that I start within it, since I need to run multiple function at the same time, to record macros from the keyboard & mouse and re-play them latter at the same timing.
I see that it's possible to use multiprocessing.Manager, but i'm using concurrent.futures.ThreadPoolExecutor. is there a similar function there?
I wrote the code below now to clarify. The actual code has a setState function for settings the recording state and such, and the key that's pressed doesn't get passed. Also, the actual code obviously has a listener for the key presses and mouse moves, the getKey and getMove functions should be the ones appending to the list. The problem in this case is that the recording variable can't be accessed from the second process that should start recording moves once the "Insert" key is pressed. A function in concurrent that's similar to Manager in multiprocessing would solve it, but i'm not sure what it's called or to use it.
from concurrent.futures import ThreadPoolExecutor as Executor
import time
class recMacros(object):
def __init__(self):
self.recording = False
self.lastKey = None
self.lastMove = None
self.mouseMoves = []
self.keyPresses = []
self.runMacros()
def getTime(self):
return time.time()
def getKey(self):
#return keyboard listener last key pressed
return "W"
def getMove(self):
#return keyboard listener last key pressed
return "W"
def recMoves(self):
while True:
while self.recording:
mouseMove = self.getMove()
if mouseMove != self.lastMove:
self.mouseMoves.append((mouseMove, self.getTime()))
self.lastMove = mouseMove
def recPresses(self):
while True:
keyPress = self.getKey()
if keyPress == "Insert":
self.recording = True
elif keyPress == "End":
self.recording = False
elif self.recording and keyPress != self.lastKey:
self.keyPresses.append((keyPress, self.getTime()))
self.lastKey = keyPress
else:
print("Error")
def recMacros(self):
with Executor(max_workers=2) as e:
e.submit(recPresses)
e.submit(recMoves)
if __name__ == "__main__":
recMacros()
I'd appreciate some quick direction since i'm in a rush. Thanks in advance
#user2357112 supports Monica
Here's the code I used to test the timing, to verify that ThreadPoolExecutor is like a process to comes to running the functions in parallel:
from concurrent.futures import ThreadPoolExecutor
import time
def printTime():
print(f"Time: {time.time()}\n")
def runPro():
with ThreadPoolExecutor(max_workers=3) as e:
for i in range(3):
e.submit(printTime)
runPro()
If you want to save something in a variable that every process can use, you can use queue.
Import queue
import queue
Create a shared variable
shared_var = queue.Queue(maxsize=0)
where maxsize is the maximum that can be saved in this queue
Edit shared variable in any process
shared_var.put(item)
Get the things in the variable
variable = shared_var.get()
There is a lot more you can do with queue, see the documentation.
I'm developping a game using pygame and I want to create a loading screen while the assets are loaded. The loading screen have animations, so loading screen and assets loading should be occurring at the same time.
Consider the code below:
class Game:
def __init__(self):
self.loading_screen()
self.load_assets()
def loading_screen(self):
# do something while load_assets() is running
def load_assets(self):
# load all assets needed
I've tried Process from multiprocessing, but I dont know how to keep loading_screen() running without freezes while load_assets() are running.
Also, I've tried threads, but python doesn't run threads simultaneously, so, in some moment, the loading_screen() will freeze. (This could be wrong, but this was observed in the game)
Some help about this?
Thanks for all
Threads are suitable for this. Nothing runs exactly simultaneously on a multi user system anyway.
import threading
from time import sleep
class Game:
def __init__(self):
self.loading_screen()
def loading_screen(self):
t = threading.Thread(target=self.load_assets)
t.daemon = True
t.start()
while t.isAlive():
sleep(0.1)
print("loading screen")
print ("assets done, ready to proceed now")
def load_assets(self):
i = 0
while i < 10:
print("loading assets")
i += 1
sleep(1)
g = Game()
print("done")
This puts your asset loading to a thread and then enters loading_screen() in the main thread. This just prints messages to demonstrate they both run in parallel.
I can understand what problems the OP is having, a preferred solution is to use multiprocessing rather than multithreading. The given demo from the first answer uses time.sleep for simulating 'heavy loading' in the loading function, but when loading is actually going to take place, it will take a major amount of performance out of the CPU.
So it might happen that your loading animation is playing at 5-10 FPS when you expected 100+ FPS from it.
In this case, it is best to use multiprocessing to achieve the same. From what I have heard, it is a bit complicated to implement it, but it is quite possible. The loading function will then run as a separate process, not a separate thread from the same process.
Here is a small demo program to illustrate a loading screen using multiprocessing:
import multiprocessing
from multiprocessing.connection import Connection
def loading_screen(conn: Connection, stage):
clock = pygame.time.Clock()
fps = 60
c = 0
font = pygame.font.SysFont('consolas', 25)
while True:
screen.fill((0, 0, 50))
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_ESCAPE:
return
if conn.poll(): # check if any object is available to be received or not
# this line is used to prevent waiting until the next object is received
c = conn.recv() # if available, receive the object
if c == 'end':
# process end condition checking
return
pygame.draw.rect(screen, 'white', (50, 50, c, 50))
screen.blit(font.render(f'{c} / {stage * 100} % [stage {stage}]', True, 'white'), (50, 150))
screen.blit(font.render('Check console for status', True, 'white'), (50, 200))
pygame.display.update()
clock.tick(fps)
pygame.display.set_caption(f'Multiprocessing Loading Screen [FPS = {int(clock.get_fps())}]')
def resource_load(conn: Connection, items):
# load resources
# for now it increments a counter and sends it via the connection object
# you can replace it for CPU intensive processes
# and pass the loaded objects to the connection object
c = 0
while True:
c += 1
if c > items:
conn.send('end') # signal end of loading current stage
return # returning will end the process automatically
conn.send(c) # send the counter value via the connection object
if __name__ == '__main__':
import pygame
pygame.init()
screen = pygame.display.set_mode((500, 300)) # initialize display
receiver, sender = multiprocessing.Pipe(duplex=False) # initialize sender and receiver connection objects [unidirectional]
print('Program to demonstrate a simple loading screen using multiprocessing')
print()
print('starting stages...')
multiprocessing.Process(target=resource_load, args=(sender, 100)).start()
print('loading stage 1')
loading_screen(receiver, 1)
multiprocessing.Process(target=resource_load, args=(sender, 200)).start()
print('loading stage 2')
loading_screen(receiver, 2)
multiprocessing.Process(target=resource_load, args=(sender, 300)).start()
print('loading stage 3')
loading_screen(receiver, 3)
input('finished loading all stages... press Enter to exit console') # press Enter inside the console to exit program
EDIT:
I have made a video on this if interested: https://www.youtube.com/watch?v=KWGDgPldPVo
I started learning python recently, and I am facing a situation that I do not even know if it is expected, or if something is wrong.
I am learning parallel threading to have two independent processes on the same program (UI control on one thread, image processing on another)
So, to test this I created this simple code:
(Camera is a custom class that connects to a usb webcam)
import thread
from vii.camera import Camera
class Process(object):
def __init__(self, width=800, height=600):
self._cam = Camera(width, height)
self._is_running = False
self._current_image = None
def start(self):
thread.start_new(self._run(), (self))
def _run(self):
self._cam.start()
self._is_running = True
while self._is_running:
self._current_image = self._cam.update()
self._current_image.show()
def get_image(self):
return self._current_image
def stop(self):
self._is_running = False
self._cam.close()
thread.exit()
process = Process()
process.start()
print("You will never see this output")
while (True):
key = raw_input()
if key == 'q':
process.stop()
break
The thread is created with success, and I am able to see the image. Now, I need to be able to affect it (stop it, get data from it) from the main thread. But the problem is that the code never enters in the while loop.
Is this behaviour expected? If it is, is there a way for me to achieve the functionality I need?
I have yet another question about Python multiprocessing.
I have a module that creates a Process and just runs in a while True loop.
This module is meant to be enabled/disabled from another Python module.
That other module will import the first one once and is also run as a process.
How would I better implement this?
so for a reference:
#foo.py
def foo():
while True:
if enabled:
#do something
p = Process(target=foo)
p.start()
and imagine second module to be something like that:
#bar.py
import foo, time
def bar():
while True:
foo.enable()
time.sleep(10)
foo.disable()
Process(target=bar).start()
Constantly running a process checking for condition inside a loop seems like a waste, but I would gladly accept the solution that just lets me set the enabled value from outside.
Ideally I would prefer to be able to terminate and restart the process, again from outside of this module.
From my understanding, I would use a Queue to pass commands to the Process. If it is indeed just that, can someone show me how to set it up in a way that I can add something to the queue from a different module.
Can this even be easily done with Python or is it time to abandon hope and switch to something like C or Java
I purposed in comment two different approches :
using a shared variable from multiprocessing.Value
pause / resume the process with signals
Control by sharing a variable
def target_process_1(run_statement):
while True:
if run_statement.value:
print "I'm running !"
time.sleep(1)
def target_process_2(run_statement):
time.sleep(3)
print "Stoping"
run_statement.value = False
time.sleep(3)
print "Resuming"
run_statement.value = True
if __name__ == "__main__":
run_statement = Value("i", 1)
process_1 = Process(target=target_process_1, args=(run_statement,))
process_2 = Process(target=target_process_2, args=(run_statement,))
process_1.start()
process_2.start()
time.sleep(8)
process_1.terminate()
process_2.terminate()
Control by sending a signal
from multiprocessing import Process
import time
import os, signal
def target_process_1():
while True:
print "Running !"
time.sleep(1)
def target_process_2(target_pid):
time.sleep(3)
os.kill(target_pid, signal.SIGSTOP)
time.sleep(3)
os.kill(target_pid, signal.SIGCONT)
if __name__ == "__main__":
process_1 = Process(target=target_process_1)
process_1.start()
process_2 = Process(target=target_process_2, args=(process_1.pid,))
process_2.start()
time.sleep(8)
process_1.terminate()
process_2.terminate()
Side note: if possible do not run a while True.
EDIT: if you want to manage your process in two different files, supposing you want to use a control by sharing a variable, this is a way to do.
# file foo.py
from multiprocessing import Value, Process
import time
__all__ = ['start', 'stop', 'pause', 'resume']
_statement = None
_process = None
def _target(run_statement):
""" Target of the foo's process """
while True:
if run_statement.value:
print "I'm running !"
time.sleep(1)
def start():
global _process, _statement
_statement = Value("i", 1)
_process = Process(target=_target, args=(_statement,))
_process.start()
def stop():
global _process, _statement
_process.terminate()
_statement, _process = None, _process
def enable():
_statement.value = True
def disable():
_statement.value = False
I'm using python-zookeeper for locking, and I'm trying to figure out a way of getting the execution to wait for notification when it's watching a file, because zookeeper.exists() returns immediately, rather than blocking.
Basically, I have the code listed below, but I'm unsure of the best way to implement the notify() and wait_for_notification() functions. It could be done with os.kill() and signal.pause(), but I'm sure that's likely to cause problems if I later have multiple locks in one program - is there a specific Python library that is good for this sort of thing?
def get_lock(zh):
lockfile = zookeeper.create(zh,lockdir + '/guid-lock-','lock', [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL | zookeeper.SEQUENCE)
while(True):
# this won't work for more than one waiting process, fix later
children = zookeeper.get_children(zh, lockdir)
if len(children) == 1 and children[0] == basename(lockfile):
return lockfile
# yeah, there's a problem here, I'll fix it later
for child in children:
if child < basename(lockfile):
break
# exists will call notify when the watched file changes
if zookeeper.exists(zh, lockdir + '/' + child, notify):
# Process should wait here until notify() wakes it
wait_for_notification()
def drop_lock(zh,lockfile):
zookeeper.delete(zh,lockfile)
def notify(zh, unknown1, unknown2, lockfile):
pass
def wait_for_notification():
pass
The Condition variables from Python's threading module are probably a very good fit for what you're trying to do:
http://docs.python.org/library/threading.html#condition-objects
I've extended to the example to make it a little more obvious how you would adapt it for your purposes:
#!/usr/bin/env python
from collections import deque
from threading import Thread,Condition
QUEUE = deque()
def an_item_is_available():
return bool(QUEUE)
def get_an_available_item():
return QUEUE.popleft()
def make_an_item_available(item):
QUEUE.append(item)
def consume(cv):
cv.acquire()
while not an_item_is_available():
cv.wait()
print 'We got an available item', get_an_available_item()
cv.release()
def produce(cv):
cv.acquire()
make_an_item_available('an item to be processed')
cv.notify()
cv.release()
def main():
cv = Condition()
Thread(target=consume, args=(cv,)).start()
Thread(target=produce, args=(cv,)).start()
if __name__ == '__main__':
main()
My answer may not be relevant to your question, but it is relevant to the question title.
from threading import Thread,Event
locker = Event()
def MyJob(locker):
while True:
#
# do some logic here
#
locker.clear() # Set event state to 'False'
locker.wait() # suspend the thread until event state is 'True'
worker_thread = Thread(target=MyJob, args=(locker,))
worker_thread.start()
#
# some main thread logic here
#
locker.set() # This sets the event state to 'True' and thus it resumes the worker_thread
More information here: https://docs.python.org/3/library/threading.html#event-objects