Simple Python Pause/Resume - python

I was trying to pause/resume a task using hotkey, wrote the program bellow which is working fine while hitting Pause hotkey, but resume is not working. I guess I did some logical errors and need your expert advice to overcome that. Here is the script I wrote
import keyboard
class Test:
def __init__(self):
self.run = True
keyboard.add_hotkey("ctrl+alt+p", self.set_run)
keyboard.add_hotkey("ctrl+alt+r", self.set_run_r)
def set_run(self):
self.run = False
def set_run_r(self):
self.run = True
def start(self):
val = 1
while self.run:
val += 1
print("running ", val)
keyboard.wait("esc")
Test().start()

Try this
import keyboard
import sys
class Test:
def __init__(self):
self.val=1
self.run = True
keyboard.add_hotkey("ctrl+alt+p", self.set_run)
keyboard.add_hotkey("ctrl+alt+r", self.set_run_r)
def set_run(self):
self.run = False
def set_run_r(self):
self.run = True
def start(self):
self.val += 1
print(self.val)
return
test= Test()
try:
while True:
if test.run:
test.start()
else:
pass
except KeyboardInterrupt:
sys.exit()

Related

python calling different class methods by user input with terminating existing one

i'm trying to build personal assistant which is running by voice command.it is working perfectly with if: elif: blocks. but i want to create more elegance way and shorten code a bit.i try to create modular structure but i cant manage to terminate running function by calling middleman function with command=false parameter
i need some guiding please
main.py
from handler import middleman
while True:
message = input("> ")
if message != "":
middleman(message) #lets say command is "play1" than next command "play2"
Handler.py
from music import Class1
from music import Class2
def func1(context, command):
if command == True:
x = Class1(context)
x.run()
else:
x.stop() --> x not exist here gives error
def func2(context, command):
if command == True:
x = Class2(context)
x.run()
else:
x.stop()
def middleman(text):
command = True
if text == "stop":
command = False
function_name = globals()[text]
function_name(command)
music.py
import logging
import threading
import time
import pygame
class Player(threading.Thread):
def __init__(self, file_path, volume=1.0, start_time=0.0, master=None):
print("player started")
threading.Thread.__init__(self)
# 传入主窗口的指针,用于触发主窗口事件(若有)
self.master = master
self.file_path = file_path
# 音乐播放起点
self.start_time = start_time
# 用于控制音乐播放与停止
self.stop_state = False
# 用于控制音乐的暂停和恢复
self.pause_state = False
# 控制默认音量
self.volume = volume
# 初始化mixer
pygame.mixer.init() # 初始化音频
self.track = pygame.mixer.music
def set_volume(self, volume):
self.volume = volume
self.track.set_volume(self.volume)
def get_volume(self):
return self.volume
def run(self,stop):
print("stop word,stop")
try:
file = self.file_path
self.track.load(file) # 载入音乐文件
self.track.set_volume(self.volume) # 设置音量
self.track.play(start=self.start_time) # 开始播放
except Exception as e:
logging.warning(e)
if self.master:
self.master.event_generate("<<MusicError>>", when="tail")
while self.stop_state:
time.sleep(1)
# 若停止播放或播放结束,则结束这个线程
if self.stop_state:
self.track.stop() # 停止播放
return
elif not self.track.get_busy():
if self.master:
self.master.event_generate("<<CouldMusicStop>>", when="tail")
return
elif not self.stop_state and self.pause_state:
self.track.pause() # 暂停播放
elif not self.stop_state and not self.pause_state:
self.track.unpause() # 恢复播放
def stop(self):
self.stop_state=True
self.player = None

Background infinite loop in python

Using python 3.8.5, i've try to create a class object which have a inside infinite loop which will update a value that i can have to read later... Unfortunaly, my knowledge on this field are a little poor.
The aims of this trial is to dissociate an app which has a state from the gui i've made...
Here is one not working trial i've made.
import asyncio
from time import sleep
class value_holder:
def __init__(self):
self.value = 0
asyncio.create_task(self.infinite_loop())
async def infinite_loop(self):
while True:
self.value += 1
sleep(3)
v = value_holder()
while True:
print(v.value)
sleep(1)
I'm actually clueless so if someone have any clue or keyword for helping me in this search of solution, i will be very thankful
Best regards
You could work with a timer instead :
import threading
from time import sleep
class value_holder:
value = 0
timer = None
delay = 1
def __init__(self, delay):
self.value = 0
self.delay = delay
def run(self):
self.value += 1
print(self.value)
if self.timer != None: self.start()
def start(self):
self.timer = threading.Timer(self.delay, self.run)
self.timer.start()
def stop(self):
self.timer.cancel()
v = value_holder(3.0)
v.start()
sleep(9) #==> 1, 2, 3
v.stop()
print("Stopped")

Passing a variable through threaded classes

In the test code below,
class1.stop_callback() sets class1.stop = True
therefore class2.stop = True
therefore class3.stop should be True but it isn't.
class1.stop_callback() should stop the program but it doesn't do that.
What am I doing wrong?
You can test the code on repl.it https://repl.it/#bahtsiz_bedevi/classtest
import threading
import time
class Class1(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop = False
def stop_callback(self):
self.stop = True
def run(self):
class2 = Class2()
class2.stop = self.stop
class2.start()
while True:
time.sleep(1)
print("{} stop status: {}".format(self.__class__, "True" if self.stop else "False"))
if self.stop:
break
class Class2(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop = False
def run(self):
class3 = Class3()
class3.stop = self.stop
while True:
time.sleep(1)
print("{} stop status: {}".format(self.__class__, "True" if self.stop else "False"))
if self.stop:
break
class3.foo()
class Class3:
def __init__(self):
self.stop = False
def foo(self):
while True:
time.sleep(1)
print("{} stop status: {}".format(self.__class__, "True" if self.stop else "False"))
if self.stop:
break
class1 = Class1()
class1.start()
for i in range(10):
time.sleep(1)
class1.stop_callback()
In Python, variables are names for objects. By assigning False to class1.stop and class1.stop to class2.stop, you are assigning False to class2.stop, nothing more.
What you seem to want is a reference to class1.stop instead, however this is not how assignment works in Python. One way to get around this would be to use a list. If you keep the list the same and only change the value at the first index, you can achieve what you want:
stop1 = [False]
stop2 = stop1
assert stop2[0] == False
stop1[0] = True
assert stop2[0] == True
Since Class3 is not a Thread-like class (despite not running in the main thread) you cannot change the value of class3.stop until class3.foo() returns. Since class3.foo() doesn't return until the value of class3.stop changes, there is no way to stop the process and it runs forever.
I would suggest basing Class3 on Thread so that you can call methods on it while it is running. If this is too much overhead, or you will be running it more than once per instance of class2, you could always just define foo and then run it within the Class2.run method.
EDIT: I was going to mention Florian's point, but since - as in his proposed solution - mutable objects do carry across during assignments, I wasn't sure if you had already thought this part through.
Below is revised code; note
the use of threading.Lock to prevent those weird print statements that were happening on the same line
the use of while not self.stop rather than if statements with breaks
the use of threading in Class3
import threading
import time
printLock = threading.Lock()
p = print
def print(*a, **b):
with printLock:
p(*a, **b)
class Class1(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop = False
def stopMe(self):
self.stop = True
def run(self):
class2 = Class2()
class2.start()
while not self.stop:
time.sleep(1)
print("{} stop status:{:6}".format(self.__class__, str(self.stop)))
class2.stopMe()
class Class2(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop = False
def stopMe(self):
self.stop = True
def run(self):
class3 = Class3()
class3.start()
while not self.stop:
time.sleep(1)
print("{} stop status:{:6}".format(self.__class__, str(self.stop)))
class3.stopMe()
class Class3(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop = False
def stopMe(self):
self.stop = True
def run(self):
while not self.stop:
time.sleep(1)
print("{} stop status:{:6}".format(self.__class__, str(self.stop)))
class1 = Class1()
class1.start()
time.sleep(10)
class1.stopMe()

Why doesn't SIGVTALRM trigger inside time.sleep()?

I'm trying to use SIGVTALRM to snapshot profile my Python code, but it doesn't seem to be firing inside blocking operations like time.sleep() and socket operations.
Why is that? And is there any way to address that, so I can collect samples while I'm inside blocking operations?
I've also tried using ITIMER_PROF/SIGPROF and ITIMER_REAL/SIGALRM and both seem to produce similar results.
The code I'm testing with follows, and the output is something like:
$ python profiler-test.py
<module>(__main__:1);test_sampling_profiler(__main__:53): 1
<module>(__main__:1);test_sampling_profiler(__main__:53);busyloop(__main__:48): 1509
Note that the timesleep function isn't shown at all.
Test code:
import time
import signal
import collections
class SamplingProfiler(object):
def __init__(self, interval=0.001, logger=None):
self.interval = interval
self.running = False
self.counter = collections.Counter()
def _sample(self, signum, frame):
if not self.running:
return
stack = []
while frame is not None:
formatted_frame = "%s(%s:%s)" %(
frame.f_code.co_name,
frame.f_globals.get('__name__'),
frame.f_code.co_firstlineno,
)
stack.append(formatted_frame)
frame = frame.f_back
formatted_stack = ';'.join(reversed(stack))
self.counter[formatted_stack] += 1
signal.setitimer(signal.ITIMER_VIRTUAL, self.interval, 0)
def start(self):
if self.running:
return
signal.signal(signal.SIGVTALRM, self._sample)
signal.setitimer(signal.ITIMER_VIRTUAL, self.interval, 0)
self.running = True
def stop(self):
if not self.running:
return
self.running = False
signal.signal(signal.SIGVTALRM, signal.SIG_IGN)
def flush(self):
res = self.counter
self.counter = collections.Counter()
return res
def busyloop():
start = time.time()
while time.time() - start < 5:
pass
def timesleep():
time.sleep(5)
def test_sampling_profiler():
p = SamplingProfiler()
p.start()
busyloop()
timesleep()
p.stop()
print "\n".join("%s: %s" %x for x in sorted(p.flush().items()))
if __name__ == "__main__":
test_sampling_profiler()
Not sure about why time.sleep works that way (could it be using SIGALRM for itself to know when to resume?) but Popen.wait does not block signals so worst case you can call out to OS sleep.
Another approach is to use a separate thread to trigger the sampling:
import sys
import threading
import time
import collections
class SamplingProfiler(object):
def __init__(self, interval=0.001):
self.interval = interval
self.running = False
self.counter = collections.Counter()
self.thread = threading.Thread(target=self._sample)
def _sample(self):
while self.running:
next_wakeup_time = time.time() + self.interval
for thread_id, frame in sys._current_frames().items():
if thread_id == self.thread.ident:
continue
stack = []
while frame is not None:
formatted_frame = "%s(%s:%s)" % (
frame.f_code.co_name,
frame.f_globals.get('__name__'),
frame.f_code.co_firstlineno,
)
stack.append(formatted_frame)
frame = frame.f_back
formatted_stack = ';'.join(reversed(stack))
self.counter[formatted_stack] += 1
sleep_time = next_wakeup_time - time.time()
if sleep_time > 0:
time.sleep(sleep_time)
def start(self):
if self.running:
return
self.running = True
self.thread.start()
def stop(self):
if not self.running:
return
self.running = False
def flush(self):
res = self.counter
self.counter = collections.Counter()
return res
def busyloop():
start = time.time()
while time.time() - start < 5:
pass
def timesleep():
time.sleep(5)
def test_sampling_profiler():
p = SamplingProfiler()
p.start()
busyloop()
timesleep()
p.stop()
print "\n".join("%s: %s" %x for x in sorted(p.flush().items()))
if __name__ == "__main__":
test_sampling_profiler()
When doing it this way the result is:
$ python profiler-test.py
<module>(__main__:1);test_sampling_profiler(__main__:62);busyloop(__main__:54): 2875
<module>(__main__:1);test_sampling_profiler(__main__:62);start(__main__:37);start(threading:717);wait(threading:597);wait(threading:309): 1
<module>(__main__:1);test_sampling_profiler(__main__:62);timesleep(__main__:59): 4280
Still not totally fair, but better than no samples at all during sleep.
The absence of SIGVTALRM during a sleep() doesn't surprise me, since ITIMER_VIRTUAL "runs only when the process is executing."
(As an aside, CPython on non-Windows platforms implements time.sleep() in terms of select().)
With a plain SIGALRM, however, I expect a signal interruption and indeed I observe one:
<module>(__main__:1);test_sampling_profiler(__main__:62);busyloop(__main__:54): 4914
<module>(__main__:1);test_sampling_profiler(__main__:62);timesleep(__main__:59): 1
I changed the code somewhat, but you get the idea:
class SamplingProfiler(object):
TimerSigs = {
signal.ITIMER_PROF : signal.SIGPROF,
signal.ITIMER_REAL : signal.SIGALRM,
signal.ITIMER_VIRTUAL : signal.SIGVTALRM,
}
def __init__(self, interval=0.001, timer = signal.ITIMER_REAL): # CHANGE
self.interval = interval
self.running = False
self.counter = collections.Counter()
self.timer = timer # CHANGE
self.signal = self.TimerSigs[timer] # CHANGE
....

Externally stop a running while loop

I have a cmd.Cmd class command line interpreter that, for example, initializes a self.counter = Counter().
After calling 'start', do_start() will call self.counter.start() and self.counter starts a while loop that counts from 0 to infinity.
Pseudocode example of Counter:
class Counter(object):
def __init__(self):
self.number = 0
self.running = False
def start():
self.running = True
while self.running:
self.number += 1
def status():
return self.number
def stop():
self.running = False
How can I call 'status' in my cmd.Cmd class (which calls do_status()) to get self.counter.status() which will give the current number that has been incremented?
And how can I call 'stop' in my cmd.Cmd class to get self.counter.stop() to stop the while loop.
If you want to do something in parallel you must use threads or multiple processes like this:
import threading
from time import sleep
class Counter(object):
def __init__(self):
self.number = 0
self.running = False
def start(self):
self.running = True
while self.running:
self.number += 1
# add sleep to prevent blocking main thread by this loop
sleep(0.1)
def status(self):
return self.number
def stop(self):
self.running = False
class Cmd(object):
t = None
counter = None
def start(self):
self.counter = Counter()
self.t = threading.Thread(target=self.counter.start)
self.t.start()
def do_status(self):
return self.counter.status()
def stop(self):
self.counter.stop()
# waiting while thread with Counter will finish
self.t.join()
if __name__ == "__main__":
cmd = Cmd()
print "Starting counter"
cmd.start()
sleep(5)
print cmd.do_status()
sleep(2)
print cmd.do_status()
cmd.stop()
print "Counter was stopped"
Output will be:
Starting counter
50
70
Counter was stopped
But if you want to be able communicate with Counter from different application then you must learn about sockets .
if cmd is an instance of Cmd and your using an instance method:
Send the instance to Counter:
def __init__(self, cmd):
self.number = 0
# self.running = False # removed - use self.cmd.status() for control
self.cmd = cmd
Control while using self.cmd:
def start():
while self.cmd.status():
self.number += 1
I expect self.cmd.status() to be blocking (expecting user input, or something like that).

Categories

Resources